From 58a9f7b25a71b6538e2101503f50c643e87c80ec Mon Sep 17 00:00:00 2001 From: Koitharu Date: Thu, 2 Jan 2025 15:45:17 +0200 Subject: [PATCH 01/12] Fix settings menu (cherry picked from commit c51218240eccb8faca629021eed345d6e8aad9f0) --- .../org/koitharu/kotatsu/settings/RootSettingsFragment.kt | 7 +++++++ .../org/koitharu/kotatsu/settings/SettingsActivity.kt | 3 --- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/RootSettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/RootSettingsFragment.kt index 220c65bda..eef3efd0c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/RootSettingsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/RootSettingsFragment.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.settings import android.os.Bundle import android.view.View import androidx.annotation.StringRes +import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.preference.Preference import dagger.hilt.android.AndroidEntryPoint @@ -10,12 +11,16 @@ import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BasePreferenceFragment +import org.koitharu.kotatsu.core.util.ext.addMenuProvider import org.koitharu.kotatsu.core.util.ext.observe +import org.koitharu.kotatsu.settings.search.SettingsSearchMenuProvider +import org.koitharu.kotatsu.settings.search.SettingsSearchViewModel @AndroidEntryPoint class RootSettingsFragment : BasePreferenceFragment(0) { private val viewModel: RootSettingsViewModel by viewModels() + private val activityViewModel: SettingsSearchViewModel by activityViewModels() override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.pref_root) @@ -41,6 +46,8 @@ class RootSettingsFragment : BasePreferenceFragment(0) { } } } + addMenuProvider(SettingsSearchMenuProvider(activityViewModel)) + addMenuProvider(SettingsMenuProvider(view.context)) } override fun setTitle(title: CharSequence?) { 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 3d687637e..18c86b69e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt @@ -34,7 +34,6 @@ import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.settings.about.AboutSettingsFragment import org.koitharu.kotatsu.settings.search.SettingsItem import org.koitharu.kotatsu.settings.search.SettingsSearchFragment -import org.koitharu.kotatsu.settings.search.SettingsSearchMenuProvider import org.koitharu.kotatsu.settings.search.SettingsSearchViewModel import org.koitharu.kotatsu.settings.sources.SourceSettingsFragment import org.koitharu.kotatsu.settings.sources.SourcesSettingsFragment @@ -76,8 +75,6 @@ class SettingsActivity : } viewModel.isSearchActive.observe(this, ::toggleSearchMode) viewModel.onNavigateToPreference.observeEvent(this, ::navigateToPreference) - addMenuProvider(SettingsSearchMenuProvider(viewModel)) - addMenuProvider(SettingsMenuProvider(this)) } override fun onPreferenceStartFragment( From b3f748c0000ebdebdcb76ef55eb71f9f2f64d184 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Thu, 9 Jan 2025 08:19:43 +0200 Subject: [PATCH 02/12] Fix crashes (cherry picked from commit 4dba90361c4c1552b5a0d5de5e25e56444f24325) --- app/src/main/AndroidManifest.xml | 2 +- .../core/exceptions/CaughtException.kt | 4 +++- .../core/exceptions/WrapperIOException.kt | 5 +++++ .../kotatsu/core/network/AppProxySelector.kt | 2 +- .../kotatsu/core/network/GZipInterceptor.kt | 19 +++++++++++++------ .../kotatsu/core/util/ext/Throwable.kt | 11 +++++++++-- .../koitharu/kotatsu/local/data/PagesCache.kt | 1 + .../kotatsu/reader/domain/PageLoader.kt | 3 ++- .../kotatsu/reader/ui/ReaderInfoBarView.kt | 3 +++ .../koitharu/kotatsu/widget/WidgetUpdater.kt | 2 +- app/src/main/res/values/bools.xml | 1 + app/src/release/res/values/bools.xml | 5 +++++ 12 files changed, 45 insertions(+), 13 deletions(-) create mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/WrapperIOException.kt create mode 100644 app/src/release/res/values/bools.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e441e910f..48e80815b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -46,7 +46,7 @@ android:allowBackup="true" android:backupAgent="org.koitharu.kotatsu.settings.backup.AppBackupAgent" android:dataExtractionRules="@xml/backup_rules" - android:enableOnBackInvokedCallback="true" + android:enableOnBackInvokedCallback="@bool/is_predictive_back_enabled" android:fullBackupContent="@xml/backup_content" android:fullBackupOnly="true" android:icon="@mipmap/ic_launcher" diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/CaughtException.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/CaughtException.kt index 3548c0872..d606b4c6e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/CaughtException.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/CaughtException.kt @@ -1,3 +1,5 @@ package org.koitharu.kotatsu.core.exceptions -class CaughtException(cause: Throwable) : RuntimeException("${cause.javaClass.simpleName}(${cause.message})", cause) +class CaughtException( + override val cause: Throwable +) : RuntimeException("${cause.javaClass.simpleName}(${cause.message})", cause) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/WrapperIOException.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/WrapperIOException.kt new file mode 100644 index 000000000..d9c9c7aa9 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/WrapperIOException.kt @@ -0,0 +1,5 @@ +package org.koitharu.kotatsu.core.exceptions + +import okio.IOException + +class WrapperIOException(override val cause: Exception) : IOException(cause) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/AppProxySelector.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/AppProxySelector.kt index 1424f1f31..4ac33ad07 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/AppProxySelector.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/AppProxySelector.kt @@ -35,7 +35,7 @@ class AppProxySelector( if (type == Proxy.Type.DIRECT) { return Proxy.NO_PROXY } - if (address.isNullOrEmpty() || port == 0) { + if (address.isNullOrEmpty() || port < 0 || port > 0xFFFF) { throw ProxyConfigException() } cachedProxy?.let { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/GZipInterceptor.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/GZipInterceptor.kt index 09a544105..f4f666dfb 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/GZipInterceptor.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/GZipInterceptor.kt @@ -1,19 +1,26 @@ package org.koitharu.kotatsu.core.network import okhttp3.Interceptor +import okhttp3.MultipartBody import okhttp3.Response import okio.IOException +import org.koitharu.kotatsu.core.exceptions.WrapperIOException import org.koitharu.kotatsu.core.network.CommonHeaders.CONTENT_ENCODING class GZipInterceptor : Interceptor { - override fun intercept(chain: Interceptor.Chain): Response { - val newRequest = chain.request().newBuilder() - newRequest.addHeader(CONTENT_ENCODING, "gzip") - return try { + override fun intercept(chain: Interceptor.Chain): Response = try { + val request = chain.request() + if (request.body is MultipartBody) { + chain.proceed(request) + } else { + val newRequest = request.newBuilder() + newRequest.addHeader(CONTENT_ENCODING, "gzip") chain.proceed(newRequest.build()) - } catch (e: NullPointerException) { - throw IOException(e) } + } catch (e: IOException) { + throw e + } catch (e: Exception) { + throw WrapperIOException(e) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt index 5811cfa65..c201853cf 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt @@ -24,6 +24,7 @@ import org.koitharu.kotatsu.core.exceptions.ProxyConfigException import org.koitharu.kotatsu.core.exceptions.SyncApiException import org.koitharu.kotatsu.core.exceptions.UnsupportedFileException import org.koitharu.kotatsu.core.exceptions.UnsupportedSourceException +import org.koitharu.kotatsu.core.exceptions.WrapperIOException import org.koitharu.kotatsu.core.exceptions.WrongPasswordException import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver import org.koitharu.kotatsu.core.io.NullOutputStream @@ -54,6 +55,8 @@ fun Throwable.getDisplayMessage(resources: Resources): String = getDisplayMessag ?: resources.getString(R.string.error_occurred) private fun Throwable.getDisplayMessageOrNull(resources: Resources): String? = when (this) { + is CaughtException -> cause.getDisplayMessageOrNull(resources) + is WrapperIOException -> cause.getDisplayMessageOrNull(resources) is ScrobblerAuthRequiredException -> resources.getString( R.string.scrobbler_auth_required, resources.getString(scrobbler.titleResId), @@ -141,7 +144,8 @@ fun Throwable.getCauseUrl(): String? = when (this) { is ParseException -> url is NotFoundException -> url is TooManyRequestExceptions -> url - is CaughtException -> cause?.getCauseUrl() + is CaughtException -> cause.getCauseUrl() + is WrapperIOException -> cause.getCauseUrl() is NoDataReceivedException -> url is CloudFlareBlockedException -> url is CloudFlareProtectedException -> url @@ -175,7 +179,10 @@ fun Throwable.isReportable(): Boolean { return true } if (this is CaughtException) { - return cause?.isReportable() == true + return cause.isReportable() + } + if (this is WrapperIOException) { + return cause.isReportable() } if (ExceptionResolver.canResolve(this)) { return false diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/PagesCache.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/PagesCache.kt index 3b8a9dd11..b4a414dfa 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/PagesCache.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/PagesCache.kt @@ -22,6 +22,7 @@ import org.koitharu.kotatsu.core.util.ext.subdir import org.koitharu.kotatsu.core.util.ext.takeIfReadable import org.koitharu.kotatsu.core.util.ext.takeIfWriteable import org.koitharu.kotatsu.core.util.ext.writeAllCancellable +import org.koitharu.kotatsu.parsers.model.MangaPage import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.suspendlazy.suspendLazy import java.io.File diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/PageLoader.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/PageLoader.kt index 04b5759b4..0e05ea9a1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/PageLoader.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/PageLoader.kt @@ -185,7 +185,7 @@ class PageLoader @Inject constructor( prefetchLock.withLock { while (prefetchQueue.isNotEmpty()) { val page = prefetchQueue.pollFirst() ?: return@launch - if (cache.get(page.url) == null) { + if (cache.get(page.url) == null) { // FIXME use pageUrl synchronized(tasks) { tasks[page.id] = loadPageAsyncImpl(page, skipCache = false, isPrefetch = true) } @@ -203,6 +203,7 @@ class PageLoader @Inject constructor( val progress = MutableStateFlow(PROGRESS_UNDEFINED) val deferred = loaderScope.async { if (!skipCache) { + // FIXME use pageUrl cache.get(page.url)?.let { return@async it.toUri() } } counter.incrementAndGet() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderInfoBarView.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderInfoBarView.kt index 7a5ca06d2..34bca6de8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderInfoBarView.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderInfoBarView.kt @@ -209,6 +209,9 @@ class ReaderInfoBarView @JvmOverloads constructor( } private fun Drawable.drawWithOutline(canvas: Canvas) { + if (bounds.isEmpty) { + return + } var requiredScale = (bounds.width() + paint.strokeWidth * 2f) / bounds.width().toFloat() setTint(colorOutline) canvas.withScale(requiredScale, requiredScale, bounds.exactCenterX(), bounds.exactCenterY()) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/widget/WidgetUpdater.kt b/app/src/main/kotlin/org/koitharu/kotatsu/widget/WidgetUpdater.kt index c5fd21e71..1508ec24a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/widget/WidgetUpdater.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/widget/WidgetUpdater.kt @@ -30,7 +30,7 @@ class WidgetUpdater @Inject constructor( private fun updateWidgets(cls: Class<*>) { val intent = Intent(context, cls) intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE - val ids = AppWidgetManager.getInstance(context) + val ids = (AppWidgetManager.getInstance(context) ?: return) .getAppWidgetIds(ComponentName(context, cls)) intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids) context.sendBroadcast(intent) diff --git a/app/src/main/res/values/bools.xml b/app/src/main/res/values/bools.xml index 8a68dc46b..dee4d7e61 100644 --- a/app/src/main/res/values/bools.xml +++ b/app/src/main/res/values/bools.xml @@ -6,4 +6,5 @@ true false true + true diff --git a/app/src/release/res/values/bools.xml b/app/src/release/res/values/bools.xml new file mode 100644 index 000000000..a491a51a2 --- /dev/null +++ b/app/src/release/res/values/bools.xml @@ -0,0 +1,5 @@ + + + + false + From eb1eee168126b276351edbc3b397db16d7323d20 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Thu, 9 Jan 2025 08:26:43 +0200 Subject: [PATCH 03/12] Fix pages cache usage (cherry picked from commit 9e2b60e15e4333e9388b1a395a91346d43e87491) --- .../kotatsu/reader/domain/PageLoader.kt | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/PageLoader.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/PageLoader.kt index 0e05ea9a1..ff5359b3f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/PageLoader.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/PageLoader.kt @@ -185,11 +185,8 @@ class PageLoader @Inject constructor( prefetchLock.withLock { while (prefetchQueue.isNotEmpty()) { val page = prefetchQueue.pollFirst() ?: return@launch - if (cache.get(page.url) == null) { // FIXME use pageUrl - synchronized(tasks) { - tasks[page.id] = loadPageAsyncImpl(page, skipCache = false, isPrefetch = true) - } - return@launch + synchronized(tasks) { + tasks[page.id] = loadPageAsyncImpl(page, skipCache = false, isPrefetch = true) } } } @@ -202,13 +199,14 @@ class PageLoader @Inject constructor( ): ProgressDeferred { val progress = MutableStateFlow(PROGRESS_UNDEFINED) val deferred = loaderScope.async { - if (!skipCache) { - // FIXME use pageUrl - cache.get(page.url)?.let { return@async it.toUri() } - } counter.incrementAndGet() try { - loadPageImpl(page, progress, isPrefetch) + loadPageImpl( + page = page, + progress = progress, + isPrefetch = isPrefetch, + skipCache = skipCache, + ) } finally { if (counter.decrementAndGet() == 0) { onIdle() @@ -232,9 +230,13 @@ class PageLoader @Inject constructor( page: MangaPage, progress: MutableStateFlow, isPrefetch: Boolean, + skipCache: Boolean, ): Uri = semaphore.withPermit { val pageUrl = getPageUrl(page) check(pageUrl.isNotBlank()) { "Cannot obtain full image url for $page" } + if (!skipCache) { + cache.get(pageUrl)?.let { return it.toUri() } + } val uri = Uri.parse(pageUrl) return when { uri.isZipUri() -> if (uri.scheme == URI_SCHEME_ZIP) { From 7991f9ca9766065c16ad3c9df35554ba005635eb Mon Sep 17 00:00:00 2001 From: Koitharu Date: Thu, 9 Jan 2025 08:32:53 +0200 Subject: [PATCH 04/12] Skip description for ParcelableManga (cherry picked from commit bf217b3cbf1a1d5a15bf2054951587abe9e5c0a0) --- .../koitharu/kotatsu/core/model/parcelable/ParcelableManga.kt | 4 +++- .../kotatsu/favourites/ui/categories/select/FavoriteSheet.kt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableManga.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableManga.kt index fb91de501..08f644280 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableManga.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableManga.kt @@ -13,6 +13,7 @@ import org.koitharu.kotatsu.parsers.model.Manga @Parcelize data class ParcelableManga( val manga: Manga, + private val withDescription: Boolean = true, ) : Parcelable { companion object : Parceler { @@ -27,7 +28,7 @@ data class ParcelableManga( ParcelCompat.writeBoolean(parcel, isNsfw) parcel.writeString(coverUrl) parcel.writeString(largeCoverUrl) - parcel.writeString(description) + parcel.writeString(description.takeIf { withDescription }) parcel.writeParcelable(ParcelableMangaTags(tags), flags) parcel.writeSerializable(state) parcel.writeString(author) @@ -52,6 +53,7 @@ data class ParcelableManga( chapters = null, source = MangaSource(parcel.readString()), ), + withDescription = true, ) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheet.kt index df99011ab..3f9188753 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheet.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheet.kt @@ -65,7 +65,7 @@ class FavoriteSheet : BaseAdaptiveSheet(), OnLis fun show(fm: FragmentManager, manga: Collection) = FavoriteSheet().withArgs(1) { putParcelableArrayList( KEY_MANGA_LIST, - manga.mapTo(ArrayList(manga.size), ::ParcelableManga), + manga.mapTo(ArrayList(manga.size)) { ParcelableManga(it, withDescription = false) }, ) }.showDistinct(fm, TAG) } From 8bc29ac3318a937f329fa1b458d359bd6a34384d Mon Sep 17 00:00:00 2001 From: Koitharu Date: Thu, 9 Jan 2025 08:41:27 +0200 Subject: [PATCH 05/12] Fix local chapters deletion (cherry picked from commit 25eb05d3058b9b057ea9cdab21c02df03c11ab28) --- .../kotatsu/local/data/LocalMangaRepository.kt | 3 ++- .../kotatsu/local/domain/DeleteReadChaptersUseCase.kt | 10 ---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalMangaRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalMangaRepository.kt index 497967246..5f05be1d9 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalMangaRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalMangaRepository.kt @@ -152,7 +152,8 @@ class LocalMangaRepository @Inject constructor( "Manga is not stored on local storage" }.manga LocalMangaUtil(subject).deleteChapters(ids) - localStorageChanges.emit(LocalManga(subject)) + val updated = getDetails(subject) + localStorageChanges.emit(LocalManga(updated)) } suspend fun getRemoteManga(localManga: Manga): Manga? { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/domain/DeleteReadChaptersUseCase.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/domain/DeleteReadChaptersUseCase.kt index 2a77be6ee..a16eb05a3 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/domain/DeleteReadChaptersUseCase.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/domain/DeleteReadChaptersUseCase.kt @@ -1,7 +1,6 @@ package org.koitharu.kotatsu.local.domain import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.fold @@ -13,7 +12,6 @@ import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.history.data.HistoryRepository import org.koitharu.kotatsu.local.data.LocalMangaRepository -import org.koitharu.kotatsu.local.data.LocalStorageChanges import org.koitharu.kotatsu.local.domain.model.LocalManga import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaChapter @@ -26,7 +24,6 @@ class DeleteReadChaptersUseCase @Inject constructor( private val localMangaRepository: LocalMangaRepository, private val historyRepository: HistoryRepository, private val mangaRepositoryFactory: MangaRepository.Factory, - @LocalStorageChanges private val localStorageChanges: MutableSharedFlow, ) { suspend operator fun invoke(manga: Manga): Int { @@ -37,7 +34,6 @@ class DeleteReadChaptersUseCase @Inject constructor( } val task = getDeletionTask(localManga) ?: return 0 localMangaRepository.deleteChapters(task.manga.manga, task.chaptersIds) - emitUpdate(localManga) return task.chaptersIds.size } @@ -62,7 +58,6 @@ class DeleteReadChaptersUseCase @Inject constructor( }.buffer().map { runCatchingCancellable { localMangaRepository.deleteChapters(it.manga.manga, it.chaptersIds) - emitUpdate(it.manga) it.chaptersIds.size }.onFailure { it.printStackTraceDebug() @@ -88,11 +83,6 @@ class DeleteReadChaptersUseCase @Inject constructor( } } - private suspend fun emitUpdate(subject: LocalManga) { - val updated = localMangaRepository.getDetails(subject.manga) - localStorageChanges.emit(subject.copy(manga = updated)) - } - private suspend fun getAllChapters(manga: LocalManga): List = runCatchingCancellable { val remoteManga = checkNotNull(localMangaRepository.getRemoteManga(manga.manga)) checkNotNull(mangaRepositoryFactory.create(remoteManga.source).getDetails(remoteManga).chapters) From efa13df10671026de6bc678beaef9767b78315bb Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sat, 11 Jan 2025 14:59:25 +0200 Subject: [PATCH 06/12] Fix crashes --- .../favourites/ui/categories/select/FavoriteSheetViewModel.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheetViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheetViewModel.kt index 472e9b863..31442cc42 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheetViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheetViewModel.kt @@ -52,7 +52,8 @@ class FavoriteSheetViewModel @Inject constructor( settings.observeAsFlow(AppSettings.KEY_TRACKER_ENABLED) { isTrackerEnabled }, ) { categories, _, tracker -> mapList(categories, tracker) - }.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(header)) + }.withErrorHandling() + .stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(header)) fun setChecked(categoryId: Long, isChecked: Boolean) { launchJob(Dispatchers.Default) { From 8d52cab6d8924047276eea7ef795db9135f0c239 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sat, 11 Jan 2025 14:37:30 +0200 Subject: [PATCH 07/12] Fix manga importing (cherry picked from commit dcb92ed1af68c451ad64985db53ab975587a50cc) --- app/src/main/AndroidManifest.xml | 4 ++++ .../kotlin/org/koitharu/kotatsu/local/ui/ImportService.kt | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 48e80815b..691d98a38 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -279,6 +279,10 @@ + ): Boolean = try { + fun start(context: Context, uris: Collection): Boolean = try { + require(uris.isNotEmpty()) for (uri in uris) { val intent = Intent(context, ImportService::class.java) intent.putExtra(DATA_URI, uri.toString()) From 04c7ca7291b5acc5e3af792c99142fa787f3b20c Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sat, 11 Jan 2025 14:54:02 +0200 Subject: [PATCH 08/12] Improve local manga chapter names (cherry picked from commit dddb00d5efca01acc93334932185bf16a719006c) --- .../kotatsu/local/data/input/LocalMangaParser.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 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 9c28e7b16..740e2e150 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 @@ -35,7 +35,6 @@ import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaPage import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.util.runCatchingCancellable -import org.koitharu.kotatsu.parsers.util.toCamelCase import org.koitharu.kotatsu.parsers.util.toFileNameSafe import java.io.File @@ -85,7 +84,7 @@ class LocalMangaParser(private val uri: Uri) { }, ) } else { - val title = rootFile.nameWithoutExtension.replace("_", " ").toCamelCase() + val title = rootFile.name.fileNameToTitle() val coverEntry = fileSystem.findFirstImage(rootPath) val mimeTypeMap = MimeTypeMap.getSingleton() Manga( @@ -116,7 +115,7 @@ class LocalMangaParser(private val uri: Uri) { }.toString().removePrefix(Path.DIRECTORY_SEPARATOR) MangaChapter( id = "$i$s".longHashCode(), - name = s.ifEmpty { title }, + name = s.fileNameToTitle().ifEmpty { title }, number = 0f, volume = 0, source = LocalMangaSource, @@ -275,6 +274,10 @@ class LocalMangaParser(private val uri: Uri) { Path.DIRECTORY_SEPARATOR + this }.toPath() + private fun String.fileNameToTitle() = substringBeforeLast('.') + .replace('_', ' ') + .replaceFirstChar { it.uppercase() } + private fun Manga.copyInternal( url: String = this.url, coverUrl: String = this.coverUrl, From 1e90d5541b954b9cb009725205a9591a9a580ec4 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sat, 11 Jan 2025 15:09:21 +0200 Subject: [PATCH 09/12] 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 92880cb0e..f98d24aa1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,8 +18,8 @@ android { applicationId 'org.koitharu.kotatsu' minSdk = 21 targetSdk = 35 - versionCode = 697 - versionName = '7.7.5' + versionCode = 698 + versionName = '7.7.6' generatedDensities = [] testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner' ksp { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d3e9bb735..2ec7056b3 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 = "8ce6694232" +parsers = "a94adf4d90" preference = "1.2.1" recyclerview = "1.3.2" room = "2.6.1" From 8e1d02f356e907c2bafef00240d6d8ca7d2838ad Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sun, 19 Jan 2025 08:01:09 +0200 Subject: [PATCH 10/12] 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 f98d24aa1..83f5400a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,8 +18,8 @@ android { applicationId 'org.koitharu.kotatsu' minSdk = 21 targetSdk = 35 - versionCode = 698 - versionName = '7.7.6' + versionCode = 699 + versionName = '7.7.7' generatedDensities = [] testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner' ksp { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2ec7056b3..ccd9e79f6 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 = "a94adf4d90" +parsers = "8481fadbd0" preference = "1.2.1" recyclerview = "1.3.2" room = "2.6.1" From 0aa78c0d7e8036a91d1c83a920e7ee670904f72e Mon Sep 17 00:00:00 2001 From: Koitharu Date: Mon, 13 Jan 2025 19:54:10 +0200 Subject: [PATCH 11/12] Adjust manga fields nullability --- .../kotatsu/core/db/entity/EntityMapping.kt | 2 +- .../kotatsu/core/db/entity/MangaEntity.kt | 2 +- .../external/ExternalPluginContentSource.kt | 2 +- .../koitharu/kotatsu/core/util/ext/String.kt | 2 +- .../kotatsu/details/ui/DetailsActivity.kt | 2 +- .../download/ui/worker/DownloadWorker.kt | 2 +- .../kotatsu/favourites/domain/model/Cover.kt | 2 +- .../list/ui/model/MangaCompactListModel.kt | 2 +- .../list/ui/model/MangaDetailedListModel.kt | 2 +- .../kotatsu/list/ui/model/MangaGridModel.kt | 2 +- .../kotatsu/list/ui/model/MangaListModel.kt | 2 +- .../list/ui/preview/PreviewFragment.kt | 2 +- .../local/data/input/LocalMangaParser.kt | 44 +------------------ .../kotatsu/tracker/ui/feed/model/FeedItem.kt | 2 +- 14 files changed, 15 insertions(+), 55 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/db/entity/EntityMapping.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/db/entity/EntityMapping.kt index 63a5ff2f4..55a5cf020 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/db/entity/EntityMapping.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/db/entity/EntityMapping.kt @@ -49,7 +49,7 @@ fun Manga.toEntity() = MangaEntity( publicUrl = publicUrl, source = source.name, largeCoverUrl = largeCoverUrl, - coverUrl = coverUrl, + coverUrl = coverUrl.orEmpty(), altTitle = altTitle, rating = rating, isNsfw = isNsfw, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/db/entity/MangaEntity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/db/entity/MangaEntity.kt index e2c474399..9156db7b7 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/db/entity/MangaEntity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/db/entity/MangaEntity.kt @@ -14,7 +14,7 @@ data class MangaEntity( @ColumnInfo(name = "url") val url: String, @ColumnInfo(name = "public_url") val publicUrl: String, @ColumnInfo(name = "rating") val rating: Float, // normalized value [0..1] or -1 - @ColumnInfo(name = "nsfw") val isNsfw: Boolean, + @ColumnInfo(name = "nsfw") val isNsfw: Boolean, // TODO change to contentRating @ColumnInfo(name = "cover_url") val coverUrl: String, @ColumnInfo(name = "large_cover_url") val largeCoverUrl: String?, @ColumnInfo(name = "state") val state: String?, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/external/ExternalPluginContentSource.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/external/ExternalPluginContentSource.kt index a0ca968e2..f59514e90 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/external/ExternalPluginContentSource.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/external/ExternalPluginContentSource.kt @@ -82,7 +82,7 @@ class ExternalPluginContentSource( publicUrl = details.publicUrl.ifEmpty { manga.publicUrl }, rating = maxOf(details.rating, manga.rating), isNsfw = details.isNsfw, - coverUrl = details.coverUrl.ifEmpty { manga.coverUrl }, + coverUrl = details.coverUrl.ifNullOrEmpty { manga.coverUrl }, tags = details.tags + manga.tags, state = details.state ?: manga.state, author = details.author.ifNullOrEmpty { manga.author }, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/String.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/String.kt index 3294e66fb..dfc16562e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/String.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/String.kt @@ -8,7 +8,7 @@ import org.koitharu.kotatsu.parsers.util.ellipsize import org.koitharu.kotatsu.parsers.util.levenshteinDistance import java.util.UUID -inline fun C?.ifNullOrEmpty(defaultValue: () -> C): C { +inline fun C?.ifNullOrEmpty(defaultValue: () -> R): R { return if (this.isNullOrEmpty()) defaultValue() else this } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt index 720d33f28..5da9cba05 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt @@ -274,7 +274,7 @@ class DetailsActivity : startActivity( ImageActivity.newIntent( v.context, - manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }, + manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl } ?: return, manga.source, ), scaleUpActivityOptionsOf(v), diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadWorker.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadWorker.kt index 8aed8f1d4..a52c1c343 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadWorker.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadWorker.kt @@ -199,7 +199,7 @@ class DownloadWorker @AssistedInject constructor( format = task.format ?: settings.preferredDownloadFormat, ) val coverUrl = mangaDetails.largeCoverUrl.ifNullOrEmpty { mangaDetails.coverUrl } - if (coverUrl.isNotEmpty()) { + if (!coverUrl.isNullOrEmpty()) { downloadFile(coverUrl, destination, repo.source).let { file -> output.addCover(file, MimeTypeMap.getFileExtensionFromUrl(coverUrl)) file.deleteAwait() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/domain/model/Cover.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/domain/model/Cover.kt index 32c0d4c0f..d5521d410 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/domain/model/Cover.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/domain/model/Cover.kt @@ -3,7 +3,7 @@ package org.koitharu.kotatsu.favourites.domain.model import org.koitharu.kotatsu.core.model.MangaSource data class Cover( - val url: String, + val url: String?, val source: String, ) { val mangaSource by lazy { MangaSource(source) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaCompactListModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaCompactListModel.kt index fae6ccfb1..d7c122492 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaCompactListModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaCompactListModel.kt @@ -7,7 +7,7 @@ data class MangaCompactListModel( override val id: Long, override val title: String, val subtitle: String, - override val coverUrl: String, + override val coverUrl: String?, override val manga: Manga, override val counter: Int, override val progress: ReadingProgress?, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaDetailedListModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaDetailedListModel.kt index 8b6633788..ee83aa9b8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaDetailedListModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaDetailedListModel.kt @@ -8,7 +8,7 @@ data class MangaDetailedListModel( override val id: Long, override val title: String, val subtitle: String?, - override val coverUrl: String, + override val coverUrl: String?, override val manga: Manga, override val counter: Int, override val progress: ReadingProgress?, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaGridModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaGridModel.kt index 244c3f7b7..52007c3b5 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaGridModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaGridModel.kt @@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.model.Manga data class MangaGridModel( override val id: Long, override val title: String, - override val coverUrl: String, + override val coverUrl: String?, override val manga: Manga, override val counter: Int, override val progress: ReadingProgress?, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaListModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaListModel.kt index 322bf0b6a..4afb11370 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaListModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/model/MangaListModel.kt @@ -11,7 +11,7 @@ sealed class MangaListModel : ListModel { abstract val id: Long abstract val manga: Manga abstract val title: String - abstract val coverUrl: String + abstract val coverUrl: String? abstract val counter: Int abstract val isFavorite: Boolean abstract val progress: ReadingProgress? diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/preview/PreviewFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/preview/PreviewFragment.kt index 549544996..cee84e64c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/preview/PreviewFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/preview/PreviewFragment.kt @@ -100,7 +100,7 @@ class PreviewFragment : BaseFragment(), View.OnClickList R.id.imageView_cover -> startActivity( ImageActivity.newIntent( v.context, - manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }, + manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl } ?: return, manga.source, ), scaleUpActivityOptionsOf(v), 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 740e2e150..83d697a42 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 @@ -33,7 +33,6 @@ import org.koitharu.kotatsu.local.domain.model.LocalManga import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaPage -import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.toFileNameSafe import java.io.File @@ -60,7 +59,7 @@ class LocalMangaParser(private val uri: Uri) { val mangaInfo = index?.getMangaInfo() if (mangaInfo != null) { val coverEntry: Path? = index.getCoverEntry()?.let { rootPath / it } ?: fileSystem.findFirstImage(rootPath) - mangaInfo.copyInternal( + mangaInfo.copy( source = LocalMangaSource, url = rootFile.toUri().toString(), coverUrl = coverEntry?.let { uri.child(it, resolve = true).toString() }.orEmpty(), @@ -71,7 +70,7 @@ class LocalMangaParser(private val uri: Uri) { if (path != null && !fileSystem.exists(rootPath / path)) { null } else { - c.copyInternal( + c.copy( url = path?.let { uri.child(it, resolve = false).toString() } ?: uri.toString(), @@ -277,44 +276,5 @@ class LocalMangaParser(private val uri: Uri) { private fun String.fileNameToTitle() = substringBeforeLast('.') .replace('_', ' ') .replaceFirstChar { it.uppercase() } - - private fun Manga.copyInternal( - url: String = this.url, - coverUrl: String = this.coverUrl, - largeCoverUrl: String? = this.largeCoverUrl, - chapters: List? = this.chapters, - source: MangaSource = this.source, - ): Manga = Manga( - id = id, - title = title, - altTitle = altTitle, - url = url, - publicUrl = publicUrl, - rating = rating, - isNsfw = isNsfw, - coverUrl = coverUrl, - tags = tags, - state = state, - author = author, - largeCoverUrl = largeCoverUrl, - description = description, - chapters = chapters, - source = source, - ) - - private fun MangaChapter.copyInternal( - url: String = this.url, - source: MangaSource = this.source, - ) = MangaChapter( - id = id, - name = name, - number = number, - volume = volume, - url = url, - scanlator = scanlator, - uploadDate = uploadDate, - branch = branch, - source = source, - ) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/model/FeedItem.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/model/FeedItem.kt index bf9510a51..4aac9f87e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/model/FeedItem.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/model/FeedItem.kt @@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.model.Manga data class FeedItem( val id: Long, - val imageUrl: String, + val imageUrl: String?, val title: String, val manga: Manga, val count: Int, From 503bff292c2aadada20b05fcabe5d4f75f7dd375 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Mon, 13 Jan 2025 19:56:02 +0200 Subject: [PATCH 12/12] Made SyncAuthActivity exported (cherry picked from commit 663602282a8857240d6b938606241ef5b9adf755) --- app/src/main/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 691d98a38..610785d48 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -209,6 +209,7 @@