From f3b48d96b3137883b968748551ddd38fbe37b903 Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Fri, 15 Nov 2024 03:02:38 +0700 Subject: [PATCH 01/21] move `Suggestions` settings to same place --- .../screen/SettingsAppearanceScreen.kt | 25 ------------------- .../settings/screen/SettingsBrowseScreen.kt | 21 ++++++++++++++++ 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt index 5a9063862f..9ffd09a8fc 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt @@ -13,7 +13,6 @@ import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow import com.materialkolor.PaletteStyle import eu.kanade.core.preference.asState -import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.model.AppTheme import eu.kanade.domain.ui.model.TabletUiMode @@ -269,10 +268,6 @@ object SettingsAppearanceScreen : SearchableSettings { @Composable fun getForkGroup(uiPreferences: UiPreferences): Preference.PreferenceGroup { val previewsRowCount by uiPreferences.previewsRowCount().collectAsState() - // KMK --> - val sourcePreferences = remember { Injekt.get() } - val relatedMangasInOverflow by uiPreferences.expandRelatedMangas().collectAsState() - // KMK <-- return Preference.PreferenceGroup( stringResource(SYMR.strings.pref_category_fork), @@ -288,26 +283,6 @@ object SettingsAppearanceScreen : SearchableSettings { pref = uiPreferences.expandFilters(), title = stringResource(SYMR.strings.toggle_expand_search_filters), ), - // KMK --> - Preference.PreferenceItem.SwitchPreference( - pref = uiPreferences.expandRelatedMangas(), - title = stringResource(KMR.strings.pref_expand_related_mangas), - subtitle = stringResource(KMR.strings.pref_expand_related_mangas_summary), - enabled = sourcePreferences.relatedMangas().get(), - ), - Preference.PreferenceItem.SwitchPreference( - pref = uiPreferences.relatedMangasInOverflow(), - enabled = !relatedMangasInOverflow, - title = stringResource(KMR.strings.put_related_mangas_in_overflow), - subtitle = stringResource(KMR.strings.put_related_mangas_in_overflow_summary), - ), - Preference.PreferenceItem.SwitchPreference( - pref = uiPreferences.showHomeOnRelatedMangas(), - title = stringResource(KMR.strings.pref_show_home_on_related_mangas), - subtitle = stringResource(KMR.strings.pref_show_home_on_related_mangas_summary), - enabled = sourcePreferences.relatedMangas().get(), - ), - // KMK <-- Preference.PreferenceItem.SwitchPreference( pref = uiPreferences.recommendsInOverflow(), title = stringResource(SYMR.strings.put_recommends_in_overflow), diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt index a071a24441..a0239a5c19 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt @@ -53,6 +53,9 @@ object SettingsBrowseScreen : SearchableSettings { val uiPreferences = remember { Injekt.get() } val unsortedPreferences = remember { Injekt.get() } // SY <-- + // KMK --> + val relatedMangasInOverflow by uiPreferences.expandRelatedMangas().collectAsState() + // KMK <-- return listOf( // SY --> Preference.PreferenceGroup( @@ -64,6 +67,24 @@ object SettingsBrowseScreen : SearchableSettings { title = stringResource(KMR.strings.pref_source_related_mangas), subtitle = stringResource(KMR.strings.pref_source_related_mangas_summary), ), + Preference.PreferenceItem.SwitchPreference( + pref = uiPreferences.expandRelatedMangas(), + title = stringResource(KMR.strings.pref_expand_related_mangas), + subtitle = stringResource(KMR.strings.pref_expand_related_mangas_summary), + enabled = sourcePreferences.relatedMangas().get(), + ), + Preference.PreferenceItem.SwitchPreference( + pref = uiPreferences.relatedMangasInOverflow(), + enabled = !relatedMangasInOverflow, + title = stringResource(KMR.strings.put_related_mangas_in_overflow), + subtitle = stringResource(KMR.strings.put_related_mangas_in_overflow_summary), + ), + Preference.PreferenceItem.SwitchPreference( + pref = uiPreferences.showHomeOnRelatedMangas(), + title = stringResource(KMR.strings.pref_show_home_on_related_mangas), + subtitle = stringResource(KMR.strings.pref_show_home_on_related_mangas_summary), + enabled = sourcePreferences.relatedMangas().get(), + ), // KMK <-- kotlin.run { val count by sourcePreferences.sourcesTabCategories().collectAsState() From 4f4af3be4aee4c15c0bc2c75d1525225d84f8aaa Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Wed, 27 Nov 2024 23:18:57 +0700 Subject: [PATCH 02/21] fix vertical chapter navigator overlap bottom bar --- .../eu/kanade/presentation/reader/appbars/ReaderAppBars.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderAppBars.kt b/app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderAppBars.kt index 68e699d05f..19bda87a02 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderAppBars.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderAppBars.kt @@ -143,7 +143,7 @@ fun ReaderAppBars( animationSpec = animationSpec, ), modifier = modifierWithInsetsPadding - .padding(bottom = 48.dp, top = 120.dp) + .padding(bottom = 64.dp, top = 112.dp) .align(Alignment.TopStart), ) { ChapterNavigator( @@ -171,7 +171,7 @@ fun ReaderAppBars( animationSpec = animationSpec, ), modifier = modifierWithInsetsPadding - .padding(bottom = 48.dp, top = 120.dp) + .padding(bottom = 64.dp, top = 112.dp) .align(Alignment.TopEnd), ) { ChapterNavigator( From 96c50d07a93a4b8cea39192b4081a9e007341b75 Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Thu, 5 Dec 2024 15:43:15 +0700 Subject: [PATCH 03/21] add color to notification --- app/src/beta/res/drawable/komikku.png | Bin 0 -> 21883 bytes app/src/beta/res/values/colors.xml | 4 ++++ app/src/debug/res/drawable/komikku.png | Bin 0 -> 19275 bytes app/src/debug/res/values/colors.xml | 4 ++++ app/src/main/java/eu/kanade/tachiyomi/App.kt | 1 + .../tachiyomi/data/backup/BackupNotifier.kt | 7 +++++-- .../tachiyomi/data/download/DownloadJob.kt | 2 ++ .../tachiyomi/data/download/DownloadNotifier.kt | 5 ++++- .../data/library/LibraryUpdateNotifier.kt | 8 +++++++- .../kanade/tachiyomi/data/sync/SyncNotifier.kt | 7 +++++-- .../tachiyomi/data/updater/AppUpdateNotifier.kt | 5 ++++- .../extension/api/ExtensionUpdateNotifier.kt | 2 ++ .../extension/util/ExtensionInstallService.kt | 2 ++ .../tachiyomi/ui/reader/SaveImageNotifier.kt | 5 ++++- .../main/java/exh/eh/EHentaiUpdateNotifier.kt | 5 ++++- app/src/main/res/drawable/komikku.png | Bin 0 -> 11523 bytes app/src/main/res/values/colors.xml | 4 ++++ 17 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 app/src/beta/res/drawable/komikku.png create mode 100644 app/src/beta/res/values/colors.xml create mode 100644 app/src/debug/res/drawable/komikku.png create mode 100644 app/src/debug/res/values/colors.xml create mode 100644 app/src/main/res/drawable/komikku.png create mode 100644 app/src/main/res/values/colors.xml diff --git a/app/src/beta/res/drawable/komikku.png b/app/src/beta/res/drawable/komikku.png new file mode 100644 index 0000000000000000000000000000000000000000..bd0b448a97b888d05bb65253d26e469092959495 GIT binary patch literal 21883 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 wj!CMl%P-D#g*{5w$)dae?>2k?cfh}!k`es + + #c837ab + diff --git a/app/src/debug/res/drawable/komikku.png b/app/src/debug/res/drawable/komikku.png new file mode 100644 index 0000000000000000000000000000000000000000..7ec36827f8e38be4fb4001cff0bcbfe6450c21c0 GIT binary patch literal 19275 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 + + #ffe680 + diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 33bf58e327..62b1f18873 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -156,6 +156,7 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor setContentTitle(stringResource(MR.strings.pref_incognito_mode)) setContentText(stringResource(MR.strings.notification_incognito_text)) setSmallIcon(R.drawable.ic_glasses_24dp) + setColor(ContextCompat.getColor(applicationContext, R.color.ic_launcher)) setOngoing(true) val pendingIntent = PendingIntent.getBroadcast( diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupNotifier.kt index dfab7d3995..9afc92936c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupNotifier.kt @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.backup import android.content.Context import android.graphics.BitmapFactory import androidx.core.app.NotificationCompat +import androidx.core.content.ContextCompat import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.core.security.SecurityPreferences @@ -34,8 +35,9 @@ class BackupNotifier(private val context: Context) { private val progressNotificationBuilder = context.notificationBuilder( Notifications.CHANNEL_BACKUP_RESTORE_PROGRESS, ) { - setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher)) setSmallIcon(R.drawable.ic_komikku) + setColor(ContextCompat.getColor(context, R.color.ic_launcher)) + setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.komikku)) setAutoCancel(false) setOngoing(true) setOnlyAlertOnce(true) @@ -44,8 +46,9 @@ class BackupNotifier(private val context: Context) { private val completeNotificationBuilder = context.notificationBuilder( Notifications.CHANNEL_BACKUP_RESTORE_COMPLETE, ) { - setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher)) setSmallIcon(R.drawable.ic_komikku) + setColor(ContextCompat.getColor(context, R.color.ic_launcher)) + setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.komikku)) setAutoCancel(false) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadJob.kt index 6a7b4469ee..8c9d14b591 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadJob.kt @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.download import android.content.Context import android.content.pm.ServiceInfo import android.os.Build +import androidx.core.content.ContextCompat import androidx.lifecycle.asFlow import androidx.work.CoroutineWorker import androidx.work.ExistingWorkPolicy @@ -41,6 +42,7 @@ class DownloadJob(context: Context, workerParams: WorkerParameters) : CoroutineW val notification = applicationContext.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_PROGRESS) { setContentTitle(applicationContext.getString(R.string.download_notifier_downloader_title)) setSmallIcon(android.R.drawable.stat_sys_download) + setColor(ContextCompat.getColor(applicationContext, R.color.ic_launcher)) }.build() return ForegroundInfo( Notifications.ID_DOWNLOAD_CHAPTER_PROGRESS, diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt index ee3a5fe0f4..2c00c914e3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt @@ -4,6 +4,7 @@ import android.app.PendingIntent import android.content.Context import android.graphics.BitmapFactory import androidx.core.app.NotificationCompat +import androidx.core.content.ContextCompat import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.core.security.SecurityPreferences import eu.kanade.tachiyomi.data.download.model.Download @@ -30,7 +31,8 @@ internal class DownloadNotifier(private val context: Context) { private val progressNotificationBuilder by lazy { context.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_PROGRESS) { - setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher)) + setColor(ContextCompat.getColor(context, R.color.ic_launcher)) + setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.komikku)) setAutoCancel(false) setOnlyAlertOnce(true) } @@ -38,6 +40,7 @@ internal class DownloadNotifier(private val context: Context) { private val errorNotificationBuilder by lazy { context.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_ERROR) { + setColor(ContextCompat.getColor(context, R.color.ic_launcher)) setAutoCancel(false) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt index 6e246f7287..c596e8f2cf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt @@ -8,6 +8,7 @@ import android.graphics.Bitmap import android.graphics.BitmapFactory import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat +import androidx.core.content.ContextCompat import coil3.asDrawable import coil3.imageLoader import coil3.request.ImageRequest @@ -70,7 +71,7 @@ class LibraryUpdateNotifier( * Bitmap of the app for notifications. */ private val notificationBitmap by lazy { - BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher) + BitmapFactory.decodeResource(context.resources, R.drawable.komikku) } /** @@ -80,6 +81,7 @@ class LibraryUpdateNotifier( context.notificationBuilder(Notifications.CHANNEL_LIBRARY_PROGRESS) { setContentTitle(context.stringResource(MR.strings.app_name)) setSmallIcon(R.drawable.ic_refresh_24dp) + setColor(ContextCompat.getColor(context, R.color.ic_launcher)) setLargeIcon(notificationBitmap) setOngoing(true) setOnlyAlertOnce(true) @@ -142,6 +144,7 @@ class LibraryUpdateNotifier( NotificationCompat.BigTextStyle().bigText(context.stringResource(MR.strings.notification_size_warning)), ) setSmallIcon(R.drawable.ic_warning_white_24dp) + setColor(ContextCompat.getColor(context, R.color.ic_launcher)) setTimeoutAfter(Downloader.WARNING_NOTIF_TIMEOUT_MS) setContentIntent(NotificationHandler.openUrl(context, HELP_WARNING_URL)) } @@ -164,6 +167,7 @@ class LibraryUpdateNotifier( setContentTitle(context.stringResource(MR.strings.notification_update_error, failed)) setContentText(context.stringResource(MR.strings.action_show_errors)) setSmallIcon(R.drawable.ic_komikku) + setColor(ContextCompat.getColor(context, R.color.ic_launcher)) setContentIntent(NotificationReceiver.openErrorLogPendingActivity(context)) } @@ -204,6 +208,7 @@ class LibraryUpdateNotifier( } setSmallIcon(R.drawable.ic_komikku) + setColor(ContextCompat.getColor(context, R.color.ic_launcher)) setLargeIcon(notificationBitmap) setGroup(Notifications.GROUP_NEW_CHAPTERS) @@ -240,6 +245,7 @@ class LibraryUpdateNotifier( setStyle(NotificationCompat.BigTextStyle().bigText(description)) setSmallIcon(R.drawable.ic_komikku) + setColor(ContextCompat.getColor(context, R.color.ic_launcher)) if (icon != null) { setLargeIcon(icon) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/sync/SyncNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/sync/SyncNotifier.kt index 9d8b2d521e..e74d9650ff 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/sync/SyncNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/sync/SyncNotifier.kt @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.sync import android.content.Context import android.graphics.BitmapFactory import androidx.core.app.NotificationCompat +import androidx.core.content.ContextCompat import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.core.security.SecurityPreferences import eu.kanade.tachiyomi.data.SyncStatus @@ -26,8 +27,9 @@ class SyncNotifier(private val context: Context) { private val progressNotificationBuilder = context.notificationBuilder( Notifications.CHANNEL_BACKUP_RESTORE_PROGRESS, ) { - setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher)) setSmallIcon(R.drawable.ic_komikku) + setColor(ContextCompat.getColor(context, R.color.ic_launcher)) + setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.komikku)) setAutoCancel(false) setOngoing(true) setOnlyAlertOnce(true) @@ -36,8 +38,9 @@ class SyncNotifier(private val context: Context) { private val completeNotificationBuilder = context.notificationBuilder( Notifications.CHANNEL_BACKUP_RESTORE_PROGRESS, ) { - setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher)) setSmallIcon(R.drawable.ic_komikku) + setColor(ContextCompat.getColor(context, R.color.ic_launcher)) + setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.komikku)) setAutoCancel(false) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt index c570141191..9882387a05 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.Intent import android.net.Uri import androidx.core.app.NotificationCompat +import androidx.core.content.ContextCompat import androidx.core.net.toUri import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.R @@ -20,7 +21,9 @@ import tachiyomi.i18n.kmk.KMR internal class AppUpdateNotifier(private val context: Context) { - private val notificationBuilder = context.notificationBuilder(Notifications.CHANNEL_APP_UPDATE) + private val notificationBuilder = context.notificationBuilder(Notifications.CHANNEL_APP_UPDATE) { + setColor(ContextCompat.getColor(context, R.color.ic_launcher)) + } /** * Call to show notification. diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt index 2b675ae60f..b1a25b75aa 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.extension.api import android.content.Context import androidx.core.app.NotificationCompat +import androidx.core.content.ContextCompat import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.core.security.SecurityPreferences import eu.kanade.tachiyomi.data.notification.NotificationReceiver @@ -35,6 +36,7 @@ class ExtensionUpdateNotifier( setStyle(NotificationCompat.BigTextStyle().bigText(extNames)) } setSmallIcon(R.drawable.ic_extension_24dp) + setColor(ContextCompat.getColor(context, R.color.ic_launcher)) setContentIntent(NotificationReceiver.openExtensionsPendingActivity(context)) setAutoCancel(true) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallService.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallService.kt index 9fd5c6ef6a..268b430c61 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallService.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.os.IBinder +import androidx.core.content.ContextCompat import eu.kanade.domain.base.BasePreferences import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.notification.Notifications @@ -26,6 +27,7 @@ class ExtensionInstallService : Service() { override fun onCreate() { val notification = notificationBuilder(Notifications.CHANNEL_EXTENSIONS_UPDATE) { setSmallIcon(R.drawable.ic_komikku) + setColor(ContextCompat.getColor(applicationContext, R.color.ic_launcher)) setAutoCancel(false) setOngoing(true) setShowWhen(false) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt index 6b08dfeb30..cdfff460e7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt @@ -4,6 +4,7 @@ import android.content.Context import android.graphics.Bitmap import android.net.Uri import androidx.core.app.NotificationCompat +import androidx.core.content.ContextCompat import coil3.asDrawable import coil3.imageLoader import coil3.request.CachePolicy @@ -24,7 +25,9 @@ import tachiyomi.i18n.MR */ class SaveImageNotifier(private val context: Context) { - private val notificationBuilder = context.notificationBuilder(Notifications.CHANNEL_COMMON) + private val notificationBuilder = context.notificationBuilder(Notifications.CHANNEL_COMMON) { + setColor(ContextCompat.getColor(context, R.color.ic_launcher)) + } private val notificationId: Int = Notifications.ID_DOWNLOAD_IMAGE /** diff --git a/app/src/main/java/exh/eh/EHentaiUpdateNotifier.kt b/app/src/main/java/exh/eh/EHentaiUpdateNotifier.kt index 8b378b1987..4ddeb3a09d 100644 --- a/app/src/main/java/exh/eh/EHentaiUpdateNotifier.kt +++ b/app/src/main/java/exh/eh/EHentaiUpdateNotifier.kt @@ -4,6 +4,7 @@ import android.content.Context import android.graphics.BitmapFactory import android.net.Uri import androidx.core.app.NotificationCompat +import androidx.core.content.ContextCompat import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.core.security.SecurityPreferences import eu.kanade.tachiyomi.data.notification.NotificationReceiver @@ -32,7 +33,7 @@ class EHentaiUpdateNotifier(private val context: Context) { * Bitmap of the app for notifications. */ private val notificationBitmap by lazy { - BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher) + BitmapFactory.decodeResource(context.resources, R.drawable.komikku) } /** @@ -42,6 +43,7 @@ class EHentaiUpdateNotifier(private val context: Context) { context.notificationBuilder(Notifications.CHANNEL_LIBRARY_EHENTAI) { setContentTitle(context.stringResource(MR.strings.app_name)) setSmallIcon(R.drawable.ic_refresh_24dp) + setColor(ContextCompat.getColor(context, R.color.ic_launcher)) setLargeIcon(notificationBitmap) setOngoing(true) setOnlyAlertOnce(true) @@ -95,6 +97,7 @@ class EHentaiUpdateNotifier(private val context: Context) { setContentTitle(context.stringResource(MR.strings.notification_update_error, failed)) setContentText(context.stringResource(MR.strings.action_show_errors)) setSmallIcon(R.drawable.ic_komikku) + setColor(ContextCompat.getColor(context, R.color.ic_launcher)) setContentIntent(NotificationReceiver.openErrorLogPendingActivity(context, uri)) } diff --git a/app/src/main/res/drawable/komikku.png b/app/src/main/res/drawable/komikku.png new file mode 100644 index 0000000000000000000000000000000000000000..d4a071ff6974261fd5e9138bf7b099e3e9f8b6e2 GIT binary patch literal 11523 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 NURp)!mxO89{{c_jGT;CJ literal 0 HcmV?d00001 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000000..f2c8a10391 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #ff5555 + From 326446b84a9a63136bcb0d1767020b7b8ea76e5d Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Tue, 3 Dec 2024 02:07:20 +0700 Subject: [PATCH 04/21] revert user-agent to latest desktop --- .../kotlin/eu/kanade/tachiyomi/network/NetworkPreferences.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkPreferences.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkPreferences.kt index 1ec7356f35..17adb7a47c 100644 --- a/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkPreferences.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkPreferences.kt @@ -19,8 +19,7 @@ class NetworkPreferences( fun defaultUserAgent(): Preference { return preferenceStore.getString( "default_user_agent", - "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Mobile Safari/537.3", - // "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:132.0) Gecko/20100101 Firefox/132.0", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:132.0) Gecko/20100101 Firefox/132.0", ) } } From 62e5b5134bcd429a27401599df12a80929c2fc83 Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Wed, 11 Dec 2024 22:31:46 +0700 Subject: [PATCH 05/21] switch off integrated H to show E-H source & extension --- .../settings/screen/SettingsAdvancedScreen.kt | 16 ++++----- .../tachiyomi/extension/ExtensionManager.kt | 13 +++++-- .../tachiyomi/extension/api/ExtensionApi.kt | 11 +++++- .../tachiyomi/source/AndroidSourceManager.kt | 36 ++++++++++++++----- .../tachiyomi/source/online/all/NHentai.kt | 3 -- 5 files changed, 56 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt index cc20d6d8d5..58d023dd5e 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt @@ -750,6 +750,14 @@ object SettingsAdvancedScreen : SearchableSettings { true }, ), + Preference.PreferenceItem.SwitchPreference( + pref = sourcePreferences.enableSourceBlacklist(), + title = stringResource(SYMR.strings.enable_source_blacklist), + subtitle = stringResource( + SYMR.strings.enable_source_blacklist_summary, + stringResource(MR.strings.app_name), + ), + ), Preference.PreferenceItem.SwitchPreference( pref = delegateSourcePreferences.delegateSources(), title = stringResource(SYMR.strings.toggle_delegated_sources), @@ -770,14 +778,6 @@ object SettingsAdvancedScreen : SearchableSettings { })" }.toMap().toImmutableMap(), ), - Preference.PreferenceItem.SwitchPreference( - pref = sourcePreferences.enableSourceBlacklist(), - title = stringResource(SYMR.strings.enable_source_blacklist), - subtitle = stringResource( - SYMR.strings.enable_source_blacklist_summary, - stringResource(MR.strings.app_name), - ), - ), kotlin.run { var enableEncryptDatabase by rememberSaveable { mutableStateOf(false) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt index f2e9cf4e91..11c19bc582 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt @@ -33,6 +33,7 @@ import kotlinx.coroutines.flow.stateIn import logcat.LogPriority import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.system.logcat +import tachiyomi.domain.UnsortedPreferences import tachiyomi.domain.source.model.StubSource import tachiyomi.i18n.MR import uy.kohesive.injekt.Injekt @@ -154,8 +155,16 @@ class ExtensionManager( } } - private fun Extension.isBlacklisted(blacklistEnabled: Boolean = preferences.enableSourceBlacklist().get()): Boolean { - return pkgName in BlacklistedSources.BLACKLISTED_EXTENSIONS && blacklistEnabled + private fun Extension.isBlacklisted( + blacklistEnabled: Boolean = preferences.enableSourceBlacklist().get(), + // KMK --> + isHentaiEnabled: Boolean = Injekt.get().isHentaiEnabled().get(), + // KMK <-- + ): Boolean { + return pkgName in BlacklistedSources.BLACKLISTED_EXTENSIONS && blacklistEnabled && + // KMK --> + isHentaiEnabled + // KMK <-- } // EXH <-- diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt index 470c1a2a1d..05a0749c50 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt @@ -23,6 +23,9 @@ import tachiyomi.core.common.preference.Preference import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.system.logcat +import tachiyomi.domain.UnsortedPreferences +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy import java.time.Instant import kotlin.time.Duration.Companion.days @@ -181,8 +184,14 @@ internal class ExtensionApi { // SY --> private fun Extension.isBlacklisted( blacklistEnabled: Boolean = sourcePreferences.enableSourceBlacklist().get(), + // KMK --> + isHentaiEnabled: Boolean = Injekt.get().isHentaiEnabled().get(), + // KMK <-- ): Boolean { - return pkgName in BlacklistedSources.BLACKLISTED_EXTENSIONS && blacklistEnabled + return pkgName in BlacklistedSources.BLACKLISTED_EXTENSIONS && blacklistEnabled && + // KMK --> + isHentaiEnabled + // KMK <-- } // SY <-- } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt b/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt index a5ae68a1d1..323818b08b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt @@ -79,8 +79,13 @@ class AndroidSourceManager( .combine(preferences.enableExhentai().changes()) { extensions, enableExhentai -> extensions to enableExhentai } + // KMK --> + .combine( + preferences.isHentaiEnabled().changes(), + ) { (a, b), c -> Triple(a, b, c) } + // KMK <-- // SY <-- - .collectLatest { (extensions, enableExhentai) -> + .collectLatest { (extensions, enableExhentai/* KMK --> */, isHentaiEnabled/* KMK <-- */) -> val mutableMap = ConcurrentHashMap( mapOf( LocalSource.ID to LocalSource( @@ -93,16 +98,20 @@ class AndroidSourceManager( ), ), ).apply { - // SY --> - put(EH_SOURCE_ID, EHentai(EH_SOURCE_ID, false, context)) - if (enableExhentai) { - put(EXH_SOURCE_ID, EHentai(EXH_SOURCE_ID, true, context)) + // KMK --> + if (isHentaiEnabled) { + // KMK <-- + // SY --> + put(EH_SOURCE_ID, EHentai(EH_SOURCE_ID, false, context)) + if (enableExhentai) { + put(EXH_SOURCE_ID, EHentai(EXH_SOURCE_ID, true, context)) + } + put(MERGED_SOURCE_ID, MergedSource()) } - put(MERGED_SOURCE_ID, MergedSource()) // SY <-- } extensions.forEach { extension -> - extension.sources.mapNotNull { it.toInternalSource() }.forEach { + extension.sources.mapNotNull { it.toInternalSource(/* KMK --> */isHentaiEnabled/* KMK <-- */) }.forEach { mutableMap[it.id] = it registerStubSource(StubSource.from(it)) } @@ -123,7 +132,11 @@ class AndroidSourceManager( } } - private fun Source.toInternalSource(): Source? { + private fun Source.toInternalSource( + // KMK --> + isHentaiEnabled: Boolean, + // KMK <-- + ): Source? { // EXH --> val sourceQName = this::class.qualifiedName val factories = DELEGATED_SOURCES.entries @@ -158,7 +171,12 @@ class AndroidSourceManager( this } - return if (id in BlacklistedSources.BLACKLISTED_EXT_SOURCES) { + return if ( + // KMK --> + isHentaiEnabled && + // KMK <-- + id in BlacklistedSources.BLACKLISTED_EXT_SOURCES + ) { xLogD( "Removing blacklisted source: (id: %s, name: %s, lang: %s)!", id, diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/NHentai.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/NHentai.kt index 754894b7df..bbef567a06 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/NHentai.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/NHentai.kt @@ -21,7 +21,6 @@ import exh.metadata.metadata.NHentaiSearchMetadata import exh.metadata.metadata.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedTag import exh.source.DelegatedHttpSource -import exh.source.NHENTAI_SOURCE_ID import exh.util.trimOrNull import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchMangaSuspend @@ -239,8 +238,6 @@ class NHentai(delegate: HttpSource, val context: Context) : } companion object { - const val otherId = NHENTAI_SOURCE_ID - private val jsonParser = Json { ignoreUnknownKeys = true } From 88a772a5954e71c1be18ef355db98af692c9f0e9 Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Fri, 13 Dec 2024 23:15:12 +0700 Subject: [PATCH 06/21] More languages for E-H/ExH, also more popular with rate 5 --- .../tachiyomi/extension/ExtensionManager.kt | 13 ++-- .../tachiyomi/source/AndroidSourceManager.kt | 18 +++--- .../tachiyomi/source/online/all/EHentai.kt | 63 +++++++++++++++++-- .../java/exh/source/BlacklistedSources.kt | 22 +------ .../java/exh/source/DomainSourceHelpers.kt | 14 +++-- .../commonMain/kotlin/exh/source/SourceIds.kt | 44 +++++++++++++ 6 files changed, 130 insertions(+), 44 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt index 11c19bc582..35ad98582b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt @@ -17,8 +17,8 @@ import eu.kanade.tachiyomi.extension.util.ExtensionLoader import eu.kanade.tachiyomi.util.system.toast import exh.log.xLogD import exh.source.BlacklistedSources -import exh.source.EH_SOURCE_ID -import exh.source.EXH_SOURCE_ID +import exh.source.EHENTAI_EXT_SOURCES +import exh.source.EXHENTAI_EXT_SOURCES import exh.source.MERGED_SOURCE_ID import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob @@ -105,8 +105,10 @@ class ExtensionManager( // SY --> return when (sourceId) { - EH_SOURCE_ID -> ContextCompat.getDrawable(context, R.mipmap.ic_ehentai_source) - EXH_SOURCE_ID -> ContextCompat.getDrawable(context, R.mipmap.ic_exhentai_source) + // KMK --> + in EHENTAI_EXT_SOURCES.keys -> ContextCompat.getDrawable(context, R.mipmap.ic_ehentai_source) + in EXHENTAI_EXT_SOURCES.keys -> ContextCompat.getDrawable(context, R.mipmap.ic_exhentai_source) + // KMK <-- MERGED_SOURCE_ID -> ContextCompat.getDrawable(context, R.mipmap.ic_merged_source) else -> null } @@ -161,7 +163,8 @@ class ExtensionManager( isHentaiEnabled: Boolean = Injekt.get().isHentaiEnabled().get(), // KMK <-- ): Boolean { - return pkgName in BlacklistedSources.BLACKLISTED_EXTENSIONS && blacklistEnabled && + return pkgName in BlacklistedSources.BLACKLISTED_EXTENSIONS && + blacklistEnabled && // KMK --> isHentaiEnabled // KMK <-- diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt b/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt index 323818b08b..432321b9a5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt @@ -15,9 +15,9 @@ import eu.kanade.tachiyomi.source.online.english.Tsumino import exh.log.xLogD import exh.source.BlacklistedSources import exh.source.DelegatedHttpSource -import exh.source.EH_SOURCE_ID +import exh.source.EHENTAI_EXT_SOURCES import exh.source.EIGHTMUSES_SOURCE_ID -import exh.source.EXH_SOURCE_ID +import exh.source.EXHENTAI_EXT_SOURCES import exh.source.EnhancedHttpSource import exh.source.HBROWSE_SOURCE_ID import exh.source.MERGED_SOURCE_ID @@ -100,15 +100,19 @@ class AndroidSourceManager( ).apply { // KMK --> if (isHentaiEnabled) { - // KMK <-- - // SY --> - put(EH_SOURCE_ID, EHentai(EH_SOURCE_ID, false, context)) + EHENTAI_EXT_SOURCES.forEach { (id, lang) -> + put(id, EHentai(id, false, context, lang)) + } if (enableExhentai) { - put(EXH_SOURCE_ID, EHentai(EXH_SOURCE_ID, true, context)) + EXHENTAI_EXT_SOURCES.forEach { (id, lang) -> + put(id, EHentai(id, true, context, lang)) + } } + // SY --> put(MERGED_SOURCE_ID, MergedSource()) + // SY <-- } - // SY <-- + // KMK <-- } extensions.forEach { extension -> extension.sources.mapNotNull { it.toInternalSource(/* KMK --> */isHentaiEnabled/* KMK <-- */) }.forEach { diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt index ad8c9485bf..f8c569f4a0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt @@ -97,6 +97,9 @@ class EHentai( override val id: Long, val exh: Boolean, val context: Context, + // KMK --> + override val lang: String = "all", + // KMK <-- ) : HttpSource(), MetadataSource, UrlImportableSource, @@ -114,9 +117,19 @@ class EHentai( override val baseUrl: String get() = "https://$domain" - override val lang = "all" override val supportsLatest = true + // KMK --> + private val ehLang = languageMapping[lang] + + // true if lang is a "natural human language" + private fun isLangNatural(): Boolean = lang !in listOf("none", "other", "all") + + private fun languageTag(): String { + return "language:$ehLang" + } + // KMK <-- + private val preferences: UnsortedPreferences by injectLazy() private val updateHelper: EHentaiUpdateHelper by injectLazy() @@ -467,9 +480,18 @@ class EHentai( return if (it.text() == ">") it.attr("href") else null } - override fun popularMangaRequest(page: Int): Request { - return exGet("$baseUrl/popular") - } + override fun popularMangaRequest(page: Int) = + // KMK --> + if (isLangNatural()) { + exGet("$baseUrl/?f_search=${languageTag()}&f_srdd=5&f_sr=on", page) + } else { + if (page > 1) { + exGet("$baseUrl/?f_srdd=5&f_sr=on", page - 1) + } else { + // KMK <-- + exGet("$baseUrl/popular") + } + } private fun Observable.checkValid(): Observable = map { it.checkValid() @@ -565,7 +587,14 @@ class EHentai( ) } - override fun latestUpdatesRequest(page: Int) = exGet(baseUrl, page) + override fun latestUpdatesRequest(page: Int) = + // KMK --> + if (isLangNatural()) { + exGet("$baseUrl/?f_search=${languageTag()}", page) + } else { + // KMK <-- + exGet(baseUrl, page) + } override fun popularMangaParse(response: Response) = genericMangaParse(response) override fun searchMangaParse(response: Response) = genericMangaParse(response) @@ -911,7 +940,7 @@ class EHentai( // Session-less extended display mode (for users without ExHentai) cookies["sl"] = "dm_2" - // Ignore all content warnings + // Ignore all content warnings ("Offensive For Everyone") cookies["nw"] = "1" return cookies @@ -1382,5 +1411,27 @@ class EHentai( fun buildCookies(cookies: Map) = cookies.entries.joinToString(separator = "; ") { "${URLEncoder.encode(it.key, "UTF-8")}=${URLEncoder.encode(it.value, "UTF-8")}" } + + // KMK --> + val languageMapping = mapOf( + "ja" to "japanese", + "en" to "english", + "zh" to "chinese", + "nl" to "dutch", + "fr" to "french", + "de" to "german", + "hu" to "hungarian", + "it" to "italian", + "ko" to "korean", + "pl" to "polish", + "pt-BR" to "portuguese", + "ru" to "russian", + "es" to "spanish", + "th" to "thai", + "vi" to "vietnamese", + "none" to "n/a", + "other" to "other", + ) + // KMK <-- } } diff --git a/app/src/main/java/exh/source/BlacklistedSources.kt b/app/src/main/java/exh/source/BlacklistedSources.kt index 1559394bd4..92393d3b0f 100644 --- a/app/src/main/java/exh/source/BlacklistedSources.kt +++ b/app/src/main/java/exh/source/BlacklistedSources.kt @@ -1,27 +1,7 @@ package exh.source object BlacklistedSources { - val EHENTAI_EXT_SOURCES = longArrayOf( - 8100626124886895451, - 57122881048805941, - 4678440076103929247, - 1876021963378735852, - 3955189842350477641, - 4348288691341764259, - 773611868725221145, - 5759417018342755550, - 825187715438990384, - 6116711405602166104, - 7151438547982231541, - 2171445159732592630, - 3032959619549451093, - 5980349886941016589, - 6073266008352078708, - 5499077866612745456, - 6140480779421365791, - ) - - val BLACKLISTED_EXT_SOURCES = EHENTAI_EXT_SOURCES + val BLACKLISTED_EXT_SOURCES = EHENTAI_EXT_SOURCES.keys val BLACKLISTED_EXTENSIONS = arrayOf( "eu.kanade.tachiyomi.extension.all.ehentai", diff --git a/domain/src/main/java/exh/source/DomainSourceHelpers.kt b/domain/src/main/java/exh/source/DomainSourceHelpers.kt index 7fcbc43977..db85ea83bd 100644 --- a/domain/src/main/java/exh/source/DomainSourceHelpers.kt +++ b/domain/src/main/java/exh/source/DomainSourceHelpers.kt @@ -10,21 +10,25 @@ var nHentaiSourceIds: List = emptyList() var mangaDexSourceIds: List = emptyList() +// KMK --> var LIBRARY_UPDATE_EXCLUDED_SOURCES = listOf( - EH_SOURCE_ID, - EXH_SOURCE_ID, PURURIN_SOURCE_ID, -) +) + EHENTAI_EXT_SOURCES.keys + EXHENTAI_EXT_SOURCES.keys +// KMK <-- // This method MUST be fast! fun isMetadataSource(source: Long) = source in 6900..6999 || metadataDelegatedSourceIds.binarySearch(source) >= 0 -fun Source.isEhBasedSource() = id == EH_SOURCE_ID || id == EXH_SOURCE_ID +// KMK --> +fun Source.isEhBasedSource() = id in EHENTAI_EXT_SOURCES.keys || id in EXHENTAI_EXT_SOURCES.keys +// KMK <-- fun Source.isMdBasedSource() = id in mangaDexSourceIds -fun Manga.isEhBasedManga() = source == EH_SOURCE_ID || source == EXH_SOURCE_ID +// KMK --> +fun Manga.isEhBasedManga() = source in EHENTAI_EXT_SOURCES.keys || source in EXHENTAI_EXT_SOURCES.keys +// KMK <-- fun Source.getMainSource(): Source = if (this is EnhancedHttpSource) { this.source() diff --git a/source-api/src/commonMain/kotlin/exh/source/SourceIds.kt b/source-api/src/commonMain/kotlin/exh/source/SourceIds.kt index 07bf5ceb07..2adbf002e2 100644 --- a/source-api/src/commonMain/kotlin/exh/source/SourceIds.kt +++ b/source-api/src/commonMain/kotlin/exh/source/SourceIds.kt @@ -14,3 +14,47 @@ const val MERGED_SOURCE_ID = LEWD_SOURCE_SERIES + 69 const val NHENTAI_OLD_ID = 6907L const val TSUMINO_OLD_ID = 6909L const val HBROWSE_OLD_ID = 6912L + +// KMK --> +val EHENTAI_EXT_SOURCES = mapOf( + 8100626124886895451 to "ja", // E-Hentai (Ja) + 57122881048805941 to "en", // E-Hentai (En) + 4678440076103929247 to "zh", // E-Hentai (Zh) + 1876021963378735852 to "nl", // E-Hentai (Nl) + 3955189842350477641 to "fr", // E-Hentai (Fr) + 4348288691341764259 to "de", // E-Hentai (De) + 773611868725221145 to "hu", // E-Hentai (Hu) + 5759417018342755550 to "it", // E-Hentai (It) + 825187715438990384 to "ko", // E-Hentai (Ko) + 6116711405602166104 to "pl", // E-Hentai (Pl) + 7151438547982231541 to "pt-BR", // E-Hentai (PtBr) + 2171445159732592630 to "ru", // E-Hentai (Ru) + 3032959619549451093 to "es", // E-Hentai (Es) + 5980349886941016589 to "th", // E-Hentai (Th) + 6073266008352078708 to "vi", // E-Hentai (Vi) + 5499077866612745456 to "none", // E-Hentai (None) + 6140480779421365791 to "other", // E-Hentai (Other) + EH_SOURCE_ID to "all", // E-Hentai (Multi) +) + +val EXHENTAI_EXT_SOURCES = mapOf( + 4069364610143267166 to "ja", // E-Hentai (Ja) + 6024400692103629868 to "en", // E-Hentai (En) + 1394807835077780591 to "zh", // E-Hentai (Zh) + 3403092744483051659 to "nl", // E-Hentai (Nl) + 2063958147920418330 to "fr", // E-Hentai (Fr) + 4277540540471304114 to "de", // E-Hentai (De) + 5580677059017993793 to "hu", // E-Hentai (Hu) + 6348816521182710144 to "it", // E-Hentai (It) + 933801322201782118 to "ko", // E-Hentai (Ko) + 250678340923599076 to "pl", // E-Hentai (Pl) + 7151438547982231541 to "pt-BR", // E-Hentai (PtBr) + 2298110591802103872 to "ru", // E-Hentai (Ru) + 1471697890032830855 to "es", // E-Hentai (Es) + 5297549186919793998 to "th", // E-Hentai (Th) + 1904260310237764859 to "vi", // E-Hentai (Vi) + 2351087900641384311 to "none", // E-Hentai (None) + 609703168489499116 to "other", // E-Hentai (Other) + EXH_SOURCE_ID to "all", // E-Hentai (Multi) +) +// KMK <-- From e4b224b362cdbd8b26d1166fadacd7fe0b983e98 Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Sat, 14 Dec 2024 02:25:22 +0700 Subject: [PATCH 07/21] Don't hide Multi source when toggle Integrated H --- .../more/settings/screen/SettingsAdvancedScreen.kt | 7 ------- .../main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt | 7 ------- 2 files changed, 14 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt index 58d023dd5e..1d28603800 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt @@ -740,13 +740,6 @@ object SettingsAdvancedScreen : SearchableSettings { title = stringResource(SYMR.strings.toggle_hentai_features), subtitle = stringResource(SYMR.strings.toggle_hentai_features_summary), onValueChanged = { - if (it) { - BlacklistedSources.HIDDEN_SOURCES += EH_SOURCE_ID - BlacklistedSources.HIDDEN_SOURCES += EXH_SOURCE_ID - } else { - BlacklistedSources.HIDDEN_SOURCES -= EH_SOURCE_ID - BlacklistedSources.HIDDEN_SOURCES -= EXH_SOURCE_ID - } true }, ), diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 348784806d..16bec072a9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -392,13 +392,6 @@ class MainActivity : BaseActivity() { chapterCache.clear() } } - - // SY --> - if (!unsortedPreferences.isHentaiEnabled().get()) { - BlacklistedSources.HIDDEN_SOURCES += EH_SOURCE_ID - BlacklistedSources.HIDDEN_SOURCES += EXH_SOURCE_ID - } - // SY --> } // KMK --> From 53b90e50c771ec0d1e63bbceba0f76065e8551de Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Sat, 14 Dec 2024 02:28:22 +0700 Subject: [PATCH 08/21] Avoid crash trying to load ehentaiBrowseDisplayMode --- .../eu/kanade/presentation/manga/components/NamespaceTags.kt | 4 ++-- .../java/eu/kanade/tachiyomi/extension/ExtensionManager.kt | 4 ++-- .../java/eu/kanade/tachiyomi/source/online/all/EHentai.kt | 3 +++ domain/src/main/java/exh/source/DomainSourceHelpers.kt | 5 +++-- .../eu/kanade/tachiyomi/source/online/all/EhBasedSource.kt | 3 +++ 5 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/all/EhBasedSource.kt diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/NamespaceTags.kt b/app/src/main/java/eu/kanade/presentation/manga/components/NamespaceTags.kt index 0477f9565c..c077e17aa0 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/NamespaceTags.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/NamespaceTags.kt @@ -31,8 +31,8 @@ import eu.kanade.tachiyomi.source.online.all.EHentai import exh.metadata.metadata.EHentaiSearchMetadata import exh.metadata.metadata.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedTag -import exh.source.EH_SOURCE_ID import exh.source.EXH_SOURCE_ID +import exh.source.isEhBasedSource import exh.util.SourceTagsUtil import androidx.compose.material3.SuggestionChipDefaults as SuggestionChipDefaultsM3 @@ -64,7 +64,7 @@ value class SearchMetadataChips( } else { SourceTagsUtil.getWrappedTag(source.id, fullTag = it.name) } ?: it.name, - border = if (source.id == EXH_SOURCE_ID || source.id == EH_SOURCE_ID) { + border = if (source.isEhBasedSource()) { when (it.type) { EHentaiSearchMetadata.TAG_TYPE_NORMAL -> 2 EHentaiSearchMetadata.TAG_TYPE_LIGHT -> 1 diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt index 35ad98582b..52c61a1c14 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt @@ -106,8 +106,8 @@ class ExtensionManager( // SY --> return when (sourceId) { // KMK --> - in EHENTAI_EXT_SOURCES.keys -> ContextCompat.getDrawable(context, R.mipmap.ic_ehentai_source) - in EXHENTAI_EXT_SOURCES.keys -> ContextCompat.getDrawable(context, R.mipmap.ic_exhentai_source) + in EHENTAI_EXT_SOURCES -> ContextCompat.getDrawable(context, R.mipmap.ic_ehentai_source) + in EXHENTAI_EXT_SOURCES -> ContextCompat.getDrawable(context, R.mipmap.ic_exhentai_source) // KMK <-- MERGED_SOURCE_ID -> ContextCompat.getDrawable(context, R.mipmap.ic_merged_source) else -> null diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt index f8c569f4a0..fc820065fd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt @@ -101,6 +101,9 @@ class EHentai( override val lang: String = "all", // KMK <-- ) : HttpSource(), + // KMK --> + EhBasedSource, + // KMK <-- MetadataSource, UrlImportableSource, NamespaceSource, diff --git a/domain/src/main/java/exh/source/DomainSourceHelpers.kt b/domain/src/main/java/exh/source/DomainSourceHelpers.kt index db85ea83bd..040fa7bbf6 100644 --- a/domain/src/main/java/exh/source/DomainSourceHelpers.kt +++ b/domain/src/main/java/exh/source/DomainSourceHelpers.kt @@ -1,6 +1,7 @@ package exh.source import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.source.online.all.EhBasedSource import tachiyomi.domain.manga.model.Manga // Used to speed up isLewdSource @@ -21,13 +22,13 @@ fun isMetadataSource(source: Long) = source in 6900..6999 || metadataDelegatedSourceIds.binarySearch(source) >= 0 // KMK --> -fun Source.isEhBasedSource() = id in EHENTAI_EXT_SOURCES.keys || id in EXHENTAI_EXT_SOURCES.keys +fun Source.isEhBasedSource() = this is EhBasedSource && id in EHENTAI_EXT_SOURCES || id in EXHENTAI_EXT_SOURCES // KMK <-- fun Source.isMdBasedSource() = id in mangaDexSourceIds // KMK --> -fun Manga.isEhBasedManga() = source in EHENTAI_EXT_SOURCES.keys || source in EXHENTAI_EXT_SOURCES.keys +fun Manga.isEhBasedManga() = source in EHENTAI_EXT_SOURCES || source in EXHENTAI_EXT_SOURCES // KMK <-- fun Source.getMainSource(): Source = if (this is EnhancedHttpSource) { diff --git a/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/all/EhBasedSource.kt b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/all/EhBasedSource.kt new file mode 100644 index 0000000000..409ed697e7 --- /dev/null +++ b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/all/EhBasedSource.kt @@ -0,0 +1,3 @@ +package eu.kanade.tachiyomi.source.online.all + +interface EhBasedSource From 64d0bbc7594e24e7cc3a92e78a0be91a6387c059 Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Thu, 12 Dec 2024 03:16:37 +0700 Subject: [PATCH 09/21] change source ID to matching general extension --- .../java/eu/kanade/tachiyomi/source/online/all/EHentai.kt | 1 - source-api/src/commonMain/kotlin/exh/source/SourceIds.kt | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt index fc820065fd..3b596da889 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt @@ -289,7 +289,6 @@ class EHentai( private fun getDateTag(element: Element?): Long? { val text = element?.text()?.nullIfBlank() return if (text != null) { - println(text) val date = ZonedDateTime.parse(text, MetadataUtil.EX_DATE_FORMAT.withZone(ZoneOffset.UTC)) date?.toInstant()?.toEpochMilli() } else { diff --git a/source-api/src/commonMain/kotlin/exh/source/SourceIds.kt b/source-api/src/commonMain/kotlin/exh/source/SourceIds.kt index 2adbf002e2..b28877fa1a 100644 --- a/source-api/src/commonMain/kotlin/exh/source/SourceIds.kt +++ b/source-api/src/commonMain/kotlin/exh/source/SourceIds.kt @@ -2,8 +2,8 @@ package exh.source // Lewd source IDs const val LEWD_SOURCE_SERIES = 6900L -const val EH_SOURCE_ID = LEWD_SOURCE_SERIES + 1 -const val EXH_SOURCE_ID = LEWD_SOURCE_SERIES + 2 +const val EH_SOURCE_ID = 1713178126840476467 // LEWD_SOURCE_SERIES + 1 +const val EXH_SOURCE_ID = 6225928719850211219 // LEWD_SOURCE_SERIES + 2 const val NHENTAI_SOURCE_ID = 7309872737163460316L const val PURURIN_SOURCE_ID = 2221515250486218861L const val TSUMINO_SOURCE_ID = 6707338697138388238L From 656d2ab913ac8fa838ab18f913fbd30f1502a254 Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Fri, 13 Dec 2024 23:14:20 +0700 Subject: [PATCH 10/21] migration old source ID to new source ID --- .../migration/migrations/EHentaiMigration.kt | 21 +++++++++++++++++++ .../core/migration/migrations/Migrations.kt | 1 + .../commonMain/kotlin/exh/source/SourceIds.kt | 9 ++++++++ 3 files changed, 31 insertions(+) create mode 100644 app/src/main/java/mihon/core/migration/migrations/EHentaiMigration.kt diff --git a/app/src/main/java/mihon/core/migration/migrations/EHentaiMigration.kt b/app/src/main/java/mihon/core/migration/migrations/EHentaiMigration.kt new file mode 100644 index 0000000000..dc20dd2ec4 --- /dev/null +++ b/app/src/main/java/mihon/core/migration/migrations/EHentaiMigration.kt @@ -0,0 +1,21 @@ +package mihon.core.migration.migrations + +import exh.source.EH_OLD_ID +import exh.source.EH_SOURCE_ID +import exh.source.EXH_OLD_ID +import exh.source.EXH_SOURCE_ID +import mihon.core.migration.MigrateUtils +import mihon.core.migration.Migration +import mihon.core.migration.MigrationContext +import tachiyomi.core.common.util.lang.withIOContext + +class EHentaiMigration : Migration { + override val version: Float = 72f + + override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext { + MigrateUtils.updateSourceId(migrationContext, EH_SOURCE_ID, EH_OLD_ID) + MigrateUtils.updateSourceId(migrationContext, EXH_SOURCE_ID, EXH_OLD_ID) + + return@withIOContext true + } +} diff --git a/app/src/main/java/mihon/core/migration/migrations/Migrations.kt b/app/src/main/java/mihon/core/migration/migrations/Migrations.kt index 5fbb364656..22f37b347e 100644 --- a/app/src/main/java/mihon/core/migration/migrations/Migrations.kt +++ b/app/src/main/java/mihon/core/migration/migrations/Migrations.kt @@ -49,5 +49,6 @@ val migrations: List OfficialExtensionRepositoryMigration(), IntegratedHentaiMigration(), SetupAppUpdateMigration(), + EHentaiMigration(), // KMK <-- ) diff --git a/source-api/src/commonMain/kotlin/exh/source/SourceIds.kt b/source-api/src/commonMain/kotlin/exh/source/SourceIds.kt index b28877fa1a..e17df31468 100644 --- a/source-api/src/commonMain/kotlin/exh/source/SourceIds.kt +++ b/source-api/src/commonMain/kotlin/exh/source/SourceIds.kt @@ -2,8 +2,12 @@ package exh.source // Lewd source IDs const val LEWD_SOURCE_SERIES = 6900L + +// KMK --> const val EH_SOURCE_ID = 1713178126840476467 // LEWD_SOURCE_SERIES + 1 const val EXH_SOURCE_ID = 6225928719850211219 // LEWD_SOURCE_SERIES + 2 +// KMK <-- + const val NHENTAI_SOURCE_ID = 7309872737163460316L const val PURURIN_SOURCE_ID = 2221515250486218861L const val TSUMINO_SOURCE_ID = 6707338697138388238L @@ -11,6 +15,11 @@ const val EIGHTMUSES_SOURCE_ID = 1802675169972965535L const val HBROWSE_SOURCE_ID = 1401584337232758222L const val MERGED_SOURCE_ID = LEWD_SOURCE_SERIES + 69 +// KMK --> +const val EH_OLD_ID = 6901L +const val EXH_OLD_ID = 6902L +// KMK <-- + const val NHENTAI_OLD_ID = 6907L const val TSUMINO_OLD_ID = 6909L const val HBROWSE_OLD_ID = 6912L From 10c3aed56613609ba5b06d3a5985f4be856ea39e Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Fri, 13 Dec 2024 23:21:15 +0700 Subject: [PATCH 11/21] Allow importing of EHentai extension backups --- app/src/main/java/exh/EXHMigrations.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/exh/EXHMigrations.kt b/app/src/main/java/exh/EXHMigrations.kt index 76789e7768..d9c0b36379 100644 --- a/app/src/main/java/exh/EXHMigrations.kt +++ b/app/src/main/java/exh/EXHMigrations.kt @@ -1,7 +1,9 @@ package exh -import exh.source.BlacklistedSources +import exh.source.EH_OLD_ID import exh.source.EH_SOURCE_ID +import exh.source.EXH_OLD_ID +import exh.source.EXH_SOURCE_ID import exh.source.HBROWSE_OLD_ID import exh.source.HBROWSE_SOURCE_ID import exh.source.NHENTAI_OLD_ID @@ -42,12 +44,19 @@ object EXHMigrations { ) } + // KMK --> // Allow importing of EHentai extension backups - if (newManga.source in BlacklistedSources.EHENTAI_EXT_SOURCES) { + if (newManga.source == EH_OLD_ID) { newManga = newManga.copy( source = EH_SOURCE_ID, ) } + if (newManga.source == EXH_OLD_ID) { + newManga = newManga.copy( + source = EXH_SOURCE_ID, + ) + } + // KMK <-- return newManga } From 229b782cd46e5ad1595d80f4aa8336d6f7063acb Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Tue, 17 Dec 2024 00:04:46 +0700 Subject: [PATCH 12/21] limit library update & metadata source to only Multi lang --- .../ui/library/LibraryScreenModel.kt | 38 +++++++------------ .../java/exh/source/DomainSourceHelpers.kt | 10 +++-- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt index f0ffdacc04..2bed5b7840 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt @@ -998,31 +998,21 @@ class LibraryScreenModel( unfiltered.asFlow().cancellable().filter { item -> val mangaId = item.libraryManga.manga.id val sourceId = item.libraryManga.manga.source - if (isMetadataSource(sourceId)) { - if (mangaWithMetaIds.binarySearch(mangaId) < 0) { - // No meta? Filter using title - filterManga( - queries = parsedQuery, - libraryManga = item.libraryManga, - tracks = tracks[mangaId], - source = sources[sourceId], - loggedInTrackServices = loggedInTrackServices, - ) - } else { - val tags = getSearchTags.await(mangaId) - val titles = getSearchTitles.await(mangaId) - filterManga( - queries = parsedQuery, - libraryManga = item.libraryManga, - tracks = tracks[mangaId], - source = sources[sourceId], - checkGenre = false, - searchTags = tags, - searchTitles = titles, - loggedInTrackServices = loggedInTrackServices, - ) - } + if (isMetadataSource(sourceId) && mangaWithMetaIds.binarySearch(mangaId) >= 0) { + val tags = getSearchTags.await(mangaId) + val titles = getSearchTitles.await(mangaId) + filterManga( + queries = parsedQuery, + libraryManga = item.libraryManga, + tracks = tracks[mangaId], + source = sources[sourceId], + checkGenre = false, + searchTags = tags, + searchTitles = titles, + loggedInTrackServices = loggedInTrackServices, + ) } else { + // No meta? Filter using title filterManga( queries = parsedQuery, libraryManga = item.libraryManga, diff --git a/domain/src/main/java/exh/source/DomainSourceHelpers.kt b/domain/src/main/java/exh/source/DomainSourceHelpers.kt index 040fa7bbf6..a7d06e830e 100644 --- a/domain/src/main/java/exh/source/DomainSourceHelpers.kt +++ b/domain/src/main/java/exh/source/DomainSourceHelpers.kt @@ -11,14 +11,18 @@ var nHentaiSourceIds: List = emptyList() var mangaDexSourceIds: List = emptyList() -// KMK --> var LIBRARY_UPDATE_EXCLUDED_SOURCES = listOf( + EH_SOURCE_ID, + EXH_SOURCE_ID, PURURIN_SOURCE_ID, -) + EHENTAI_EXT_SOURCES.keys + EXHENTAI_EXT_SOURCES.keys -// KMK <-- +) // This method MUST be fast! fun isMetadataSource(source: Long) = source in 6900..6999 || + // KMK --> + source == EH_SOURCE_ID || + source == EXH_SOURCE_ID || + // KMK <-- metadataDelegatedSourceIds.binarySearch(source) >= 0 // KMK --> From 20441b41e35579df54e315d9390df3c0af90b415 Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Tue, 17 Dec 2024 14:44:50 +0700 Subject: [PATCH 13/21] update app version --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 33dbd0d9fd..dd2ab386e5 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -30,8 +30,8 @@ android { defaultConfig { applicationId = "app.komikku" - versionCode = 71 - versionName = "1.12.2" + versionCode = 72 + versionName = "1.13.0" buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"") From 68d8f3c7b4ef7bde2da259e1a9d76116f11c3d44 Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Tue, 31 Dec 2024 10:47:11 +0700 Subject: [PATCH 14/21] fix spotless --- .../more/settings/screen/SettingsAdvancedScreen.kt | 3 --- app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt | 3 --- 2 files changed, 6 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt index 1d28603800..5a159a3259 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt @@ -72,9 +72,6 @@ import eu.kanade.tachiyomi.util.system.toast import exh.debug.SettingsDebugScreen import exh.log.EHLogLevel import exh.pref.DelegateSourcePreferences -import exh.source.BlacklistedSources -import exh.source.EH_SOURCE_ID -import exh.source.EXH_SOURCE_ID import exh.util.toAnnotatedString import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentMapOf diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 16bec072a9..638d034269 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -93,9 +93,6 @@ import eu.kanade.tachiyomi.util.view.setComposeContent import exh.debug.DebugToggles import exh.eh.EHentaiUpdateWorker import exh.log.DebugModeOverlay -import exh.source.BlacklistedSources -import exh.source.EH_SOURCE_ID -import exh.source.EXH_SOURCE_ID import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.collectLatest From 14209b42293901f5dca5a0eeacaee920d5e0df1d Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Tue, 31 Dec 2024 13:17:36 +0700 Subject: [PATCH 15/21] add app icon to extension update notification --- .../kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt index b1a25b75aa..eadeac9155 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt @@ -1,6 +1,7 @@ package eu.kanade.tachiyomi.extension.api import android.content.Context +import android.graphics.BitmapFactory import androidx.core.app.NotificationCompat import androidx.core.content.ContextCompat import eu.kanade.tachiyomi.R @@ -37,6 +38,7 @@ class ExtensionUpdateNotifier( } setSmallIcon(R.drawable.ic_extension_24dp) setColor(ContextCompat.getColor(context, R.color.ic_launcher)) + setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.komikku)) setContentIntent(NotificationReceiver.openExtensionsPendingActivity(context)) setAutoCancel(true) } From c5b61412e429282b56af48c42dc0ccb59cf5a92a Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Tue, 31 Dec 2024 14:10:21 +0700 Subject: [PATCH 16/21] add app icon to download error notification --- .../java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt index 2c00c914e3..f58fb4d6a4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt @@ -41,6 +41,7 @@ internal class DownloadNotifier(private val context: Context) { private val errorNotificationBuilder by lazy { context.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_ERROR) { setColor(ContextCompat.getColor(context, R.color.ic_launcher)) + setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.komikku)) setAutoCancel(false) } } From c4be056fe755d6575871cc897b050e8cd6c9f735 Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Thu, 2 Jan 2025 17:22:58 +0700 Subject: [PATCH 17/21] also migrate saved search/feed & pinned EH/EXH source ID when restore from backup or update app --- .../source/service/SourcePreferences.kt | 4 +- .../backup/restore/restorers/FeedRestorer.kt | 7 +- .../restore/restorers/PreferenceRestorer.kt | 12 ++- .../restore/restorers/SavedSearchRestorer.kt | 7 +- .../tachiyomi/extension/api/ExtensionApi.kt | 3 +- app/src/main/java/exh/EXHMigrations.kt | 80 +++++++++++++++++++ .../java/mihon/core/migration/MigrateUtils.kt | 18 +++++ data/src/main/sqldelight/tachiyomi/data/eh.sq | 12 +++ 8 files changed, 138 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt b/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt index fdc5e085a0..944281a87d 100644 --- a/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt +++ b/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt @@ -23,7 +23,7 @@ class SourcePreferences( fun disabledSources() = preferenceStore.getStringSet("hidden_catalogues", emptySet()) - fun pinnedSources() = preferenceStore.getStringSet("pinned_catalogues", emptySet()) + fun pinnedSources() = preferenceStore.getStringSet(pinnedSourcesPrefKey, emptySet()) fun lastUsedSource() = preferenceStore.getLong( Preference.appStateKey("last_catalogue_source"), @@ -107,3 +107,5 @@ class SourcePreferences( fun relatedMangas() = preferenceStore.getBoolean("related_mangas", true) // KMK <-- } + +const val pinnedSourcesPrefKey = "pinned_catalogues" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/FeedRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/FeedRestorer.kt index cac304d9fe..cf8687d287 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/FeedRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/FeedRestorer.kt @@ -1,6 +1,7 @@ package eu.kanade.tachiyomi.data.backup.restore.restorers import eu.kanade.tachiyomi.data.backup.models.BackupFeed +import exh.EXHMigrations import exh.util.nullIfBlank import tachiyomi.data.DatabaseHandler import uy.kohesive.injekt.Injekt @@ -20,7 +21,11 @@ class FeedRestorer( saved_searchQueries.selectAll() } - backupFeeds.filter { backupFeed -> + backupFeeds.map { + // KMK --> + EXHMigrations.migrateBackupFeed(it) + // KMK <-- + }.filter { backupFeed -> // Filter out source's global Popular/Latest feed already existed backupFeed.savedSearch == null && currentFeeds.none { currentFeed -> diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/PreferenceRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/PreferenceRestorer.kt index b70280c557..3d371088fa 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/PreferenceRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/PreferenceRestorer.kt @@ -1,6 +1,7 @@ package eu.kanade.tachiyomi.data.backup.restore.restorers import android.content.Context +import eu.kanade.domain.source.service.pinnedSourcesPrefKey import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob import eu.kanade.tachiyomi.data.backup.models.BackupPreference import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences @@ -12,6 +13,7 @@ import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue import eu.kanade.tachiyomi.data.library.LibraryUpdateJob import eu.kanade.tachiyomi.source.sourcePreferences +import exh.EXHMigrations import tachiyomi.core.common.preference.AndroidPreferenceStore import tachiyomi.core.common.preference.PreferenceStore import uy.kohesive.injekt.Injekt @@ -70,7 +72,15 @@ class PreferenceRestorer( } is StringSetPreferenceValue -> { if (prefs[key] is Set<*>?) { - preferenceStore.getStringSet(key).set(value.value) + preferenceStore.getStringSet(key).set( + // KMK --> + if (key == pinnedSourcesPrefKey) { + EXHMigrations.migratePinnedSources(value.value) + } else { + // KMK <-- + value.value + }, + ) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/SavedSearchRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/SavedSearchRestorer.kt index 8e4735a6f4..a904138949 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/SavedSearchRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/SavedSearchRestorer.kt @@ -1,6 +1,7 @@ package eu.kanade.tachiyomi.data.backup.restore.restorers import eu.kanade.tachiyomi.data.backup.models.BackupSavedSearch +import exh.EXHMigrations import exh.util.nullIfBlank import tachiyomi.data.DatabaseHandler import uy.kohesive.injekt.Injekt @@ -22,7 +23,11 @@ class SavedSearchRestorer( // KMK <-- } - backupSavedSearches.filter { backupSavedSearch -> + backupSavedSearches.map { + // KMK --> + EXHMigrations.migrateBackupSavedSearch(it) + // KMK <-- + }.filter { backupSavedSearch -> currentSavedSearches.none { currentSavedSearch -> currentSavedSearch.source == backupSavedSearch.source && currentSavedSearch.name == backupSavedSearch.name && diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt index 05a0749c50..58a0c8dcce 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt @@ -188,7 +188,8 @@ internal class ExtensionApi { isHentaiEnabled: Boolean = Injekt.get().isHentaiEnabled().get(), // KMK <-- ): Boolean { - return pkgName in BlacklistedSources.BLACKLISTED_EXTENSIONS && blacklistEnabled && + return pkgName in BlacklistedSources.BLACKLISTED_EXTENSIONS && + blacklistEnabled && // KMK --> isHentaiEnabled // KMK <-- diff --git a/app/src/main/java/exh/EXHMigrations.kt b/app/src/main/java/exh/EXHMigrations.kt index d9c0b36379..e296fc7c83 100644 --- a/app/src/main/java/exh/EXHMigrations.kt +++ b/app/src/main/java/exh/EXHMigrations.kt @@ -1,5 +1,7 @@ package exh +import eu.kanade.tachiyomi.data.backup.models.BackupFeed +import eu.kanade.tachiyomi.data.backup.models.BackupSavedSearch import exh.source.EH_OLD_ID import exh.source.EH_SOURCE_ID import exh.source.EXH_OLD_ID @@ -61,6 +63,84 @@ object EXHMigrations { return newManga } + // KMK --> + /** + * Migrate old source ID of delegated sources in old backup + */ + fun migrateBackupSavedSearch(savedSearch: BackupSavedSearch): BackupSavedSearch { + return when (savedSearch.source) { + NHENTAI_OLD_ID -> savedSearch.copy( + source = NHENTAI_SOURCE_ID, + ) + TSUMINO_OLD_ID -> savedSearch.copy( + source = TSUMINO_SOURCE_ID, + ) + HBROWSE_OLD_ID -> savedSearch.copy( + source = HBROWSE_SOURCE_ID, + ) + EH_OLD_ID -> savedSearch.copy( + source = EH_SOURCE_ID, + ) + EXH_OLD_ID -> savedSearch.copy( + source = EXH_SOURCE_ID, + ) + else -> savedSearch + } + } + + /** + * Migrate old source ID of delegated sources in old backup + */ + fun migrateBackupFeed(feed: BackupFeed): BackupFeed { + return when (feed.source) { + NHENTAI_OLD_ID -> feed.copy( + source = NHENTAI_SOURCE_ID, + ) + TSUMINO_OLD_ID -> feed.copy( + source = TSUMINO_SOURCE_ID, + ) + HBROWSE_OLD_ID -> feed.copy( + source = HBROWSE_SOURCE_ID, + ) + EH_OLD_ID -> feed.copy( + source = EH_SOURCE_ID, + ) + EXH_OLD_ID -> feed.copy( + source = EXH_SOURCE_ID, + ) + else -> feed + } + } + + /** + * Migrate old source ID of delegated sources in old backup + */ + fun migratePinnedSources(pinnedSources: Set): Set { + var pinned = pinnedSources + if (NHENTAI_OLD_ID.toString() in pinned) { + pinned = pinned.minus(NHENTAI_OLD_ID.toString()) + .plus(NHENTAI_SOURCE_ID.toString()) + } + if (TSUMINO_OLD_ID.toString() in pinned) { + pinned = pinned.minus(TSUMINO_OLD_ID.toString()) + .plus(TSUMINO_SOURCE_ID.toString()) + } + if (HBROWSE_OLD_ID.toString() in pinned) { + pinned = pinned.minus(HBROWSE_OLD_ID.toString()) + .plus(HBROWSE_SOURCE_ID.toString()) + } + if (EH_OLD_ID.toString() in pinned) { + pinned = pinned.minus(EH_OLD_ID.toString()) + .plus(EH_SOURCE_ID.toString()) + } + if (EXH_OLD_ID.toString() in pinned) { + pinned = pinned.minus(EXH_OLD_ID.toString()) + .plus(EXH_SOURCE_ID.toString()) + } + return pinned + } + // KMK --> + private fun getUrlWithoutDomain(orig: String): String { return try { val uri = URI(orig) diff --git a/app/src/main/java/mihon/core/migration/MigrateUtils.kt b/app/src/main/java/mihon/core/migration/MigrateUtils.kt index d3e6592cd2..2b18cac673 100644 --- a/app/src/main/java/mihon/core/migration/MigrateUtils.kt +++ b/app/src/main/java/mihon/core/migration/MigrateUtils.kt @@ -1,7 +1,9 @@ package mihon.core.migration +import eu.kanade.domain.source.service.SourcePreferences import kotlinx.coroutines.runBlocking import tachiyomi.core.common.preference.PreferenceStore +import tachiyomi.core.common.preference.getAndSet import tachiyomi.data.DatabaseHandler object MigrateUtils { @@ -9,7 +11,23 @@ object MigrateUtils { val handler = migrationContext.get() ?: return runBlocking { handler.await { ehQueries.migrateSource(newId, oldId) } + + // KMK --> + // Migrate saved searches & feeds + handler.await { ehQueries.migrateSourceSavedSearch(newId, oldId) } + handler.await { ehQueries.migrateSourceFeed(newId, oldId) } + } + + // Also update pin + val preferences = migrationContext.get() ?: return + val isPinned = oldId.toString() in preferences.pinnedSources().get() + if (isPinned) { + preferences.pinnedSources().getAndSet { pinned -> + pinned.minus(oldId.toString()) + .plus(newId.toString()) + } } + // KMK <-- } @Suppress("UNCHECKED_CAST") diff --git a/data/src/main/sqldelight/tachiyomi/data/eh.sq b/data/src/main/sqldelight/tachiyomi/data/eh.sq index 3b4bf9220f..7107f734de 100644 --- a/data/src/main/sqldelight/tachiyomi/data/eh.sq +++ b/data/src/main/sqldelight/tachiyomi/data/eh.sq @@ -6,6 +6,18 @@ UPDATE mangas SET source = :newId WHERE source = :oldId; +-- KMK --> +migrateSourceSavedSearch: +UPDATE saved_search +SET source = :newId +WHERE source = :oldId; + +migrateSourceFeed: +UPDATE feed_saved_search +SET source = :newId +WHERE source = :oldId; +-- KMK <-- + getChaptersByMangaIds: SELECT * FROM chapters WHERE manga_id IN :mangaIds; From 187c7c9b326490f0f36a6fa5648c7e8b0f7feb76 Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Fri, 3 Jan 2025 01:06:08 +0700 Subject: [PATCH 18/21] Drag to reorder feeds --- .../presentation/browse/FeedOrderScreen.kt | 57 +++++++++++++------ .../kanade/presentation/browse/FeedScreen.kt | 16 ++---- .../browse/SourceFeedOrderScreen.kt | 51 +++++++++++------ .../presentation/browse/SourceFeedScreen.kt | 18 +++--- .../browse/components/FeedOrderListItem.kt | 51 +++-------------- .../browse/components/SourceFeedDialogs.kt | 37 ------------ .../category/components/CategoryListItem.kt | 1 - .../ui/browse/feed/FeedScreenModel.kt | 17 +----- .../tachiyomi/ui/browse/feed/FeedTab.kt | 7 +-- .../ui/browse/source/feed/SourceFeedScreen.kt | 7 +-- .../source/feed/SourceFeedScreenModel.kt | 16 +----- .../category/interactor/HideCategory.kt | 2 +- .../domain/source/interactor/ReorderFeed.kt | 19 +------ .../source/model/FeedSavedSearchUpdate.kt | 2 + 14 files changed, 108 insertions(+), 193 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/browse/FeedOrderScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/FeedOrderScreen.kt index 723083d458..eeadabcf42 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/FeedOrderScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/FeedOrderScreen.kt @@ -3,13 +3,20 @@ package eu.kanade.presentation.browse import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import eu.kanade.presentation.browse.components.FeedOrderListItem import eu.kanade.tachiyomi.ui.browse.feed.FeedScreenState +import sh.calvin.reorderable.ReorderableItem +import sh.calvin.reorderable.rememberReorderableLazyListState import tachiyomi.domain.source.model.FeedSavedSearch import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.padding @@ -22,39 +29,53 @@ import tachiyomi.presentation.core.util.plus fun FeedOrderScreen( state: FeedScreenState, onClickDelete: (FeedSavedSearch) -> Unit, - onClickMoveUp: (FeedSavedSearch) -> Unit, - onClickMoveDown: (FeedSavedSearch) -> Unit, + changeOrder: (FeedSavedSearch, Int) -> Unit, ) { when { state.isLoading -> LoadingScreen() - state.isEmpty -> EmptyScreen( + state.isEmpty || state.items.isNullOrEmpty() -> EmptyScreen( stringRes = MR.strings.empty_screen, ) else -> { val lazyListState = rememberLazyListState() - val feeds = state.items ?: emptyList() + val feeds = state.items + + var reorderableList by remember { mutableStateOf(feeds) } + val reorderableLazyColumnState = rememberReorderableLazyListState(lazyListState) { from, to -> + reorderableList = reorderableList.toMutableList().apply { + changeOrder(reorderableList[from.index].feed, to.index - from.index) + add(to.index, removeAt(from.index)) + } + } + + LaunchedEffect(feeds) { + if (!reorderableLazyColumnState.isAnyItemDragging) { + reorderableList = feeds + } + } + LazyColumn( state = lazyListState, contentPadding = topSmallPaddingValues + PaddingValues(horizontal = MaterialTheme.padding.medium), verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small), ) { - itemsIndexed( - items = feeds, - key = { _, feed -> "feed-${feed.feed.id}" }, - ) { index, feed -> - FeedOrderListItem( - modifier = Modifier.animateItem(), - title = feed.title, - canMoveUp = index != 0, - canMoveDown = index != feeds.lastIndex, - onMoveUp = { onClickMoveUp(feed.feed) }, - onMoveDown = { onClickMoveDown(feed.feed) }, - onDelete = { onClickDelete(feed.feed) }, - ) + items( + items = reorderableList, + key = { it.feed.key }, + ) { feed -> + ReorderableItem(reorderableLazyColumnState, feed.feed.key) { + FeedOrderListItem( + modifier = Modifier.animateItem(), + title = feed.title, + onDelete = { onClickDelete(feed.feed) }, + ) + } } } } } } + +internal val FeedSavedSearch.key get() = "feed-$id" diff --git a/app/src/main/java/eu/kanade/presentation/browse/FeedScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/FeedScreen.kt index a55773205b..494a64925d 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/FeedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/FeedScreen.kt @@ -11,7 +11,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.AlertDialog @@ -75,7 +75,7 @@ fun FeedScreen( onClickSavedSearch: (SavedSearch, CatalogueSource) -> Unit, onClickSource: (CatalogueSource) -> Unit, // KMK --> - onLongClickFeed: (FeedItemUI, Boolean, Boolean) -> Unit, + onLongClickFeed: (FeedItemUI) -> Unit, // KMK <-- onClickManga: (Manga) -> Unit, // KMK --> @@ -113,21 +113,17 @@ fun FeedScreen( ) { // KMK --> val feeds = state.items.orEmpty() - itemsIndexed( + items( items = feeds, - key = { _, it -> "feed-${it.feed.id}" }, - ) { index, item -> + key = { it.feed.key }, + ) { item -> // KMK <-- GlobalSearchResultItem( title = item.title, subtitle = item.subtitle, onLongClick = { // KMK --> - onLongClickFeed( - item, - index != 0, - index != feeds.lastIndex, - ) + onLongClickFeed(item) // KMK <-- }, onClick = { diff --git a/app/src/main/java/eu/kanade/presentation/browse/SourceFeedOrderScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/SourceFeedOrderScreen.kt index 637c61450e..58a33e21dd 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/SourceFeedOrderScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/SourceFeedOrderScreen.kt @@ -3,18 +3,25 @@ package eu.kanade.presentation.browse import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.SortByAlpha import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import eu.kanade.presentation.browse.components.FeedOrderListItem import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBarTitle import eu.kanade.tachiyomi.ui.browse.source.feed.SourceFeedState import kotlinx.collections.immutable.persistentListOf +import sh.calvin.reorderable.ReorderableItem +import sh.calvin.reorderable.rememberReorderableLazyListState import tachiyomi.domain.source.model.FeedSavedSearch import tachiyomi.i18n.MR import tachiyomi.i18n.kmk.KMR @@ -30,8 +37,7 @@ import tachiyomi.presentation.core.util.plus fun SourceFeedOrderScreen( state: SourceFeedState, onClickDelete: (FeedSavedSearch) -> Unit, - onClickMoveUp: (FeedSavedSearch) -> Unit, - onClickMoveDown: (FeedSavedSearch) -> Unit, + changeOrder: (FeedSavedSearch, Int) -> Unit, onClickSortAlphabetically: () -> Unit, navigateUp: (() -> Unit)? = null, ) { @@ -68,25 +74,38 @@ fun SourceFeedOrderScreen( val lazyListState = rememberLazyListState() val feeds = state.items .filterIsInstance() + + var reorderableList by remember { mutableStateOf(feeds) } + val reorderableLazyColumnState = rememberReorderableLazyListState(lazyListState) { from, to -> + reorderableList = reorderableList.toMutableList().apply { + changeOrder(reorderableList[from.index].feed, to.index - from.index) + add(to.index, removeAt(from.index)) + } + } + + LaunchedEffect(feeds) { + if (!reorderableLazyColumnState.isAnyItemDragging) { + reorderableList = feeds + } + } + LazyColumn( state = lazyListState, contentPadding = paddingValues + topSmallPaddingValues + PaddingValues(horizontal = MaterialTheme.padding.medium), verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small), ) { - itemsIndexed( - items = feeds, - key = { _, feed -> "feed-${feed.feed.id}" }, - ) { index, feed -> - FeedOrderListItem( - modifier = Modifier.animateItem(), - title = feed.title, - canMoveUp = index != 0, - canMoveDown = index != feeds.lastIndex, - onMoveUp = { onClickMoveUp(feed.feed) }, - onMoveDown = { onClickMoveDown(feed.feed) }, - onDelete = { onClickDelete(feed.feed) }, - ) + items( + items = reorderableList, + key = { it.feed.key }, + ) { feed -> + ReorderableItem(reorderableLazyColumnState, feed.feed.key) { + FeedOrderListItem( + modifier = Modifier.animateItem(), + title = feed.title, + onDelete = { onClickDelete(feed.feed) }, + ) + } } } } diff --git a/app/src/main/java/eu/kanade/presentation/browse/SourceFeedScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/SourceFeedScreen.kt index e1ea7441b6..f9f69722cb 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/SourceFeedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/SourceFeedScreen.kt @@ -2,7 +2,7 @@ package eu.kanade.presentation.browse import androidx.compose.animation.Crossfade import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Public import androidx.compose.material3.TopAppBarScrollBehavior @@ -94,7 +94,7 @@ fun SourceFeedScreen( onClickSavedSearch: (SavedSearch) -> Unit, // KMK --> // onClickDelete: (FeedSavedSearch) -> Unit, - onLongClickFeed: (SourceFeedUI.SourceSavedSearch, Boolean, Boolean) -> Unit, + onLongClickFeed: (SourceFeedUI.SourceSavedSearch) -> Unit, // KMK <-- onClickManga: (Manga) -> Unit, onClickSearch: (String) -> Unit, @@ -200,7 +200,7 @@ fun SourceFeedList( onClickSavedSearch: (SavedSearch) -> Unit, // KMK --> // onClickDelete: (FeedSavedSearch) -> Unit, - onLongClickFeed: (SourceFeedUI.SourceSavedSearch, Boolean, Boolean) -> Unit, + onLongClickFeed: (SourceFeedUI.SourceSavedSearch) -> Unit, // KMK <-- onClickManga: (Manga) -> Unit, // KMK --> @@ -212,10 +212,10 @@ fun SourceFeedList( contentPadding = paddingValues + topSmallPaddingValues, ) { // KMK --> - itemsIndexed( + items( items, - key = { _, it -> "source-feed-${it.id}" }, - ) { index, item -> + key = { "source-feed-${it.id}" }, + ) { item -> // KMK <-- GlobalSearchResultItem( modifier = Modifier.animateItem(), @@ -231,11 +231,7 @@ fun SourceFeedList( onLongClick = if (item is SourceFeedUI.SourceSavedSearch) { { // KMK --> - onLongClickFeed( - item, - index != items.size - items.filterIsInstance().size, - index != items.lastIndex, - ) + onLongClickFeed(item) // KMK <-- } } else { diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/FeedOrderListItem.kt b/app/src/main/java/eu/kanade/presentation/browse/components/FeedOrderListItem.kt index ebba5a22c4..d4e46b0a8d 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/FeedOrderListItem.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/FeedOrderListItem.kt @@ -1,14 +1,11 @@ package eu.kanade.presentation.browse.components import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.ArrowDropDown -import androidx.compose.material.icons.outlined.ArrowDropUp import androidx.compose.material.icons.outlined.Delete -import androidx.compose.material.icons.outlined.Splitscreen +import androidx.compose.material.icons.outlined.DragHandle import androidx.compose.material3.ElevatedCard import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -17,18 +14,14 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview +import sh.calvin.reorderable.ReorderableCollectionItemScope import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource @Composable -fun FeedOrderListItem( +fun ReorderableCollectionItemScope.FeedOrderListItem( title: String, - canMoveUp: Boolean, - canMoveDown: Boolean, - onMoveUp: () -> Unit, - onMoveDown: () -> Unit, onDelete: () -> Unit, modifier: Modifier = Modifier, ) { @@ -39,52 +32,26 @@ fun FeedOrderListItem( modifier = Modifier .fillMaxWidth() .padding( - start = MaterialTheme.padding.medium, - top = MaterialTheme.padding.medium, + start = MaterialTheme.padding.small, end = MaterialTheme.padding.medium, ), verticalAlignment = Alignment.CenterVertically, ) { Icon( - imageVector = Icons.Outlined.Splitscreen, + imageVector = Icons.Outlined.DragHandle, contentDescription = null, + modifier = Modifier + .padding(MaterialTheme.padding.medium) + .draggableHandle(), ) Text( text = title, modifier = Modifier - .padding(start = MaterialTheme.padding.medium), + .weight(1f), ) - } - Row { - IconButton( - onClick = onMoveUp, - enabled = canMoveUp, - ) { - Icon(imageVector = Icons.Outlined.ArrowDropUp, contentDescription = null) - } - IconButton( - onClick = onMoveDown, - enabled = canMoveDown, - ) { - Icon(imageVector = Icons.Outlined.ArrowDropDown, contentDescription = null) - } - Spacer(modifier = Modifier.weight(1f)) IconButton(onClick = onDelete) { Icon(imageVector = Icons.Outlined.Delete, contentDescription = stringResource(MR.strings.action_delete)) } } } } - -@Preview -@Composable -private fun FeedOrderListItemPreview() { - FeedOrderListItem( - title = "Feed 1", - canMoveUp = true, - canMoveDown = true, - onMoveUp = {}, - onMoveDown = {}, - onDelete = {}, - ) -} diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/SourceFeedDialogs.kt b/app/src/main/java/eu/kanade/presentation/browse/components/SourceFeedDialogs.kt index f16b850dc3..8e227b141a 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/SourceFeedDialogs.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/SourceFeedDialogs.kt @@ -11,11 +11,8 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.sizeIn import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.ArrowDownward -import androidx.compose.material.icons.outlined.ArrowUpward import androidx.compose.material.icons.outlined.Delete import androidx.compose.material3.AlertDialog -import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text @@ -99,12 +96,8 @@ private val TitlePadding = PaddingValues(bottom = 16.dp, top = 8.dp) fun FeedActionsDialog( feed: FeedSavedSearch, title: String, - canMoveUp: Boolean, - canMoveDown: Boolean, onDismissRequest: () -> Unit, onClickDelete: (FeedSavedSearch) -> Unit, - onMoveUp: (FeedSavedSearch) -> Unit, - onMoveDown: (FeedSavedSearch) -> Unit, modifier: Modifier = Modifier, ) { val minHeight = LocalPreferenceMinHeight.current @@ -129,32 +122,6 @@ fun FeedActionsDialog( Spacer(Modifier.height(PaddingSize)) - if (canMoveUp) { - TextPreferenceWidget( - title = stringResource(KMR.strings.action_move_up), - icon = Icons.Outlined.ArrowUpward, - onPreferenceClick = { - onDismissRequest() - onMoveUp(feed) - }, - ) - - HorizontalDivider() - } - - if (canMoveDown) { - TextPreferenceWidget( - title = stringResource(KMR.strings.action_move_down), - icon = Icons.Outlined.ArrowDownward, - onPreferenceClick = { - onDismissRequest() - onMoveDown(feed) - }, - ) - - HorizontalDivider() - } - TextPreferenceWidget( title = stringResource(MR.strings.action_delete), icon = Icons.Outlined.Delete, @@ -200,12 +167,8 @@ private fun FeedActionsDialogPreview() { feedOrder = 0, ), title = "Feed 1", - canMoveUp = true, - canMoveDown = true, onDismissRequest = { }, onClickDelete = { }, - onMoveUp = { }, - onMoveDown = { }, ) } diff --git a/app/src/main/java/eu/kanade/presentation/category/components/CategoryListItem.kt b/app/src/main/java/eu/kanade/presentation/category/components/CategoryListItem.kt index c3e9b08532..b4e08785a7 100644 --- a/app/src/main/java/eu/kanade/presentation/category/components/CategoryListItem.kt +++ b/app/src/main/java/eu/kanade/presentation/category/components/CategoryListItem.kt @@ -44,7 +44,6 @@ fun ReorderableCollectionItemScope.CategoryListItem( modifier = Modifier .fillMaxWidth() .clickable { onRename() } - .padding(vertical = MaterialTheme.padding.small) .padding( start = MaterialTheme.padding.small, end = MaterialTheme.padding.medium, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedScreenModel.kt index d77d06f384..db1984e7a6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedScreenModel.kt @@ -23,7 +23,6 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update @@ -159,16 +158,12 @@ open class FeedScreenModel( // KMK --> fun openActionsDialog( feed: FeedItemUI, - canMoveUp: Boolean, - canMoveDown: Boolean, ) { screenModelScope.launchIO { mutableState.update { state -> state.copy( dialog = Dialog.FeedActions( feedItem = feed, - canMoveUp = canMoveUp, - canMoveDown = canMoveDown, ), ) } @@ -219,15 +214,9 @@ open class FeedScreenModel( } // KMK --> - fun moveUp(feed: FeedSavedSearch) { + fun changeOrder(feed: FeedSavedSearch, newOrder: Int) { screenModelScope.launch { - reorderFeed.moveUp(feed) - } - } - - fun moveDown(feed: FeedSavedSearch) { - screenModelScope.launch { - reorderFeed.moveDown(feed) + reorderFeed.changeOrder(feed, newOrder) } } @@ -390,8 +379,6 @@ open class FeedScreenModel( // KMK --> data class FeedActions( val feedItem: FeedItemUI, - val canMoveUp: Boolean, - val canMoveDown: Boolean, ) : Dialog() data object SortAlphabetically : Dialog() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedTab.kt index 8644279180..f3611311a8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/feed/FeedTab.kt @@ -142,8 +142,7 @@ fun feedTab( FeedOrderScreen( state = state, onClickDelete = screenModel::openDeleteDialog, - onClickMoveUp = screenModel::moveUp, - onClickMoveDown = screenModel::moveDown, + changeOrder = screenModel::changeOrder, ) } else { // KMK <-- @@ -249,12 +248,8 @@ fun feedTab( FeedActionsDialog( feed = dialog.feedItem.feed, title = dialog.feedItem.title, - canMoveUp = dialog.canMoveUp, - canMoveDown = dialog.canMoveDown, onDismissRequest = onDismissRequest, onClickDelete = { screenModel.openDeleteDialog(it) }, - onMoveUp = { screenModel.moveUp(it) }, - onMoveDown = { screenModel.moveDown(it) }, ) } is FeedScreenModel.Dialog.SortAlphabetically -> { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedScreen.kt index 8bb9f55237..6002806453 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedScreen.kt @@ -105,8 +105,7 @@ class SourceFeedScreen(val sourceId: Long) : Screen() { SourceFeedOrderScreen( state = state, onClickDelete = screenModel::openDeleteFeed, - onClickMoveUp = screenModel::moveUp, - onClickMoveDown = screenModel::moveDown, + changeOrder = screenModel::changeOrder, onClickSortAlphabetically = { screenModel.showDialog(SourceFeedScreenModel.Dialog.SortAlphabetically) }, @@ -221,12 +220,8 @@ class SourceFeedScreen(val sourceId: Long) : Screen() { FeedActionsDialog( feed = dialog.feedItem.feed, title = dialog.feedItem.title, - canMoveUp = dialog.canMoveUp, - canMoveDown = dialog.canMoveDown, onDismissRequest = screenModel::dismissDialog, onClickDelete = { screenModel.openDeleteFeed(it) }, - onMoveUp = { screenModel.moveUp(it) }, - onMoveDown = { screenModel.moveDown(it) }, ) } is SourceFeedScreenModel.Dialog.SortAlphabetically -> { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedScreenModel.kt index 23d3cda849..5b6447848e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/feed/SourceFeedScreenModel.kt @@ -156,15 +156,9 @@ open class SourceFeedScreenModel( } // KMK --> - fun moveUp(feed: FeedSavedSearch) { + fun changeOrder(feed: FeedSavedSearch, newOrder: Int) { screenModelScope.launch { - reorderFeed.moveUp(feed, false) - } - } - - fun moveDown(feed: FeedSavedSearch) { - screenModelScope.launch { - reorderFeed.moveDown(feed, false) + reorderFeed.changeOrder(feed, newOrder, false) } } @@ -391,16 +385,12 @@ open class SourceFeedScreenModel( // KMK --> fun openActionsDialog( feed: SourceFeedUI.SourceSavedSearch, - canMoveUp: Boolean, - canMoveDown: Boolean, ) { screenModelScope.launchIO { mutableState.update { state -> state.copy( dialog = Dialog.FeedActions( feedItem = feed, - canMoveUp = canMoveUp, - canMoveDown = canMoveDown, ), ) } @@ -432,8 +422,6 @@ open class SourceFeedScreenModel( // KMK --> data class FeedActions( val feedItem: SourceFeedUI.SourceSavedSearch, - val canMoveUp: Boolean, - val canMoveDown: Boolean, ) : Dialog() data object SortAlphabetically : Dialog() diff --git a/domain/src/main/java/tachiyomi/domain/category/interactor/HideCategory.kt b/domain/src/main/java/tachiyomi/domain/category/interactor/HideCategory.kt index d47335a6ae..5ab46a7c82 100644 --- a/domain/src/main/java/tachiyomi/domain/category/interactor/HideCategory.kt +++ b/domain/src/main/java/tachiyomi/domain/category/interactor/HideCategory.kt @@ -27,7 +27,7 @@ class HideCategory( } sealed class Result { - object Success : Result() + data object Success : Result() data class InternalError(val error: Throwable) : Result() } } diff --git a/domain/src/main/java/tachiyomi/domain/source/interactor/ReorderFeed.kt b/domain/src/main/java/tachiyomi/domain/source/interactor/ReorderFeed.kt index a7d533bbee..bd1ae9b32d 100644 --- a/domain/src/main/java/tachiyomi/domain/source/interactor/ReorderFeed.kt +++ b/domain/src/main/java/tachiyomi/domain/source/interactor/ReorderFeed.kt @@ -8,7 +8,6 @@ import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.source.model.FeedSavedSearch import tachiyomi.domain.source.model.FeedSavedSearchUpdate import tachiyomi.domain.source.repository.FeedSavedSearchRepository -import java.util.Collections class ReorderFeed( private val feedSavedSearchRepository: FeedSavedSearchRepository, @@ -16,11 +15,7 @@ class ReorderFeed( private val mutex = Mutex() - suspend fun moveUp(feed: FeedSavedSearch, global: Boolean = true): Result = awaitGlobal(feed, MoveTo.UP, global) - - suspend fun moveDown(feed: FeedSavedSearch, global: Boolean = true): Result = awaitGlobal(feed, MoveTo.DOWN, global) - - private suspend fun awaitGlobal(feed: FeedSavedSearch, moveTo: MoveTo, global: Boolean = true) = withNonCancellableContext { + suspend fun changeOrder(feed: FeedSavedSearch, newOrder: Int, global: Boolean = true) = withNonCancellableContext { mutex.withLock { val feeds = if (global) { feedSavedSearchRepository.getGlobal() @@ -35,13 +30,10 @@ class ReorderFeed( return@withNonCancellableContext Result.Unchanged } - val newPosition = when (moveTo) { - MoveTo.UP -> currentIndex - 1 - MoveTo.DOWN -> currentIndex + 1 - }.toInt() + val newPosition = currentIndex + newOrder try { - Collections.swap(feeds, currentIndex, newPosition) + feeds.add(newPosition, feeds.removeAt(currentIndex)) val updates = feeds.mapIndexed { index, feed -> FeedSavedSearchUpdate( @@ -77,9 +69,4 @@ class ReorderFeed( data object Unchanged : Result data class InternalError(val error: Throwable) : Result } - - private enum class MoveTo { - UP, - DOWN, - } } diff --git a/domain/src/main/java/tachiyomi/domain/source/model/FeedSavedSearchUpdate.kt b/domain/src/main/java/tachiyomi/domain/source/model/FeedSavedSearchUpdate.kt index 95bafd4f36..a16b7ea5af 100644 --- a/domain/src/main/java/tachiyomi/domain/source/model/FeedSavedSearchUpdate.kt +++ b/domain/src/main/java/tachiyomi/domain/source/model/FeedSavedSearchUpdate.kt @@ -12,5 +12,7 @@ data class FeedSavedSearchUpdate( // If the feed is a global (FeedScreen) or source specific feed (SourceFeedScreen) val global: Boolean? = null, + + // Local index in global feed if it's a global one or in source feed if it's a source one val feedOrder: Long? = null, ) From a3528720a6f147233215012dbc338ec57c172f85 Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Mon, 6 Jan 2025 19:44:08 +0700 Subject: [PATCH 19/21] add large app icon to all the notification --- app/src/main/java/eu/kanade/tachiyomi/App.kt | 2 ++ .../java/eu/kanade/tachiyomi/data/download/DownloadJob.kt | 4 +++- .../eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt | 2 ++ .../tachiyomi/extension/util/ExtensionInstallService.kt | 2 ++ .../java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt | 2 ++ app/src/main/java/exh/eh/EHentaiUpdateNotifier.kt | 1 + 6 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 62b1f18873..fd213b440d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -7,6 +7,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.graphics.BitmapFactory import android.os.Build import android.os.Looper import android.webkit.WebView @@ -157,6 +158,7 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor setContentText(stringResource(MR.strings.notification_incognito_text)) setSmallIcon(R.drawable.ic_glasses_24dp) setColor(ContextCompat.getColor(applicationContext, R.color.ic_launcher)) + setLargeIcon(BitmapFactory.decodeResource(applicationContext.resources, R.drawable.komikku)) setOngoing(true) val pendingIntent = PendingIntent.getBroadcast( diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadJob.kt index 8c9d14b591..f7955cea7c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadJob.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.download import android.content.Context import android.content.pm.ServiceInfo +import android.graphics.BitmapFactory import android.os.Build import androidx.core.content.ContextCompat import androidx.lifecycle.asFlow @@ -33,7 +34,7 @@ import uy.kohesive.injekt.api.get * This worker is used to manage the downloader. The system can decide to stop the worker, in * which case the downloader is also stopped. It's also stopped while there's no network available. */ -class DownloadJob(context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) { +class DownloadJob(private val context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) { private val downloadManager: DownloadManager = Injekt.get() private val downloadPreferences: DownloadPreferences = Injekt.get() @@ -43,6 +44,7 @@ class DownloadJob(context: Context, workerParams: WorkerParameters) : CoroutineW setContentTitle(applicationContext.getString(R.string.download_notifier_downloader_title)) setSmallIcon(android.R.drawable.stat_sys_download) setColor(ContextCompat.getColor(applicationContext, R.color.ic_launcher)) + setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.komikku)) }.build() return ForegroundInfo( Notifications.ID_DOWNLOAD_CHAPTER_PROGRESS, diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt index 9882387a05..ad2a3aa57d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.updater import android.app.PendingIntent import android.content.Context import android.content.Intent +import android.graphics.BitmapFactory import android.net.Uri import androidx.core.app.NotificationCompat import androidx.core.content.ContextCompat @@ -23,6 +24,7 @@ internal class AppUpdateNotifier(private val context: Context) { private val notificationBuilder = context.notificationBuilder(Notifications.CHANNEL_APP_UPDATE) { setColor(ContextCompat.getColor(context, R.color.ic_launcher)) + setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.komikku)) } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallService.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallService.kt index 268b430c61..dab5df61e4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallService.kt @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.extension.util import android.app.Service import android.content.Context import android.content.Intent +import android.graphics.BitmapFactory import android.net.Uri import android.os.IBinder import androidx.core.content.ContextCompat @@ -28,6 +29,7 @@ class ExtensionInstallService : Service() { val notification = notificationBuilder(Notifications.CHANNEL_EXTENSIONS_UPDATE) { setSmallIcon(R.drawable.ic_komikku) setColor(ContextCompat.getColor(applicationContext, R.color.ic_launcher)) + setLargeIcon(BitmapFactory.decodeResource(applicationContext.resources, R.drawable.komikku)) setAutoCancel(false) setOngoing(true) setShowWhen(false) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt index cdfff460e7..b4a869493a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.reader import android.content.Context import android.graphics.Bitmap +import android.graphics.BitmapFactory import android.net.Uri import androidx.core.app.NotificationCompat import androidx.core.content.ContextCompat @@ -62,6 +63,7 @@ class SaveImageNotifier(private val context: Context) { fun onError(error: String?) { // Create notification with(notificationBuilder) { + setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.komikku)) setContentTitle(context.stringResource(MR.strings.download_notifier_title_error)) setContentText(error ?: context.stringResource(MR.strings.unknown_error)) setSmallIcon(android.R.drawable.ic_menu_report_image) diff --git a/app/src/main/java/exh/eh/EHentaiUpdateNotifier.kt b/app/src/main/java/exh/eh/EHentaiUpdateNotifier.kt index 4ddeb3a09d..954fa477b1 100644 --- a/app/src/main/java/exh/eh/EHentaiUpdateNotifier.kt +++ b/app/src/main/java/exh/eh/EHentaiUpdateNotifier.kt @@ -98,6 +98,7 @@ class EHentaiUpdateNotifier(private val context: Context) { setContentText(context.stringResource(MR.strings.action_show_errors)) setSmallIcon(R.drawable.ic_komikku) setColor(ContextCompat.getColor(context, R.color.ic_launcher)) + setLargeIcon(notificationBitmap) setContentIntent(NotificationReceiver.openErrorLogPendingActivity(context, uri)) } From cce38e2e838f7832159c93dbe164c471e178404e Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Tue, 7 Jan 2025 13:13:37 +0700 Subject: [PATCH 20/21] set large image for chapter update & save image notification --- .../data/library/LibraryUpdateNotifier.kt | 20 ++++++++++---- .../tachiyomi/ui/reader/SaveImageNotifier.kt | 26 ++++++++++++++++--- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt index c596e8f2cf..987af94f2f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt @@ -12,8 +12,6 @@ import androidx.core.content.ContextCompat import coil3.asDrawable import coil3.imageLoader import coil3.request.ImageRequest -import coil3.request.transformations -import coil3.transform.CircleCropTransformation import eu.kanade.presentation.util.formatChapterNumber import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.core.security.SecurityPreferences @@ -242,13 +240,23 @@ class LibraryUpdateNotifier( val description = getNewChaptersDescription(chapters) setContentText(description) - setStyle(NotificationCompat.BigTextStyle().bigText(description)) setSmallIcon(R.drawable.ic_komikku) setColor(ContextCompat.getColor(context, R.color.ic_launcher)) if (icon != null) { setLargeIcon(icon) + // KMK --> + setStyle( + NotificationCompat.BigPictureStyle() + .bigPicture(icon) + .bigLargeIcon(notificationBitmap) + .setBigContentTitle(manga.title) + .setSummaryText(description), + ) + // KMK <-- + } else { + setStyle(NotificationCompat.BigTextStyle().bigText(description)) } setGroup(Notifications.GROUP_NEW_CHAPTERS) @@ -307,8 +315,10 @@ class LibraryUpdateNotifier( private suspend fun getMangaIcon(manga: Manga): Bitmap? { val request = ImageRequest.Builder(context) .data(manga) - .transformations(CircleCropTransformation()) - .size(NOTIF_ICON_SIZE) + // KMK --> + // .transformations(CircleCropTransformation()) + // .size(NOTIF_ICON_SIZE) + // KMK <-- .build() val drawable = context.imageLoader.execute(request).image?.asDrawable(context.resources) return drawable?.getBitmapOrNull() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt index b4a869493a..166e13286f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt @@ -56,6 +56,15 @@ class SaveImageNotifier(private val context: Context) { context.cancelNotification(notificationId) } + // KMK --> + /** + * Bitmap of the app for notifications. + */ + private val notificationBitmap by lazy { + BitmapFactory.decodeResource(context.resources, R.drawable.komikku) + } + // KMK <-- + /** * Called on error while downloading image. * @param error string containing error information. @@ -63,7 +72,9 @@ class SaveImageNotifier(private val context: Context) { fun onError(error: String?) { // Create notification with(notificationBuilder) { - setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.komikku)) + // KMK --> + setLargeIcon(notificationBitmap) + // KMK <-- setContentTitle(context.stringResource(MR.strings.download_notifier_title_error)) setContentText(error ?: context.stringResource(MR.strings.unknown_error)) setSmallIcon(android.R.drawable.ic_menu_report_image) @@ -75,8 +86,17 @@ class SaveImageNotifier(private val context: Context) { with(notificationBuilder) { setContentTitle(context.stringResource(MR.strings.picture_saved)) setSmallIcon(R.drawable.ic_photo_24dp) - image?.let { setStyle(NotificationCompat.BigPictureStyle().bigPicture(it)) } - setLargeIcon(image) + image?.let { + setStyle( + NotificationCompat.BigPictureStyle() + // KMK --> + .bigLargeIcon(notificationBitmap) + .setBigContentTitle(context.stringResource(MR.strings.picture_saved)) + // KMK <-- + .bigPicture(it), + ) + setLargeIcon(image) + } setAutoCancel(true) // Clear old actions if they exist From 505d18d7e975bb4fa1aa64b311412611747c2e52 Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Fri, 3 Jan 2025 23:46:29 +0700 Subject: [PATCH 21/21] Restore chapter's bookmark & oldest dateUpload from backup --- .../backup/restore/restorers/MangaRestorer.kt | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt index 7775bfce11..d2cd3292b3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt @@ -31,6 +31,7 @@ import uy.kohesive.injekt.api.get import java.time.ZonedDateTime import java.util.Date import kotlin.math.max +import kotlin.math.min class MangaRestorer( private var isSync: Boolean = false, @@ -206,20 +207,28 @@ class MangaRestorer( read = chapter.read, lastPageRead = chapter.lastPageRead, sourceOrder = chapter.sourceOrder, + // KMK --> + dateUpload = min(chapter.dateUpload, dbChapter.dateUpload), + // KMK <-- ) } else { - chapter.copyFrom(dbChapter).let { - when { - dbChapter.read && !it.read -> it.copy(read = true, lastPageRead = dbChapter.lastPageRead) - it.lastPageRead == 0L && dbChapter.lastPageRead != 0L -> it.copy( - lastPageRead = dbChapter.lastPageRead, - ) - else -> it - } - } + chapter.copyFrom(dbChapter) // KMK --> - .copy(id = dbChapter.id) - // KMK <-- + .copy( + id = dbChapter.id, + bookmark = chapter.bookmark || dbChapter.bookmark, + dateUpload = min(chapter.dateUpload, dbChapter.dateUpload), + ) + // KMK <-- + .let { + when { + dbChapter.read && !it.read -> it.copy(read = true, lastPageRead = dbChapter.lastPageRead) + it.lastPageRead == 0L && dbChapter.lastPageRead != 0L -> it.copy( + lastPageRead = dbChapter.lastPageRead, + ) + else -> it + } + } } }