Migrate to Metro DI (#3608)

Assisted-by: Claude:claude-opus-5
This commit is contained in:
AntsyLich
2026-08-17 00:47:48 +06:00
committed by GitHub
parent 1e5b1dc5e7
commit b2015d1ef3
314 changed files with 2416 additions and 2010 deletions
+5
View File
@@ -12,6 +12,7 @@ plugins {
alias(mihonx.plugins.compose) alias(mihonx.plugins.compose)
alias(mihonx.plugins.spotless) alias(mihonx.plugins.spotless)
alias(libs.plugins.metro)
alias(libs.plugins.aboutLibraries) alias(libs.plugins.aboutLibraries)
alias(libs.plugins.androidx.baselineProfile) alias(libs.plugins.androidx.baselineProfile)
alias(libs.plugins.kotlin.serialization) alias(libs.plugins.kotlin.serialization)
@@ -209,6 +210,7 @@ dependencies {
implementation(projects.i18n) implementation(projects.i18n)
implementation(projects.core.archive) implementation(projects.core.archive)
implementation(projects.core.common) implementation(projects.core.common)
implementation(projects.core.metro)
implementation(projects.coreMetadata) implementation(projects.coreMetadata)
implementation(projects.sourceApi) implementation(projects.sourceApi)
implementation(projects.sourceLocal) implementation(projects.sourceLocal)
@@ -283,6 +285,9 @@ dependencies {
// Dependency injection // Dependency injection
implementation(libs.injekt) implementation(libs.injekt)
implementation(libs.metro.runtime)
implementation(libs.metrox.viewmodel)
implementation(libs.metrox.viewmodel.compose)
// Image loading // Image loading
implementation(libs.bundles.coil) implementation(libs.bundles.coil)
@@ -3,11 +3,11 @@ package eu.kanade.core.util
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import tachiyomi.domain.source.service.SourceManager import androidx.compose.ui.platform.LocalContext
import uy.kohesive.injekt.Injekt import mihon.app.di.appGraph
import uy.kohesive.injekt.api.get
@Composable @Composable
fun ifSourcesLoaded(): Boolean { fun ifSourcesLoaded(): Boolean {
return remember { Injekt.get<SourceManager>().isInitialized }.collectAsState().value val context = LocalContext.current
return remember { context.appGraph.sourceManager.isInitialized }.collectAsState().value
} }
@@ -1,209 +0,0 @@
package eu.kanade.domain
import eu.kanade.domain.chapter.interactor.GetAvailableScanlators
import eu.kanade.domain.chapter.interactor.SetReadStatus
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.download.interactor.DeleteDownload
import eu.kanade.domain.extension.interactor.GetExtensionLanguages
import eu.kanade.domain.extension.interactor.GetExtensionSources
import eu.kanade.domain.extension.interactor.GetExtensionsByType
import eu.kanade.domain.extension.interactor.TrustExtension
import eu.kanade.domain.manga.interactor.GetExcludedScanlators
import eu.kanade.domain.manga.interactor.SetExcludedScanlators
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.source.interactor.GetEnabledSources
import eu.kanade.domain.source.interactor.GetIncognitoState
import eu.kanade.domain.source.interactor.GetLanguagesWithSources
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
import eu.kanade.domain.source.interactor.SetMigrateSorting
import eu.kanade.domain.source.interactor.ToggleIncognito
import eu.kanade.domain.source.interactor.ToggleLanguage
import eu.kanade.domain.source.interactor.ToggleSource
import eu.kanade.domain.source.interactor.ToggleSourcePin
import eu.kanade.domain.track.interactor.AddTracks
import eu.kanade.domain.track.interactor.RefreshTracks
import eu.kanade.domain.track.interactor.SyncChapterProgressWithTrack
import eu.kanade.domain.track.interactor.TrackChapter
import mihon.data.extension.repository.ExtensionStoreRepositoryImpl
import mihon.data.extension.service.ExtensionStoreService
import mihon.domain.chapter.interactor.FilterChaptersForDownload
import mihon.domain.extension.interactor.AddExtensionStore
import mihon.domain.extension.interactor.GetExtensionStoreCountAsFlow
import mihon.domain.extension.interactor.GetExtensionStores
import mihon.domain.extension.interactor.RemoveExtensionStore
import mihon.domain.extension.interactor.UpdateExtensionStores
import mihon.domain.extension.repository.ExtensionStoreRepository
import mihon.domain.migration.usecases.MigrateMangaUseCase
import mihon.domain.source.interactor.UpdateMangaFromRemote
import mihon.domain.upcoming.interactor.GetUpcomingManga
import tachiyomi.data.category.CategoryRepositoryImpl
import tachiyomi.data.chapter.ChapterRepositoryImpl
import tachiyomi.data.history.HistoryRepositoryImpl
import tachiyomi.data.manga.MangaRepositoryImpl
import tachiyomi.data.release.ReleaseServiceImpl
import tachiyomi.data.source.SourceRepositoryImpl
import tachiyomi.data.source.StubSourceRepositoryImpl
import tachiyomi.data.track.TrackRepositoryImpl
import tachiyomi.data.updates.UpdatesRepositoryImpl
import tachiyomi.domain.category.interactor.CreateCategoryWithName
import tachiyomi.domain.category.interactor.DeleteCategory
import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.category.interactor.RenameCategory
import tachiyomi.domain.category.interactor.ReorderCategory
import tachiyomi.domain.category.interactor.ResetCategoryFlags
import tachiyomi.domain.category.interactor.SetDisplayMode
import tachiyomi.domain.category.interactor.SetMangaCategories
import tachiyomi.domain.category.interactor.SetSortModeForCategory
import tachiyomi.domain.category.repository.CategoryRepository
import tachiyomi.domain.chapter.interactor.GetBookmarkedChaptersByMangaId
import tachiyomi.domain.chapter.interactor.GetChapter
import tachiyomi.domain.chapter.interactor.GetChapterByUrlAndMangaId
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
import tachiyomi.domain.chapter.interactor.SetMangaDefaultChapterFlags
import tachiyomi.domain.chapter.interactor.ShouldUpdateDbChapter
import tachiyomi.domain.chapter.interactor.UpdateChapter
import tachiyomi.domain.chapter.repository.ChapterRepository
import tachiyomi.domain.history.interactor.GetHistory
import tachiyomi.domain.history.interactor.GetNextChapters
import tachiyomi.domain.history.interactor.GetTotalReadDuration
import tachiyomi.domain.history.interactor.RemoveHistory
import tachiyomi.domain.history.interactor.UpsertHistory
import tachiyomi.domain.history.repository.HistoryRepository
import tachiyomi.domain.manga.interactor.FetchInterval
import tachiyomi.domain.manga.interactor.GetDuplicateLibraryManga
import tachiyomi.domain.manga.interactor.GetFavorites
import tachiyomi.domain.manga.interactor.GetLibraryManga
import tachiyomi.domain.manga.interactor.GetManga
import tachiyomi.domain.manga.interactor.GetMangaByUrlAndSourceId
import tachiyomi.domain.manga.interactor.GetMangaWithChapters
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
import tachiyomi.domain.manga.interactor.ResetViewerFlags
import tachiyomi.domain.manga.interactor.SetMangaChapterFlags
import tachiyomi.domain.manga.interactor.UpdateMangaNotes
import tachiyomi.domain.manga.repository.MangaRepository
import tachiyomi.domain.release.interactor.GetApplicationRelease
import tachiyomi.domain.release.service.ReleaseService
import tachiyomi.domain.source.interactor.GetRemoteManga
import tachiyomi.domain.source.interactor.GetSourcesWithNonLibraryManga
import tachiyomi.domain.source.repository.SourceRepository
import tachiyomi.domain.source.repository.StubSourceRepository
import tachiyomi.domain.track.interactor.DeleteTrack
import tachiyomi.domain.track.interactor.GetTracks
import tachiyomi.domain.track.interactor.GetTracksPerManga
import tachiyomi.domain.track.interactor.InsertTrack
import tachiyomi.domain.track.repository.TrackRepository
import tachiyomi.domain.updates.interactor.GetUpdates
import tachiyomi.domain.updates.repository.UpdatesRepository
import uy.kohesive.injekt.api.InjektModule
import uy.kohesive.injekt.api.InjektRegistrar
import uy.kohesive.injekt.api.addFactory
import uy.kohesive.injekt.api.addSingletonFactory
import uy.kohesive.injekt.api.get
class DomainModule : InjektModule {
override fun InjektRegistrar.registerInjectables() {
addSingletonFactory<CategoryRepository> { CategoryRepositoryImpl(get()) }
addFactory { GetCategories(get()) }
addFactory { ResetCategoryFlags(get(), get()) }
addFactory { SetDisplayMode(get()) }
addFactory { SetSortModeForCategory(get(), get()) }
addFactory { CreateCategoryWithName(get(), get()) }
addFactory { RenameCategory(get()) }
addFactory { ReorderCategory(get()) }
addFactory { DeleteCategory(get(), get(), get()) }
addSingletonFactory<MangaRepository> { MangaRepositoryImpl(get()) }
addFactory { GetDuplicateLibraryManga(get()) }
addFactory { GetFavorites(get()) }
addFactory { GetLibraryManga(get()) }
addFactory { GetMangaWithChapters(get(), get()) }
addFactory { GetMangaByUrlAndSourceId(get()) }
addFactory { GetManga(get()) }
addFactory { GetNextChapters(get(), get(), get()) }
addFactory { GetUpcomingManga(get()) }
addFactory { ResetViewerFlags(get()) }
addFactory { SetMangaChapterFlags(get()) }
addFactory { FetchInterval(get()) }
addFactory { SetMangaDefaultChapterFlags(get(), get(), get()) }
addFactory { SetMangaViewerFlags(get()) }
addFactory { NetworkToLocalManga(get()) }
addFactory { UpdateManga(get(), get()) }
addFactory { UpdateMangaNotes(get()) }
addFactory { SetMangaCategories(get()) }
addFactory { GetExcludedScanlators(get()) }
addFactory { SetExcludedScanlators(get()) }
addFactory {
MigrateMangaUseCase(
get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(),
)
}
addSingletonFactory<ReleaseService> { ReleaseServiceImpl(get(), get()) }
addFactory { GetApplicationRelease(get()) }
addSingletonFactory<TrackRepository> { TrackRepositoryImpl(get()) }
addFactory { TrackChapter(get(), get(), get(), get()) }
addFactory { AddTracks(get(), get(), get(), get()) }
addFactory { RefreshTracks(get(), get(), get(), get()) }
addFactory { DeleteTrack(get()) }
addFactory { GetTracksPerManga(get()) }
addFactory { GetTracks(get()) }
addFactory { InsertTrack(get()) }
addFactory { SyncChapterProgressWithTrack(get(), get(), get()) }
addSingletonFactory<ChapterRepository> { ChapterRepositoryImpl(get()) }
addFactory { GetChapter(get()) }
addFactory { GetChaptersByMangaId(get()) }
addFactory { GetBookmarkedChaptersByMangaId(get()) }
addFactory { GetChapterByUrlAndMangaId(get()) }
addFactory { UpdateChapter(get()) }
addFactory { SetReadStatus(get(), get(), get(), get()) }
addFactory { ShouldUpdateDbChapter() }
addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get(), get(), get()) }
addFactory { GetAvailableScanlators(get()) }
addFactory { FilterChaptersForDownload(get(), get(), get()) }
addSingletonFactory<HistoryRepository> { HistoryRepositoryImpl(get()) }
addFactory { GetHistory(get()) }
addFactory { UpsertHistory(get()) }
addFactory { RemoveHistory(get()) }
addFactory { GetTotalReadDuration(get()) }
addFactory { DeleteDownload(get(), get()) }
addFactory { GetExtensionsByType(get(), get()) }
addFactory { GetExtensionSources(get()) }
addFactory { GetExtensionLanguages(get(), get()) }
addSingletonFactory<UpdatesRepository> { UpdatesRepositoryImpl(get()) }
addFactory { GetUpdates(get()) }
addSingletonFactory<SourceRepository> { SourceRepositoryImpl(get(), get()) }
addSingletonFactory<StubSourceRepository> { StubSourceRepositoryImpl(get()) }
addFactory { GetEnabledSources(get(), get()) }
addFactory { GetLanguagesWithSources(get(), get()) }
addFactory { GetRemoteManga(get()) }
addFactory { GetSourcesWithFavoriteCount(get(), get()) }
addFactory { GetSourcesWithNonLibraryManga(get()) }
addFactory { SetMigrateSorting(get()) }
addFactory { ToggleLanguage(get()) }
addFactory { ToggleSource(get()) }
addFactory { ToggleSourcePin(get()) }
addFactory { TrustExtension(get(), get()) }
addSingletonFactory { ExtensionStoreService(get(), get(), get()) }
addSingletonFactory<ExtensionStoreRepository> { ExtensionStoreRepositoryImpl(get(), get()) }
addFactory { AddExtensionStore(get()) }
addFactory { GetExtensionStoreCountAsFlow(get()) }
addFactory { GetExtensionStores(get()) }
addFactory { RemoveExtensionStore(get()) }
addFactory { UpdateExtensionStores(get()) }
addFactory { ToggleIncognito(get()) }
addFactory { GetIncognitoState(get(), get(), get()) }
addFactory { UpdateMangaFromRemote(get(), get(), get(), get(), get(), get(), get()) }
}
}
@@ -2,10 +2,15 @@ package eu.kanade.domain.base
import android.content.Context import android.content.Context
import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.StringResource
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.SingleIn
import tachiyomi.core.common.preference.Preference import tachiyomi.core.common.preference.Preference
import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
@Inject
@SingleIn(AppScope::class)
class BasePreferences( class BasePreferences(
val context: Context, val context: Context,
preferenceStore: PreferenceStore, preferenceStore: PreferenceStore,
@@ -1,9 +1,11 @@
package eu.kanade.domain.chapter.interactor package eu.kanade.domain.chapter.interactor
import dev.zacsweers.metro.Inject
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import tachiyomi.domain.chapter.repository.ChapterRepository import tachiyomi.domain.chapter.repository.ChapterRepository
@Inject
class GetAvailableScanlators( class GetAvailableScanlators(
private val repository: ChapterRepository, private val repository: ChapterRepository,
) { ) {
@@ -1,5 +1,6 @@
package eu.kanade.domain.chapter.interactor package eu.kanade.domain.chapter.interactor
import dev.zacsweers.metro.Inject
import eu.kanade.domain.download.interactor.DeleteDownload import eu.kanade.domain.download.interactor.DeleteDownload
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.common.util.lang.withNonCancellableContext import tachiyomi.core.common.util.lang.withNonCancellableContext
@@ -11,6 +12,7 @@ import tachiyomi.domain.download.service.DownloadPreferences
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.repository.MangaRepository import tachiyomi.domain.manga.repository.MangaRepository
@Inject
class SetReadStatus( class SetReadStatus(
private val downloadPreferences: DownloadPreferences, private val downloadPreferences: DownloadPreferences,
private val deleteDownload: DeleteDownload, private val deleteDownload: DeleteDownload,
@@ -1,5 +1,6 @@
package eu.kanade.domain.chapter.interactor package eu.kanade.domain.chapter.interactor
import dev.zacsweers.metro.Inject
import eu.kanade.domain.chapter.model.copyFromSChapter import eu.kanade.domain.chapter.model.copyFromSChapter
import eu.kanade.domain.chapter.model.toSChapter import eu.kanade.domain.chapter.model.toSChapter
import eu.kanade.domain.manga.interactor.GetExcludedScanlators import eu.kanade.domain.manga.interactor.GetExcludedScanlators
@@ -29,6 +30,7 @@ import java.lang.Long.max
import java.util.TreeSet import java.util.TreeSet
import kotlin.time.Clock import kotlin.time.Clock
@Inject
class SyncChaptersWithSource( class SyncChaptersWithSource(
private val downloadManager: DownloadManager, private val downloadManager: DownloadManager,
private val downloadProvider: DownloadProvider, private val downloadProvider: DownloadProvider,
@@ -1,11 +1,13 @@
package eu.kanade.domain.download.interactor package eu.kanade.domain.download.interactor
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadManager
import tachiyomi.core.common.util.lang.withNonCancellableContext import tachiyomi.core.common.util.lang.withNonCancellableContext
import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.service.SourceManager
@Inject
class DeleteDownload( class DeleteDownload(
private val sourceManager: SourceManager, private val sourceManager: SourceManager,
private val downloadManager: DownloadManager, private val downloadManager: DownloadManager,
@@ -1,11 +1,13 @@
package eu.kanade.domain.extension.interactor package eu.kanade.domain.extension.interactor
import dev.zacsweers.metro.Inject
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.extension.ExtensionManager import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
@Inject
class GetExtensionLanguages( class GetExtensionLanguages(
private val preferences: SourcePreferences, private val preferences: SourcePreferences,
private val extensionManager: ExtensionManager, private val extensionManager: ExtensionManager,
@@ -1,11 +1,13 @@
package eu.kanade.domain.extension.interactor package eu.kanade.domain.extension.interactor
import dev.zacsweers.metro.Inject
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
@Inject
class GetExtensionSources( class GetExtensionSources(
private val preferences: SourcePreferences, private val preferences: SourcePreferences,
) { ) {
@@ -1,5 +1,6 @@
package eu.kanade.domain.extension.interactor package eu.kanade.domain.extension.interactor
import dev.zacsweers.metro.Inject
import eu.kanade.domain.extension.model.Extensions import eu.kanade.domain.extension.model.Extensions
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.extension.ExtensionManager import eu.kanade.tachiyomi.extension.ExtensionManager
@@ -7,6 +8,7 @@ import eu.kanade.tachiyomi.extension.model.Extension
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
@Inject
class GetExtensionsByType( class GetExtensionsByType(
private val preferences: SourcePreferences, private val preferences: SourcePreferences,
private val extensionManager: ExtensionManager, private val extensionManager: ExtensionManager,
@@ -2,10 +2,12 @@ package eu.kanade.domain.extension.interactor
import android.content.pm.PackageInfo import android.content.pm.PackageInfo
import androidx.core.content.pm.PackageInfoCompat import androidx.core.content.pm.PackageInfoCompat
import dev.zacsweers.metro.Inject
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import mihon.domain.extension.repository.ExtensionStoreRepository import mihon.domain.extension.repository.ExtensionStoreRepository
import tachiyomi.core.common.preference.getAndSet import tachiyomi.core.common.preference.getAndSet
@Inject
class TrustExtension( class TrustExtension(
private val repository: ExtensionStoreRepository, private val repository: ExtensionStoreRepository,
private val preferences: SourcePreferences, private val preferences: SourcePreferences,
@@ -1,11 +1,13 @@
package eu.kanade.domain.manga.interactor package eu.kanade.domain.manga.interactor
import app.cash.sqldelight.async.coroutines.awaitAsList import app.cash.sqldelight.async.coroutines.awaitAsList
import dev.zacsweers.metro.Inject
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import tachiyomi.data.Database import tachiyomi.data.Database
import tachiyomi.data.subscribeToList import tachiyomi.data.subscribeToList
@Inject
class GetExcludedScanlators( class GetExcludedScanlators(
private val database: Database, private val database: Database,
) { ) {
@@ -1,8 +1,10 @@
package eu.kanade.domain.manga.interactor package eu.kanade.domain.manga.interactor
import app.cash.sqldelight.async.coroutines.awaitAsList import app.cash.sqldelight.async.coroutines.awaitAsList
import dev.zacsweers.metro.Inject
import tachiyomi.data.Database import tachiyomi.data.Database
@Inject
class SetExcludedScanlators( class SetExcludedScanlators(
private val database: Database, private val database: Database,
) { ) {
@@ -1,10 +1,12 @@
package eu.kanade.domain.manga.interactor package eu.kanade.domain.manga.interactor
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
import tachiyomi.domain.manga.model.MangaUpdate import tachiyomi.domain.manga.model.MangaUpdate
import tachiyomi.domain.manga.repository.MangaRepository import tachiyomi.domain.manga.repository.MangaRepository
@Inject
class SetMangaViewerFlags( class SetMangaViewerFlags(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,
) { ) {
@@ -1,5 +1,6 @@
package eu.kanade.domain.manga.interactor package eu.kanade.domain.manga.interactor
import dev.zacsweers.metro.Inject
import kotlinx.datetime.LocalDateTime import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime import kotlinx.datetime.toLocalDateTime
@@ -9,6 +10,7 @@ import tachiyomi.domain.manga.model.MangaUpdate
import tachiyomi.domain.manga.repository.MangaRepository import tachiyomi.domain.manga.repository.MangaRepository
import kotlin.time.Clock import kotlin.time.Clock
@Inject
class UpdateManga( class UpdateManga(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,
private val fetchInterval: FetchInterval, private val fetchInterval: FetchInterval,
@@ -1,10 +1,11 @@
package eu.kanade.domain.manga.model package eu.kanade.domain.manga.model
import eu.kanade.domain.base.BasePreferences import android.content.Context
import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
import mihon.app.di.appGraph
import tachiyomi.core.common.preference.TriState import tachiyomi.core.common.preference.TriState
import tachiyomi.core.metadata.comicinfo.ComicInfo import tachiyomi.core.metadata.comicinfo.ComicInfo
import tachiyomi.core.metadata.comicinfo.ComicInfoPublishingStatus import tachiyomi.core.metadata.comicinfo.ComicInfoPublishingStatus
@@ -22,7 +23,7 @@ val Manga.readerOrientation: Long
val Manga.downloadedFilter: TriState val Manga.downloadedFilter: TriState
get() { get() {
if (Injekt.get<BasePreferences>().downloadedOnly.get()) return TriState.ENABLED_IS if (Injekt.get<Context>().appGraph.basePreferences.downloadedOnly.get()) return TriState.ENABLED_IS
return when (downloadedFilterRaw) { return when (downloadedFilterRaw) {
Manga.CHAPTER_SHOW_DOWNLOADED -> TriState.ENABLED_IS Manga.CHAPTER_SHOW_DOWNLOADED -> TriState.ENABLED_IS
Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriState.ENABLED_NOT Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriState.ENABLED_NOT
@@ -1,5 +1,6 @@
package eu.kanade.domain.source.interactor package eu.kanade.domain.source.interactor
import dev.zacsweers.metro.Inject
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
@@ -10,6 +11,7 @@ import tachiyomi.domain.source.model.Source
import tachiyomi.domain.source.repository.SourceRepository import tachiyomi.domain.source.repository.SourceRepository
import tachiyomi.source.local.isLocal import tachiyomi.source.local.isLocal
@Inject
class GetEnabledSources( class GetEnabledSources(
private val repository: SourceRepository, private val repository: SourceRepository,
private val preferences: SourcePreferences, private val preferences: SourcePreferences,
@@ -1,5 +1,6 @@
package eu.kanade.domain.source.interactor package eu.kanade.domain.source.interactor
import dev.zacsweers.metro.Inject
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.extension.ExtensionManager import eu.kanade.tachiyomi.extension.ExtensionManager
@@ -7,6 +8,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
@Inject
class GetIncognitoState( class GetIncognitoState(
private val basePreferences: BasePreferences, private val basePreferences: BasePreferences,
private val sourcePreferences: SourcePreferences, private val sourcePreferences: SourcePreferences,
@@ -1,5 +1,6 @@
package eu.kanade.domain.source.interactor package eu.kanade.domain.source.interactor
import dev.zacsweers.metro.Inject
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@@ -8,6 +9,7 @@ import tachiyomi.domain.source.model.Source
import tachiyomi.domain.source.repository.SourceRepository import tachiyomi.domain.source.repository.SourceRepository
import java.util.SortedMap import java.util.SortedMap
@Inject
class GetLanguagesWithSources( class GetLanguagesWithSources(
private val repository: SourceRepository, private val repository: SourceRepository,
private val preferences: SourcePreferences, private val preferences: SourcePreferences,
@@ -1,5 +1,6 @@
package eu.kanade.domain.source.interactor package eu.kanade.domain.source.interactor
import dev.zacsweers.metro.Inject
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
@@ -9,6 +10,7 @@ import tachiyomi.domain.source.repository.SourceRepository
import tachiyomi.source.local.isLocal import tachiyomi.source.local.isLocal
import java.util.Collections import java.util.Collections
@Inject
class GetSourcesWithFavoriteCount( class GetSourcesWithFavoriteCount(
private val repository: SourceRepository, private val repository: SourceRepository,
private val preferences: SourcePreferences, private val preferences: SourcePreferences,
@@ -1,7 +1,9 @@
package eu.kanade.domain.source.interactor package eu.kanade.domain.source.interactor
import dev.zacsweers.metro.Inject
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
@Inject
class SetMigrateSorting( class SetMigrateSorting(
private val preferences: SourcePreferences, private val preferences: SourcePreferences,
) { ) {
@@ -1,8 +1,10 @@
package eu.kanade.domain.source.interactor package eu.kanade.domain.source.interactor
import dev.zacsweers.metro.Inject
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import tachiyomi.core.common.preference.getAndSet import tachiyomi.core.common.preference.getAndSet
@Inject
class ToggleIncognito( class ToggleIncognito(
private val preferences: SourcePreferences, private val preferences: SourcePreferences,
) { ) {
@@ -1,8 +1,10 @@
package eu.kanade.domain.source.interactor package eu.kanade.domain.source.interactor
import dev.zacsweers.metro.Inject
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import tachiyomi.core.common.preference.getAndSet import tachiyomi.core.common.preference.getAndSet
@Inject
class ToggleLanguage( class ToggleLanguage(
val preferences: SourcePreferences, val preferences: SourcePreferences,
) { ) {
@@ -1,9 +1,11 @@
package eu.kanade.domain.source.interactor package eu.kanade.domain.source.interactor
import dev.zacsweers.metro.Inject
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import tachiyomi.core.common.preference.getAndSet import tachiyomi.core.common.preference.getAndSet
import tachiyomi.domain.source.model.Source import tachiyomi.domain.source.model.Source
@Inject
class ToggleSource( class ToggleSource(
private val preferences: SourcePreferences, private val preferences: SourcePreferences,
) { ) {
@@ -1,9 +1,11 @@
package eu.kanade.domain.source.interactor package eu.kanade.domain.source.interactor
import dev.zacsweers.metro.Inject
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import tachiyomi.core.common.preference.getAndSet import tachiyomi.core.common.preference.getAndSet
import tachiyomi.domain.source.model.Source import tachiyomi.domain.source.model.Source
@Inject
class ToggleSourcePin( class ToggleSourcePin(
private val preferences: SourcePreferences, private val preferences: SourcePreferences,
) { ) {
@@ -1,16 +1,17 @@
package eu.kanade.domain.source.model package eu.kanade.domain.source.model
import android.content.Context
import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.asImageBitmap
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import eu.kanade.tachiyomi.extension.ExtensionManager import mihon.app.di.appGraph
import tachiyomi.domain.source.model.Source import tachiyomi.domain.source.model.Source
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
val Source.icon: ImageBitmap? val Source.icon: ImageBitmap?
get() { get() {
return Injekt.get<ExtensionManager>().getAppIconForSource(id) return Injekt.get<Context>().appGraph.extensionManager.getAppIconForSource(id)
?.toBitmap() ?.toBitmap()
?.asImageBitmap() ?.asImageBitmap()
} }
@@ -1,5 +1,8 @@
package eu.kanade.domain.source.service package eu.kanade.domain.source.service
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.SingleIn
import eu.kanade.domain.source.interactor.SetMigrateSorting import eu.kanade.domain.source.interactor.SetMigrateSorting
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import mihon.domain.migration.models.MigrationFlag import mihon.domain.migration.models.MigrationFlag
@@ -9,6 +12,8 @@ import tachiyomi.core.common.preference.getEnum
import tachiyomi.core.common.preference.getLongArray import tachiyomi.core.common.preference.getLongArray
import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibraryDisplayMode
@Inject
@SingleIn(AppScope::class)
class SourcePreferences( class SourcePreferences(
preferenceStore: PreferenceStore, preferenceStore: PreferenceStore,
) { ) {
@@ -1,5 +1,6 @@
package eu.kanade.domain.track.interactor package eu.kanade.domain.track.interactor
import dev.zacsweers.metro.Inject
import eu.kanade.domain.track.model.toDbTrack import eu.kanade.domain.track.model.toDbTrack
import eu.kanade.domain.track.model.toDomainTrack import eu.kanade.domain.track.model.toDomainTrack
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
@@ -17,14 +18,14 @@ import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
import tachiyomi.domain.history.interactor.GetHistory import tachiyomi.domain.history.interactor.GetHistory
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.track.interactor.InsertTrack import tachiyomi.domain.track.interactor.InsertTrack
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@Inject
class AddTracks( class AddTracks(
private val insertTrack: InsertTrack, private val insertTrack: InsertTrack,
private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack, private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack,
private val getChaptersByMangaId: GetChaptersByMangaId, private val getChaptersByMangaId: GetChaptersByMangaId,
private val trackerManager: TrackerManager, private val trackerManager: TrackerManager,
private val getHistory: GetHistory,
) { ) {
// TODO: update all trackers based on common data // TODO: update all trackers based on common data
@@ -55,7 +56,7 @@ class AddTracks(
} }
if (track.startDate <= 0) { if (track.startDate <= 0) {
val firstReadChapterDate = Injekt.get<GetHistory>().await(mangaId) val firstReadChapterDate = getHistory.await(mangaId)
.sortedBy { it.readAt } .sortedBy { it.readAt }
.firstOrNull() .firstOrNull()
?.readAt ?.readAt
@@ -1,5 +1,6 @@
package eu.kanade.domain.track.interactor package eu.kanade.domain.track.interactor
import dev.zacsweers.metro.Inject
import eu.kanade.domain.track.model.toDbTrack import eu.kanade.domain.track.model.toDbTrack
import eu.kanade.domain.track.model.toDomainTrack import eu.kanade.domain.track.model.toDomainTrack
import eu.kanade.tachiyomi.data.track.Tracker import eu.kanade.tachiyomi.data.track.Tracker
@@ -10,6 +11,7 @@ import kotlinx.coroutines.supervisorScope
import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.domain.track.interactor.GetTracks
import tachiyomi.domain.track.interactor.InsertTrack import tachiyomi.domain.track.interactor.InsertTrack
@Inject
class RefreshTracks( class RefreshTracks(
private val getTracks: GetTracks, private val getTracks: GetTracks,
private val trackerManager: TrackerManager, private val trackerManager: TrackerManager,
@@ -1,5 +1,6 @@
package eu.kanade.domain.track.interactor package eu.kanade.domain.track.interactor
import dev.zacsweers.metro.Inject
import eu.kanade.domain.track.model.toDbTrack import eu.kanade.domain.track.model.toDbTrack
import eu.kanade.tachiyomi.data.track.EnhancedTracker import eu.kanade.tachiyomi.data.track.EnhancedTracker
import eu.kanade.tachiyomi.data.track.Tracker import eu.kanade.tachiyomi.data.track.Tracker
@@ -12,6 +13,7 @@ import tachiyomi.domain.track.interactor.InsertTrack
import tachiyomi.domain.track.model.Track import tachiyomi.domain.track.model.Track
import kotlin.math.max import kotlin.math.max
@Inject
class SyncChapterProgressWithTrack( class SyncChapterProgressWithTrack(
private val updateChapter: UpdateChapter, private val updateChapter: UpdateChapter,
private val insertTrack: InsertTrack, private val insertTrack: InsertTrack,
@@ -1,6 +1,7 @@
package eu.kanade.domain.track.interactor package eu.kanade.domain.track.interactor
import android.content.Context import android.content.Context
import dev.zacsweers.metro.Inject
import eu.kanade.domain.track.model.toDbTrack import eu.kanade.domain.track.model.toDbTrack
import eu.kanade.domain.track.model.toDomainTrack import eu.kanade.domain.track.model.toDomainTrack
import eu.kanade.domain.track.service.DelayedTrackingUpdateJob import eu.kanade.domain.track.service.DelayedTrackingUpdateJob
@@ -14,6 +15,7 @@ import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.domain.track.interactor.GetTracks
import tachiyomi.domain.track.interactor.InsertTrack import tachiyomi.domain.track.interactor.InsertTrack
@Inject
class TrackChapter( class TrackChapter(
private val getTracks: GetTracks, private val getTracks: GetTracks,
private val trackerManager: TrackerManager, private val trackerManager: TrackerManager,
@@ -8,30 +8,36 @@ import androidx.work.ExistingWorkPolicy
import androidx.work.NetworkType import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import dev.zacsweers.metro.Inject
import eu.kanade.domain.track.interactor.TrackChapter import eu.kanade.domain.track.interactor.TrackChapter
import eu.kanade.domain.track.store.DelayedTrackingStore import eu.kanade.domain.track.store.DelayedTrackingStore
import eu.kanade.tachiyomi.util.system.workManager import eu.kanade.tachiyomi.util.system.workManager
import logcat.LogPriority import logcat.LogPriority
import mihon.app.di.AppGraph
import mihon.core.metro.metroGraph
import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.lang.withIOContext
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.domain.track.interactor.GetTracks
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class DelayedTrackingUpdateJob(private val context: Context, workerParams: WorkerParameters) : class DelayedTrackingUpdateJob(private val context: Context, workerParams: WorkerParameters) :
CoroutineWorker(context, workerParams) { CoroutineWorker(context, workerParams) {
private val graph: AppGraph = context.metroGraph()
@Inject lateinit var getTracks: GetTracks
@Inject lateinit var trackChapter: TrackChapter
@Inject lateinit var delayedTrackingStore: DelayedTrackingStore
override suspend fun doWork(): Result { override suspend fun doWork(): Result {
graph.inject(this)
if (runAttemptCount > 3) { if (runAttemptCount > 3) {
return Result.failure() return Result.failure()
} }
val getTracks = Injekt.get<GetTracks>()
val trackChapter = Injekt.get<TrackChapter>()
val delayedTrackingStore = Injekt.get<DelayedTrackingStore>()
withIOContext { withIOContext {
delayedTrackingStore.getItems() delayedTrackingStore.getItems()
.mapNotNull { .mapNotNull {
@@ -1,5 +1,8 @@
package eu.kanade.domain.track.service package eu.kanade.domain.track.service
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.SingleIn
import eu.kanade.domain.track.model.AutoTrackState import eu.kanade.domain.track.model.AutoTrackState
import eu.kanade.tachiyomi.data.track.Tracker import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.anilist.Anilist import eu.kanade.tachiyomi.data.track.anilist.Anilist
@@ -8,6 +11,8 @@ import tachiyomi.core.common.preference.Preference
import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.core.common.preference.getEnum import tachiyomi.core.common.preference.getEnum
@Inject
@SingleIn(AppScope::class)
class TrackPreferences( class TrackPreferences(
private val preferenceStore: PreferenceStore, private val preferenceStore: PreferenceStore,
) { ) {
@@ -2,9 +2,14 @@ package eu.kanade.domain.track.store
import android.content.Context import android.content.Context
import androidx.core.content.edit import androidx.core.content.edit
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.SingleIn
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
@Inject
@SingleIn(AppScope::class)
class DelayedTrackingStore(context: Context) { class DelayedTrackingStore(context: Context) {
/** /**
@@ -1,5 +1,8 @@
package eu.kanade.domain.ui package eu.kanade.domain.ui
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.SingleIn
import eu.kanade.domain.ui.model.AppTheme import eu.kanade.domain.ui.model.AppTheme
import eu.kanade.domain.ui.model.TabletUiMode import eu.kanade.domain.ui.model.TabletUiMode
import eu.kanade.domain.ui.model.ThemeMode import eu.kanade.domain.ui.model.ThemeMode
@@ -12,6 +15,8 @@ import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle import java.time.format.FormatStyle
import java.util.Locale import java.util.Locale
@Inject
@SingleIn(AppScope::class)
class UiPreferences( class UiPreferences(
preferenceStore: PreferenceStore, preferenceStore: PreferenceStore,
) { ) {
@@ -8,10 +8,9 @@ import eu.kanade.tachiyomi.util.lang.toRelativeString
import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate
import kotlinx.datetime.TimeZone import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime import kotlinx.datetime.toLocalDateTime
import mihon.app.di.appGraph
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.time.Instant import kotlin.time.Instant
@Composable @Composable
@@ -32,7 +31,7 @@ fun relativeDateText(
): String { ): String {
val context = LocalContext.current val context = LocalContext.current
val preferences = remember { Injekt.get<UiPreferences>() } val preferences = remember { context.appGraph.uiPreferences }
val relativeTime = remember { preferences.relativeTime.get() } val relativeTime = remember { preferences.relativeTime.get() }
val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat.get()) } val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat.get()) }
@@ -9,14 +9,15 @@ import androidx.compose.material.icons.outlined.BugReport
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.tooling.preview.PreviewLightDark
import eu.kanade.presentation.theme.TachiyomiPreviewTheme import eu.kanade.presentation.theme.TachiyomiPreviewTheme
import eu.kanade.tachiyomi.util.CrashLogUtil
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import mihon.app.di.appGraph
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
@@ -29,6 +30,7 @@ fun CrashScreen(
) { ) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val context = LocalContext.current val context = LocalContext.current
val crashLogUtil = remember { context.appGraph.crashLogUtil }
InfoScreen( InfoScreen(
icon = Icons.Outlined.BugReport, icon = Icons.Outlined.BugReport,
@@ -37,7 +39,7 @@ fun CrashScreen(
acceptText = stringResource(MR.strings.pref_dump_crash_logs), acceptText = stringResource(MR.strings.pref_dump_crash_logs),
onAcceptClick = { onAcceptClick = {
scope.launch { scope.launch {
CrashLogUtil(context).dumpLogs(exception) crashLogUtil.dumpLogs(exception)
} }
}, },
rejectText = stringResource(MR.strings.crash_screen_restart_application), rejectText = stringResource(MR.strings.crash_screen_restart_application),
@@ -26,11 +26,12 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.manga.model.downloadedFilter import eu.kanade.domain.manga.model.downloadedFilter
import eu.kanade.presentation.components.TabbedDialog import eu.kanade.presentation.components.TabbedDialog
import eu.kanade.presentation.components.TabbedDialogPaddings import eu.kanade.presentation.components.TabbedDialogPaddings
import mihon.app.di.appGraph
import tachiyomi.core.common.preference.TriState import tachiyomi.core.common.preference.TriState
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
@@ -40,8 +41,6 @@ import tachiyomi.presentation.core.components.SortItem
import tachiyomi.presentation.core.components.TriStateItem import tachiyomi.presentation.core.components.TriStateItem
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.theme.active import tachiyomi.presentation.core.theme.active
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@Composable @Composable
fun ChapterSettingsDialog( fun ChapterSettingsDialog(
@@ -65,7 +64,8 @@ fun ChapterSettingsDialog(
) )
} }
val downloadedOnly = remember { Injekt.get<BasePreferences>().downloadedOnly.get() } val context = LocalContext.current
val downloadedOnly = remember { context.appGraph.basePreferences.downloadedOnly.get() }
TabbedDialog( TabbedDialog(
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
@@ -63,10 +63,10 @@ import eu.kanade.presentation.more.settings.LocalPreferenceMinHeight
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import mihon.app.di.appGraph
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.MangaWithChapterCount import tachiyomi.domain.manga.model.MangaWithChapterCount
import tachiyomi.domain.source.model.StubSource import tachiyomi.domain.source.model.StubSource
import tachiyomi.domain.source.service.SourceManager
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.Badge import tachiyomi.presentation.core.components.Badge
import tachiyomi.presentation.core.components.BadgeGroup import tachiyomi.presentation.core.components.BadgeGroup
@@ -74,8 +74,6 @@ import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.pluralStringResource import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.secondaryItemAlpha import tachiyomi.presentation.core.util.secondaryItemAlpha
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@Composable @Composable
fun DuplicateMangaDialog( fun DuplicateMangaDialog(
@@ -86,7 +84,8 @@ fun DuplicateMangaDialog(
onMigrate: (manga: Manga) -> Unit, onMigrate: (manga: Manga) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val sourceManager = remember { Injekt.get<SourceManager>() } val context = LocalContext.current
val sourceManager = remember { context.appGraph.sourceManager }
val minHeight = LocalPreferenceMinHeight.current val minHeight = LocalPreferenceMinHeight.current
val horizontalPadding = PaddingValues(horizontal = TabbedDialogPaddings.Horizontal) val horizontalPadding = PaddingValues(horizontal = TabbedDialogPaddings.Horizontal)
val horizontalPaddingModifier = Modifier.padding(horizontalPadding) val horizontalPaddingModifier = Modifier.padding(horizontalPadding)
@@ -30,6 +30,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.DpOffset
@@ -48,7 +49,6 @@ import coil3.imageLoader
import coil3.request.CachePolicy import coil3.request.CachePolicy
import coil3.request.ImageRequest import coil3.request.ImageRequest
import coil3.size.Size import coil3.size.Size
import eu.kanade.domain.base.BasePreferences
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.components.AppBarActions
import eu.kanade.presentation.components.DropdownMenu import eu.kanade.presentation.components.DropdownMenu
@@ -57,13 +57,12 @@ import eu.kanade.tachiyomi.data.coil.ImageDecoder
import eu.kanade.tachiyomi.data.coil.newDecoder import eu.kanade.tachiyomi.data.coil.newDecoder
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderPageImageView import eu.kanade.tachiyomi.ui.reader.viewer.ReaderPageImageView
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import mihon.app.di.appGraph
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.clickableNoIndication import tachiyomi.presentation.core.util.clickableNoIndication
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@Composable @Composable
fun MangaCoverDialog( fun MangaCoverDialog(
@@ -75,7 +74,7 @@ fun MangaCoverDialog(
onEditClick: ((EditCoverAction) -> Unit)?, onEditClick: ((EditCoverAction) -> Unit)?,
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
) { ) {
val useNewRenderer = Injekt.get<BasePreferences>().highQualityRenderer.get() val useNewRenderer = LocalContext.current.appGraph.basePreferences.highQualityRenderer.get()
val view = LocalView.current val view = LocalView.current
Dialog( Dialog(
@@ -86,13 +86,13 @@ import coil3.request.crossfade
import com.mikepenz.markdown.model.markdownAnnotator import com.mikepenz.markdown.model.markdownAnnotator
import com.mikepenz.markdown.model.markdownAnnotatorConfig import com.mikepenz.markdown.model.markdownAnnotatorConfig
import com.mikepenz.markdown.utils.getUnescapedTextInNode import com.mikepenz.markdown.utils.getUnescapedTextInNode
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.presentation.components.DropdownMenu import eu.kanade.presentation.components.DropdownMenu
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import kotlinx.datetime.TimeZone import kotlinx.datetime.TimeZone
import kotlinx.datetime.daysUntil import kotlinx.datetime.daysUntil
import mihon.app.di.appGraph
import org.intellij.markdown.MarkdownElementTypes import org.intellij.markdown.MarkdownElementTypes
import org.intellij.markdown.MarkdownTokenTypes import org.intellij.markdown.MarkdownTokenTypes
import org.intellij.markdown.ast.findChildOfType import org.intellij.markdown.ast.findChildOfType
@@ -105,8 +105,6 @@ import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.clickableNoIndication import tachiyomi.presentation.core.util.clickableNoIndication
import tachiyomi.presentation.core.util.secondaryItemAlpha import tachiyomi.presentation.core.util.secondaryItemAlpha
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlin.time.Clock import kotlin.time.Clock
import kotlin.time.Instant import kotlin.time.Instant
@@ -619,7 +617,8 @@ private fun MangaSummary(
onEditNotesClicked: () -> Unit, onEditNotesClicked: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val preferences = remember { Injekt.get<UiPreferences>() } val context = LocalContext.current
val preferences = remember { context.appGraph.uiPreferences }
val loadImages = remember { preferences.imagesInDescription.get() } val loadImages = remember { preferences.imagesInDescription.get() }
val animProgress by animateFloatAsState( val animProgress by animateFloatAsState(
targetValue = if (expanded) 1f else 0f, targetValue = if (expanded) 1f else 0f,
@@ -25,6 +25,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@@ -36,19 +37,16 @@ import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.LocalLifecycleOwner
import eu.kanade.presentation.util.rememberRequestPackageInstallsPermissionState import eu.kanade.presentation.util.rememberRequestPackageInstallsPermissionState
import eu.kanade.tachiyomi.core.security.PrivacyPreferences
import eu.kanade.tachiyomi.util.system.launchRequestPackageInstallsPermission import eu.kanade.tachiyomi.util.system.launchRequestPackageInstallsPermission
import eu.kanade.tachiyomi.util.system.telemetryIncluded import eu.kanade.tachiyomi.util.system.telemetryIncluded
import mihon.app.di.appGraph
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
import tachiyomi.presentation.core.util.secondaryItemAlpha import tachiyomi.presentation.core.util.secondaryItemAlpha
import uy.kohesive.injekt.injectLazy
internal class PermissionStep : OnboardingStep { internal class PermissionStep : OnboardingStep {
private val privacyPreferences: PrivacyPreferences by injectLazy()
private var notificationGranted by mutableStateOf(false) private var notificationGranted by mutableStateOf(false)
private var batteryGranted by mutableStateOf(false) private var batteryGranted by mutableStateOf(false)
@@ -57,6 +55,7 @@ internal class PermissionStep : OnboardingStep {
@Composable @Composable
override fun Content() { override fun Content() {
val context = LocalContext.current val context = LocalContext.current
val privacyPreferences = remember { context.appGraph.privacyPreferences }
val lifecycleOwner = LocalLifecycleOwner.current val lifecycleOwner = LocalLifecycleOwner.current
val installGranted = rememberRequestPackageInstallsPermissionState() val installGranted = rememberRequestPackageInstallsPermissionState()
@@ -12,6 +12,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@@ -20,18 +21,14 @@ import androidx.compose.ui.unit.dp
import eu.kanade.presentation.more.settings.screen.SettingsDataScreen import eu.kanade.presentation.more.settings.screen.SettingsDataScreen
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import tachiyomi.domain.storage.service.StoragePreferences import mihon.app.di.appGraph
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.Button import tachiyomi.presentation.core.components.material.Button
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
internal class StorageStep : OnboardingStep { internal class StorageStep : OnboardingStep {
private val storagePref = Injekt.get<StoragePreferences>().baseStorageDirectory
private var _isComplete by mutableStateOf(false) private var _isComplete by mutableStateOf(false)
override val isComplete: Boolean override val isComplete: Boolean
@@ -42,6 +39,7 @@ internal class StorageStep : OnboardingStep {
val context = LocalContext.current val context = LocalContext.current
val handler = LocalUriHandler.current val handler = LocalUriHandler.current
val storagePref = remember { context.appGraph.storagePreferences.baseStorageDirectory }
val pickStorageLocation = SettingsDataScreen.storageLocationPicker(storagePref) val pickStorageLocation = SettingsDataScreen.storageLocationPicker(storagePref)
Column( Column(
@@ -3,22 +3,22 @@ package eu.kanade.presentation.more.onboarding
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import eu.kanade.domain.ui.UiPreferences import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode
import eu.kanade.presentation.more.settings.widget.AppThemeModePreferenceWidget import eu.kanade.presentation.more.settings.widget.AppThemeModePreferenceWidget
import eu.kanade.presentation.more.settings.widget.AppThemePreferenceWidget import eu.kanade.presentation.more.settings.widget.AppThemePreferenceWidget
import mihon.app.di.appGraph
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
internal class ThemeStep : OnboardingStep { internal class ThemeStep : OnboardingStep {
override val isComplete: Boolean = true override val isComplete: Boolean = true
private val uiPreferences: UiPreferences = Injekt.get()
@Composable @Composable
override fun Content() { override fun Content() {
val context = LocalContext.current
val uiPreferences = remember { context.appGraph.uiPreferences }
val themeModePref = uiPreferences.themeMode val themeModePref = uiPreferences.themeMode
val themeMode by themeModePref.collectAsState() val themeMode by themeModePref.collectAsState()
@@ -25,13 +25,10 @@ import androidx.core.net.toUri
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.extension.interactor.TrustExtension
import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.more.settings.screen.advanced.ClearDatabaseScreen import eu.kanade.presentation.more.settings.screen.advanced.ClearDatabaseScreen
import eu.kanade.presentation.more.settings.screen.debug.DebugInfoScreen import eu.kanade.presentation.more.settings.screen.debug.DebugInfoScreen
import eu.kanade.tachiyomi.data.download.DownloadCache
import eu.kanade.tachiyomi.data.library.MetadataUpdateJob import eu.kanade.tachiyomi.data.library.MetadataUpdateJob
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.NetworkPreferences import eu.kanade.tachiyomi.network.NetworkPreferences
import eu.kanade.tachiyomi.network.PREF_DOH_360 import eu.kanade.tachiyomi.network.PREF_DOH_360
import eu.kanade.tachiyomi.network.PREF_DOH_ADGUARD import eu.kanade.tachiyomi.network.PREF_DOH_ADGUARD
@@ -46,26 +43,24 @@ import eu.kanade.tachiyomi.network.PREF_DOH_QUAD101
import eu.kanade.tachiyomi.network.PREF_DOH_QUAD9 import eu.kanade.tachiyomi.network.PREF_DOH_QUAD9
import eu.kanade.tachiyomi.network.PREF_DOH_SHECAN import eu.kanade.tachiyomi.network.PREF_DOH_SHECAN
import eu.kanade.tachiyomi.ui.more.OnboardingScreen import eu.kanade.tachiyomi.ui.more.OnboardingScreen
import eu.kanade.tachiyomi.util.CrashLogUtil
import eu.kanade.tachiyomi.util.system.isReleaseBuildType import eu.kanade.tachiyomi.util.system.isReleaseBuildType
import eu.kanade.tachiyomi.util.system.isShizukuInstalled import eu.kanade.tachiyomi.util.system.isShizukuInstalled
import eu.kanade.tachiyomi.util.system.powerManager import eu.kanade.tachiyomi.util.system.powerManager
import eu.kanade.tachiyomi.util.system.setDefaultSettings import eu.kanade.tachiyomi.util.system.setDefaultSettings
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.system.workManager
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import logcat.LogPriority import logcat.LogPriority
import mihon.app.di.appGraph
import okhttp3.Headers import okhttp3.Headers
import tachiyomi.core.common.util.lang.launchNonCancellable import tachiyomi.core.common.util.lang.launchNonCancellable
import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.lang.withUIContext
import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.ImageUtil
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.manga.interactor.ResetViewerFlags
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File import java.io.File
object SettingsAdvancedScreen : SearchableSettings { object SettingsAdvancedScreen : SearchableSettings {
@@ -80,9 +75,11 @@ object SettingsAdvancedScreen : SearchableSettings {
val context = LocalContext.current val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
val basePreferences = remember { Injekt.get<BasePreferences>() } val graph = remember { context.appGraph }
val networkPreferences = remember { Injekt.get<NetworkPreferences>() } val basePreferences = remember { graph.basePreferences }
val libraryPreferences = remember { Injekt.get<LibraryPreferences>() } val networkPreferences = remember { graph.networkPreferences }
val libraryPreferences = remember { graph.libraryPreferences }
val crashLogUtil = remember { graph.crashLogUtil }
return listOf( return listOf(
Preference.PreferenceItem.TextPreference( Preference.PreferenceItem.TextPreference(
@@ -90,7 +87,7 @@ object SettingsAdvancedScreen : SearchableSettings {
subtitle = stringResource(MR.strings.pref_dump_crash_logs_summary), subtitle = stringResource(MR.strings.pref_dump_crash_logs_summary),
onClick = { onClick = {
scope.launch { scope.launch {
CrashLogUtil(context).dumpLogs() crashLogUtil.dumpLogs()
} }
}, },
), ),
@@ -180,7 +177,7 @@ object SettingsAdvancedScreen : SearchableSettings {
title = stringResource(MR.strings.pref_invalidate_download_cache), title = stringResource(MR.strings.pref_invalidate_download_cache),
subtitle = stringResource(MR.strings.pref_invalidate_download_cache_summary), subtitle = stringResource(MR.strings.pref_invalidate_download_cache_summary),
onClick = { onClick = {
Injekt.get<DownloadCache>().invalidateCache() context.appGraph.downloadCache.invalidateCache()
context.toast(MR.strings.download_cache_invalidated) context.toast(MR.strings.download_cache_invalidated)
}, },
), ),
@@ -198,7 +195,7 @@ object SettingsAdvancedScreen : SearchableSettings {
networkPreferences: NetworkPreferences, networkPreferences: NetworkPreferences,
): Preference.PreferenceGroup { ): Preference.PreferenceGroup {
val context = LocalContext.current val context = LocalContext.current
val networkHelper = remember { Injekt.get<NetworkHelper>() } val networkHelper = remember { context.appGraph.networkHelper }
val userAgentPref = networkPreferences.defaultUserAgent val userAgentPref = networkPreferences.defaultUserAgent
val userAgent by userAgentPref.collectAsState() val userAgent by userAgentPref.collectAsState()
@@ -295,14 +292,14 @@ object SettingsAdvancedScreen : SearchableSettings {
preferenceItems = listOf( preferenceItems = listOf(
Preference.PreferenceItem.TextPreference( Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.pref_refresh_library_covers), title = stringResource(MR.strings.pref_refresh_library_covers),
onClick = { MetadataUpdateJob.startNow(context) }, onClick = { MetadataUpdateJob.startNow(context.workManager) },
), ),
Preference.PreferenceItem.TextPreference( Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.pref_reset_viewer_flags), title = stringResource(MR.strings.pref_reset_viewer_flags),
subtitle = stringResource(MR.strings.pref_reset_viewer_flags_summary), subtitle = stringResource(MR.strings.pref_reset_viewer_flags_summary),
onClick = { onClick = {
scope.launchNonCancellable { scope.launchNonCancellable {
val success = Injekt.get<ResetViewerFlags>().await() val success = context.appGraph.resetViewerFlags.await()
withUIContext { withUIContext {
val message = if (success) { val message = if (success) {
MR.strings.pref_reset_viewer_flags_success MR.strings.pref_reset_viewer_flags_success
@@ -352,7 +349,7 @@ object SettingsAdvancedScreen : SearchableSettings {
val uriHandler = LocalUriHandler.current val uriHandler = LocalUriHandler.current
val extensionInstallerPref = basePreferences.extensionInstaller val extensionInstallerPref = basePreferences.extensionInstaller
var shizukuMissing by rememberSaveable { mutableStateOf(false) } var shizukuMissing by rememberSaveable { mutableStateOf(false) }
val trustExtension = remember { Injekt.get<TrustExtension>() } val trustExtension = remember { context.appGraph.trustExtension }
if (shizukuMissing) { if (shizukuMissing) {
val dismiss = { shizukuMissing = false } val dismiss = { shizukuMissing = false }
@@ -22,11 +22,10 @@ import eu.kanade.tachiyomi.util.system.toast
import kotlinx.datetime.TimeZone import kotlinx.datetime.TimeZone
import kotlinx.datetime.toJavaLocalDateTime import kotlinx.datetime.toJavaLocalDateTime
import kotlinx.datetime.toLocalDateTime import kotlinx.datetime.toLocalDateTime
import mihon.app.di.appGraph
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.time.Clock import kotlin.time.Clock
object SettingsAppearanceScreen : SearchableSettings { object SettingsAppearanceScreen : SearchableSettings {
@@ -37,7 +36,8 @@ object SettingsAppearanceScreen : SearchableSettings {
@Composable @Composable
override fun getPreferences(): List<Preference> { override fun getPreferences(): List<Preference> {
val uiPreferences = remember { Injekt.get<UiPreferences>() } val context = LocalContext.current
val uiPreferences = remember { context.appGraph.uiPreferences }
return listOf( return listOf(
getThemeGroup(uiPreferences = uiPreferences), getThemeGroup(uiPreferences = uiPreferences),
@@ -9,17 +9,14 @@ import androidx.compose.ui.platform.LocalContext
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.more.settings.screen.browse.ExtensionStoresScreen import eu.kanade.presentation.more.settings.screen.browse.ExtensionStoresScreen
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate
import mihon.domain.extension.interactor.GetExtensionStoreCountAsFlow import mihon.app.di.appGraph
import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.i18n.stringResource
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.pluralStringResource import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
object SettingsBrowseScreen : SearchableSettings { object SettingsBrowseScreen : SearchableSettings {
@@ -32,8 +29,8 @@ object SettingsBrowseScreen : SearchableSettings {
val context = LocalContext.current val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
val sourcePreferences = remember { Injekt.get<SourcePreferences>() } val sourcePreferences = remember { context.appGraph.sourcePreferences }
val getExtensionStoreCountAsFlow = remember { Injekt.get<GetExtensionStoreCountAsFlow>() } val getExtensionStoreCountAsFlow = remember { context.appGraph.getExtensionStoreCountAsFlow }
val reposCount by getExtensionStoreCountAsFlow().collectAsState(0) val reposCount by getExtensionStoreCountAsFlow().collectAsState(0)
@@ -51,30 +51,27 @@ import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding
import eu.kanade.presentation.util.relativeTimeSpanString import eu.kanade.presentation.util.relativeTimeSpanString
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
import eu.kanade.tachiyomi.data.cache.ChapterCache
import eu.kanade.tachiyomi.data.export.LibraryExporter import eu.kanade.tachiyomi.data.export.LibraryExporter
import eu.kanade.tachiyomi.data.export.LibraryExporter.ExportOptions import eu.kanade.tachiyomi.data.export.LibraryExporter.ExportOptions
import eu.kanade.tachiyomi.util.system.DeviceUtil import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.system.workManager
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import logcat.LogPriority import logcat.LogPriority
import mihon.app.di.appGraph
import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.i18n.stringResource
import tachiyomi.core.common.storage.displayablePath import tachiyomi.core.common.storage.displayablePath
import tachiyomi.core.common.util.lang.launchNonCancellable import tachiyomi.core.common.util.lang.launchNonCancellable
import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.lang.withUIContext
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.backup.service.BackupPreferences import tachiyomi.domain.backup.service.BackupPreferences
import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.manga.interactor.GetFavorites
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.storage.service.StoragePreferences import tachiyomi.domain.storage.service.StoragePreferences
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.TextButton import tachiyomi.presentation.core.components.material.TextButton
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
object SettingsDataScreen : SearchableSettings { object SettingsDataScreen : SearchableSettings {
@@ -98,8 +95,9 @@ object SettingsDataScreen : SearchableSettings {
@Composable @Composable
override fun getPreferences(): List<Preference> { override fun getPreferences(): List<Preference> {
val backupPreferences = Injekt.get<BackupPreferences>() val context = LocalContext.current
val storagePreferences = Injekt.get<StoragePreferences>() val backupPreferences = remember { context.appGraph.backupPreferences }
val storagePreferences = remember { context.appGraph.storagePreferences }
return listOf( return listOf(
getStorageLocationPref(storagePreferences = storagePreferences), getStorageLocationPref(storagePreferences = storagePreferences),
@@ -230,7 +228,7 @@ object SettingsDataScreen : SearchableSettings {
modifier = Modifier.fillMaxHeight(), modifier = Modifier.fillMaxHeight(),
checked = false, checked = false,
onCheckedChange = { onCheckedChange = {
if (!BackupRestoreJob.isRunning(context)) { if (!BackupRestoreJob.isRunning(context.workManager)) {
if (DeviceUtil.isMiui && DeviceUtil.isMiuiOptimizationDisabled()) { if (DeviceUtil.isMiui && DeviceUtil.isMiuiOptimizationDisabled()) {
context.toast(MR.strings.restore_miui_warning) context.toast(MR.strings.restore_miui_warning)
} }
@@ -279,9 +277,9 @@ object SettingsDataScreen : SearchableSettings {
private fun getDataGroup(): Preference.PreferenceGroup { private fun getDataGroup(): Preference.PreferenceGroup {
val context = LocalContext.current val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val libraryPreferences = remember { Injekt.get<LibraryPreferences>() } val libraryPreferences = remember { context.appGraph.libraryPreferences }
val chapterCache = remember { Injekt.get<ChapterCache>() } val chapterCache = remember { context.appGraph.chapterCache }
var cacheReadableSizeSema by remember { mutableIntStateOf(0) } var cacheReadableSizeSema by remember { mutableIntStateOf(0) }
val cacheReadableSize = remember(cacheReadableSizeSema) { chapterCache.readableSize } val cacheReadableSize = remember(cacheReadableSizeSema) { chapterCache.readableSize }
@@ -341,7 +339,7 @@ object SettingsDataScreen : SearchableSettings {
val context = LocalContext.current val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val getFavorites = remember { Injekt.get<GetFavorites>() } val getFavorites = remember { context.appGraph.getFavorites }
var favorites by remember { mutableStateOf<List<Manga>>(emptyList()) } var favorites by remember { mutableStateOf<List<Manga>>(emptyList()) }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
favorites = getFavorites.await() favorites = getFavorites.await()
@@ -8,19 +8,18 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.util.fastMap import androidx.compose.ui.util.fastMap
import eu.kanade.presentation.category.visualName import eu.kanade.presentation.category.visualName
import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.more.settings.widget.TriStateListDialog import eu.kanade.presentation.more.settings.widget.TriStateListDialog
import tachiyomi.domain.category.interactor.GetCategories import mihon.app.di.appGraph
import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.model.Category
import tachiyomi.domain.download.service.DownloadPreferences import tachiyomi.domain.download.service.DownloadPreferences
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.pluralStringResource import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
object SettingsDownloadScreen : SearchableSettings { object SettingsDownloadScreen : SearchableSettings {
@@ -30,10 +29,11 @@ object SettingsDownloadScreen : SearchableSettings {
@Composable @Composable
override fun getPreferences(): List<Preference> { override fun getPreferences(): List<Preference> {
val getCategories = remember { Injekt.get<GetCategories>() } val context = LocalContext.current
val getCategories = remember { context.appGraph.getCategories }
val allCategories by getCategories.subscribe().collectAsState(initial = emptyList()) val allCategories by getCategories.subscribe().collectAsState(initial = emptyList())
val downloadPreferences = remember { Injekt.get<DownloadPreferences>() } val downloadPreferences = remember { context.appGraph.downloadPreferences }
val parallelSourceLimit by downloadPreferences.parallelSourceLimit.collectAsState() val parallelSourceLimit by downloadPreferences.parallelSourceLimit.collectAsState()
val parallelPageLimit by downloadPreferences.parallelPageLimit.collectAsState() val parallelPageLimit by downloadPreferences.parallelPageLimit.collectAsState()
return listOf( return listOf(
@@ -21,8 +21,7 @@ import eu.kanade.presentation.more.settings.widget.TriStateListDialog
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.ui.category.CategoryScreen import eu.kanade.tachiyomi.ui.category.CategoryScreen
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import tachiyomi.domain.category.interactor.GetCategories import mihon.app.di.appGraph
import tachiyomi.domain.category.interactor.ResetCategoryFlags
import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.model.Category
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.library.service.LibraryPreferences.Companion.DEVICE_CHARGING import tachiyomi.domain.library.service.LibraryPreferences.Companion.DEVICE_CHARGING
@@ -38,8 +37,6 @@ import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.pluralStringResource import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
object SettingsLibraryScreen : SearchableSettings { object SettingsLibraryScreen : SearchableSettings {
@@ -49,8 +46,9 @@ object SettingsLibraryScreen : SearchableSettings {
@Composable @Composable
override fun getPreferences(): List<Preference> { override fun getPreferences(): List<Preference> {
val getCategories = remember { Injekt.get<GetCategories>() } val context = LocalContext.current
val libraryPreferences = remember { Injekt.get<LibraryPreferences>() } val getCategories = remember { context.appGraph.getCategories }
val libraryPreferences = remember { context.appGraph.libraryPreferences }
val allCategories by getCategories.subscribe().collectAsState(initial = emptyList()) val allCategories by getCategories.subscribe().collectAsState(initial = emptyList())
return listOf( return listOf(
@@ -66,6 +64,7 @@ object SettingsLibraryScreen : SearchableSettings {
allCategories: List<Category>, allCategories: List<Category>,
libraryPreferences: LibraryPreferences, libraryPreferences: LibraryPreferences,
): Preference.PreferenceGroup { ): Preference.PreferenceGroup {
val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val userCategoriesCount = allCategories.filterNot(Category::isSystemCategory).size val userCategoriesCount = allCategories.filterNot(Category::isSystemCategory).size
@@ -98,7 +97,7 @@ object SettingsLibraryScreen : SearchableSettings {
onValueChanged = { onValueChanged = {
if (!it) { if (!it) {
scope.launch { scope.launch {
Injekt.get<ResetCategoryFlags>().await() context.appGraph.resetCategoryFlags.await()
} }
} }
true true
@@ -172,7 +171,9 @@ object SettingsLibraryScreen : SearchableSettings {
enabled = autoUpdateInterval > 0, enabled = autoUpdateInterval > 0,
onValueChanged = { onValueChanged = {
// Post to event looper to allow the preference to be updated. // Post to event looper to allow the preference to be updated.
ContextCompat.getMainExecutor(context).execute { LibraryUpdateJob.setupTask(context) } ContextCompat.getMainExecutor(context).execute {
LibraryUpdateJob.setupTask(context)
}
true true
}, },
), ),
@@ -4,18 +4,18 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.Preference
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
import eu.kanade.tachiyomi.util.system.hasDisplayCutout import eu.kanade.tachiyomi.util.system.hasDisplayCutout
import mihon.app.di.appGraph
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.pluralStringResource import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.text.NumberFormat import java.text.NumberFormat
object SettingsReaderScreen : SearchableSettings { object SettingsReaderScreen : SearchableSettings {
@@ -26,7 +26,8 @@ object SettingsReaderScreen : SearchableSettings {
@Composable @Composable
override fun getPreferences(): List<Preference> { override fun getPreferences(): List<Preference> {
val readerPref = remember { Injekt.get<ReaderPreferences>() } val context = LocalContext.current
val readerPref = remember { context.appGraph.readerPreferences }
return listOf( return listOf(
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
@@ -12,13 +12,12 @@ import eu.kanade.tachiyomi.core.security.SecurityPreferences
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.isAuthenticationSupported import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.isAuthenticationSupported
import eu.kanade.tachiyomi.util.system.telemetryIncluded import eu.kanade.tachiyomi.util.system.telemetryIncluded
import mihon.app.di.appGraph
import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.i18n.stringResource
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.pluralStringResource import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
object SettingsSecurityScreen : SearchableSettings { object SettingsSecurityScreen : SearchableSettings {
@@ -28,8 +27,9 @@ object SettingsSecurityScreen : SearchableSettings {
@Composable @Composable
override fun getPreferences(): List<Preference> { override fun getPreferences(): List<Preference> {
val securityPreferences = remember { Injekt.get<SecurityPreferences>() } val context = LocalContext.current
val privacyPreferences = remember { Injekt.get<PrivacyPreferences>() } val securityPreferences = remember { context.appGraph.securityPreferences }
val privacyPreferences = remember { context.appGraph.privacyPreferences }
return buildList(2) { return buildList(2) {
add(getSecurityGroup(securityPreferences)) add(getSecurityGroup(securityPreferences))
if (!telemetryIncluded) return@buildList if (!telemetryIncluded) return@buildList
@@ -44,11 +44,9 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.StringResource
import eu.kanade.domain.track.model.AutoTrackState import eu.kanade.domain.track.model.AutoTrackState
import eu.kanade.domain.track.service.TrackPreferences
import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.Preference
import eu.kanade.tachiyomi.data.track.EnhancedTracker import eu.kanade.tachiyomi.data.track.EnhancedTracker
import eu.kanade.tachiyomi.data.track.Tracker import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.data.track.anilist.AnilistApi import eu.kanade.tachiyomi.data.track.anilist.AnilistApi
import eu.kanade.tachiyomi.data.track.bangumi.BangumiApi import eu.kanade.tachiyomi.data.track.bangumi.BangumiApi
import eu.kanade.tachiyomi.data.track.hikka.HikkaApi import eu.kanade.tachiyomi.data.track.hikka.HikkaApi
@@ -57,14 +55,12 @@ import eu.kanade.tachiyomi.data.track.myanimelist.MyAnimeListApi
import eu.kanade.tachiyomi.data.track.shikimori.ShikimoriApi import eu.kanade.tachiyomi.data.track.shikimori.ShikimoriApi
import eu.kanade.tachiyomi.util.system.openInBrowser import eu.kanade.tachiyomi.util.system.openInBrowser
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import mihon.app.di.appGraph
import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.lang.withUIContext
import tachiyomi.domain.source.service.SourceManager
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
object SettingsTrackingScreen : SearchableSettings { object SettingsTrackingScreen : SearchableSettings {
@@ -86,9 +82,9 @@ object SettingsTrackingScreen : SearchableSettings {
@Composable @Composable
override fun getPreferences(): List<Preference> { override fun getPreferences(): List<Preference> {
val context = LocalContext.current val context = LocalContext.current
val trackPreferences = remember { Injekt.get<TrackPreferences>() } val trackPreferences = remember { context.appGraph.trackPreferences }
val trackerManager = remember { Injekt.get<TrackerManager>() } val trackerManager = remember { context.appGraph.trackerManager }
val sourceManager = remember { Injekt.get<SourceManager>() } val sourceManager = remember { context.appGraph.sourceManager }
var dialog by remember { mutableStateOf<Any?>(null) } var dialog by remember { mutableStateOf<Any?>(null) }
dialog?.run { dialog?.run {
@@ -30,10 +30,8 @@ import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
import eu.kanade.presentation.util.LocalBackPress import eu.kanade.presentation.util.LocalBackPress
import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.data.updater.AppUpdateChecker
import eu.kanade.tachiyomi.data.updater.RELEASE_URL import eu.kanade.tachiyomi.data.updater.RELEASE_URL
import eu.kanade.tachiyomi.ui.more.NewUpdateScreen import eu.kanade.tachiyomi.ui.more.NewUpdateScreen
import eu.kanade.tachiyomi.util.CrashLogUtil
import eu.kanade.tachiyomi.util.lang.toDateTimestampString import eu.kanade.tachiyomi.util.lang.toDateTimestampString
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.isFossBuildType import eu.kanade.tachiyomi.util.system.isFossBuildType
@@ -44,6 +42,7 @@ import kotlinx.coroutines.launch
import kotlinx.datetime.TimeZone import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime import kotlinx.datetime.toLocalDateTime
import logcat.LogPriority import logcat.LogPriority
import mihon.app.di.appGraph
import tachiyomi.core.common.Constants import tachiyomi.core.common.Constants
import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.lang.withIOContext
import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.lang.withUIContext
@@ -74,6 +73,7 @@ object AboutScreen : Screen() {
val handleBack = LocalBackPress.current val handleBack = LocalBackPress.current
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
var isCheckingUpdates by remember { mutableStateOf(false) } var isCheckingUpdates by remember { mutableStateOf(false) }
val crashLogUtil = remember { context.appGraph.crashLogUtil }
Scaffold( Scaffold(
topBar = { scrollBehavior -> topBar = { scrollBehavior ->
@@ -98,7 +98,7 @@ object AboutScreen : Screen() {
title = stringResource(MR.strings.version), title = stringResource(MR.strings.version),
subtitle = getVersionName(withBuildDate = true), subtitle = getVersionName(withBuildDate = true),
onPreferenceClick = { onPreferenceClick = {
val deviceInfo = CrashLogUtil(context).getDebugInfo() val deviceInfo = crashLogUtil.getDebugInfo()
context.copyToClipboard("Debug information", deviceInfo) context.copyToClipboard("Debug information", deviceInfo)
}, },
) )
@@ -217,7 +217,7 @@ object AboutScreen : Screen() {
onAvailableUpdate: (GetApplicationRelease.Result.NewUpdate) -> Unit, onAvailableUpdate: (GetApplicationRelease.Result.NewUpdate) -> Unit,
onFinish: () -> Unit, onFinish: () -> Unit,
) { ) {
val updateChecker = AppUpdateChecker() val updateChecker = context.appGraph.updateChecker
withUIContext { withUIContext {
try { try {
when (val result = withIOContext { updateChecker.checkForUpdate(forceCheck = true) }) { when (val result = withIOContext { updateChecker.checkForUpdate(forceCheck = true) }) {
@@ -279,7 +279,7 @@ object AboutScreen : Screen() {
.toLocalDateTime(TimeZone.currentSystemDefault()) .toLocalDateTime(TimeZone.currentSystemDefault())
.toDateTimestampString( .toDateTimestampString(
UiPreferences.dateFormat( UiPreferences.dateFormat(
Injekt.get<UiPreferences>().dateFormat.get(), Injekt.get<Context>().appGraph.uiPreferences.dateFormat.get(),
), ),
) )
} catch (_: Exception) { } catch (_: Exception) {
@@ -32,9 +32,14 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastMap import androidx.compose.ui.util.fastMap
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesIntoMap
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.binding
import dev.zacsweers.metrox.viewmodel.ViewModelKey
import dev.zacsweers.metrox.viewmodel.metroViewModel
import eu.kanade.presentation.browse.components.SourceIcon import eu.kanade.presentation.browse.components.SourceIcon
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.components.AppBarActions
@@ -60,8 +65,6 @@ import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.screens.LoadingScreen
import tachiyomi.presentation.core.util.selectedBackground import tachiyomi.presentation.core.util.selectedBackground
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class ClearDatabaseScreen : Screen() { class ClearDatabaseScreen : Screen() {
@@ -69,7 +72,7 @@ class ClearDatabaseScreen : Screen() {
override fun Content() { override fun Content() {
val context = LocalContext.current val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
val viewModel = viewModel<ClearDatabaseViewModel>() val viewModel = metroViewModel<ClearDatabaseViewModel>()
val state by viewModel.state.collectAsState() val state by viewModel.state.collectAsState()
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
@@ -222,14 +225,17 @@ class ClearDatabaseScreen : Screen() {
} }
} }
class ClearDatabaseViewModel : ViewModel() { @Inject
@ViewModelKey
@ContributesIntoMap(AppScope::class, binding = binding<ViewModel>())
class ClearDatabaseViewModel(
private val database: Database,
private val getSourcesWithNonLibraryManga: GetSourcesWithNonLibraryManga,
) : ViewModel() {
val state: StateFlow<ClearDatabaseViewModel.State> val state: StateFlow<ClearDatabaseViewModel.State>
field = MutableStateFlow<ClearDatabaseViewModel.State>(State.Loading) field = MutableStateFlow<ClearDatabaseViewModel.State>(State.Loading)
private val getSourcesWithNonLibraryManga: GetSourcesWithNonLibraryManga = Injekt.get()
private val database: Database = Injekt.get()
init { init {
viewModelScope.launchIO { viewModelScope.launchIO {
getSourcesWithNonLibraryManga.subscribe() getSourcesWithNonLibraryManga.subscribe()
@@ -8,6 +8,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import dev.zacsweers.metrox.viewmodel.metroViewModel
import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionStoreConfirmDialog import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionStoreConfirmDialog
import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionStoreCreateDialog import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionStoreCreateDialog
import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionStoreDeleteDialog import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionStoreDeleteDialog
@@ -26,7 +27,7 @@ class ExtensionStoresScreen(
val context = LocalContext.current val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
val viewModel = viewModel<ExtensionStoresViewModel>() val viewModel = metroViewModel<ExtensionStoresViewModel>()
val state by viewModel.state.collectAsStateWithLifecycle() val state by viewModel.state.collectAsStateWithLifecycle()
LaunchedEffect(url) { LaunchedEffect(url) {
@@ -3,6 +3,11 @@ package eu.kanade.presentation.more.settings.screen.browse
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesIntoMap
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.binding
import dev.zacsweers.metrox.viewmodel.ViewModelKey
import eu.kanade.tachiyomi.extension.ExtensionManager import eu.kanade.tachiyomi.extension.ExtensionManager
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@@ -20,16 +25,17 @@ import mihon.domain.extension.interactor.RemoveExtensionStore
import mihon.domain.extension.interactor.UpdateExtensionStores import mihon.domain.extension.interactor.UpdateExtensionStores
import mihon.domain.extension.model.ExtensionStore import mihon.domain.extension.model.ExtensionStore
import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.lang.launchIO
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
@Inject
@ViewModelKey
@ContributesIntoMap(AppScope::class, binding = binding<ViewModel>())
class ExtensionStoresViewModel( class ExtensionStoresViewModel(
private val getExtensionStores: GetExtensionStores = Injekt.get(), private val getExtensionStores: GetExtensionStores,
private val addExtensionStore: AddExtensionStore = Injekt.get(), private val addExtensionStore: AddExtensionStore,
private val removeExtensionStore: RemoveExtensionStore = Injekt.get(), private val removeExtensionStore: RemoveExtensionStore,
private val updateExtensionStores: UpdateExtensionStores = Injekt.get(), private val updateExtensionStores: UpdateExtensionStores,
private val extensionManager: ExtensionManager = Injekt.get(), private val extensionManager: ExtensionManager,
) : ViewModel() { ) : ViewModel() {
private val dialog = MutableStateFlow<ExtensionStoreDialog?>(null) private val dialog = MutableStateFlow<ExtensionStoreDialog?>(null)
@@ -12,9 +12,14 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesIntoMap
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.binding
import dev.zacsweers.metrox.viewmodel.ViewModelKey
import dev.zacsweers.metrox.viewmodel.metroViewModel
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.WarningBanner import eu.kanade.presentation.components.WarningBanner
import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.Screen
@@ -39,7 +44,7 @@ class CreateBackupScreen : Screen() {
override fun Content() { override fun Content() {
val context = LocalContext.current val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
val viewModel = viewModel<CreateBackupViewModel>() val viewModel = metroViewModel<CreateBackupViewModel>()
val state by viewModel.state.collectAsState() val state by viewModel.state.collectAsState()
val chooseBackupDir = rememberLauncherForActivityResult( val chooseBackupDir = rememberLauncherForActivityResult(
@@ -73,7 +78,7 @@ class CreateBackupScreen : Screen() {
if (!BackupCreateJob.isManualJobRunning(context)) { if (!BackupCreateJob.isManualJobRunning(context)) {
try { try {
chooseBackupDir.launch(BackupCreator.getFilename()) chooseBackupDir.launch(BackupCreator.getFilename())
} catch (e: ActivityNotFoundException) { } catch (_: ActivityNotFoundException) {
context.toast(MR.strings.file_picker_error) context.toast(MR.strings.file_picker_error)
} }
} else { } else {
@@ -121,6 +126,9 @@ class CreateBackupScreen : Screen() {
} }
} }
@Inject
@ViewModelKey
@ContributesIntoMap(AppScope::class, binding = binding<ViewModel>())
class CreateBackupViewModel : ViewModel() { class CreateBackupViewModel : ViewModel() {
val state: StateFlow<CreateBackupViewModel.State> val state: StateFlow<CreateBackupViewModel.State>
@@ -1,6 +1,5 @@
package eu.kanade.presentation.more.settings.screen.data package eu.kanade.presentation.more.settings.screen.data
import android.app.Application
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@@ -21,12 +20,16 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle import androidx.compose.ui.text.withStyle
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedFactory
import dev.zacsweers.metro.AssistedInject
import dev.zacsweers.metro.ContributesIntoMap
import dev.zacsweers.metrox.viewmodel.ManualViewModelAssistedFactory
import dev.zacsweers.metrox.viewmodel.ManualViewModelAssistedFactoryKey
import dev.zacsweers.metrox.viewmodel.assistedMetroViewModel
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.WarningBanner import eu.kanade.presentation.components.WarningBanner
import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.Screen
@@ -34,6 +37,7 @@ import eu.kanade.tachiyomi.data.backup.BackupFileValidator
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
import eu.kanade.tachiyomi.data.backup.restore.RestoreOptions import eu.kanade.tachiyomi.data.backup.restore.RestoreOptions
import eu.kanade.tachiyomi.util.system.DeviceUtil import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.workManager
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
@@ -44,8 +48,6 @@ import tachiyomi.presentation.core.components.SectionCard
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class RestoreBackupScreen( class RestoreBackupScreen(
private val uri: String, private val uri: String,
@@ -54,12 +56,8 @@ class RestoreBackupScreen(
@Composable @Composable
override fun Content() { override fun Content() {
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
val viewModel = viewModel<RestoreBackupViewModel>( val viewModel =
factory = RestoreBackupViewModel.Factory, assistedMetroViewModel<RestoreBackupViewModel, RestoreBackupViewModel.Factory> { create(uri = uri) }
extras = CreationExtras {
set(RestoreBackupViewModel.URI_KEY, uri)
},
)
val state by viewModel.state.collectAsState() val state by viewModel.state.collectAsState()
Scaffold( Scaffold(
@@ -175,25 +173,21 @@ class RestoreBackupScreen(
} }
} }
@AssistedInject
class RestoreBackupViewModel( class RestoreBackupViewModel(
@Assisted private val uri: String,
private val backupFileValidator: BackupFileValidator,
private val context: Context, private val context: Context,
private val uri: String,
) : ViewModel() { ) : ViewModel() {
val state: StateFlow<RestoreBackupViewModel.State> val state: StateFlow<RestoreBackupViewModel.State>
field = MutableStateFlow<RestoreBackupViewModel.State>(State()) field = MutableStateFlow<RestoreBackupViewModel.State>(State())
companion object { @AssistedFactory
val URI_KEY = CreationExtras.Key<String>() @ManualViewModelAssistedFactoryKey
@ContributesIntoMap(AppScope::class)
val Factory = viewModelFactory { interface Factory : ManualViewModelAssistedFactory {
initializer { fun create(uri: String): RestoreBackupViewModel
RestoreBackupViewModel(
context = Injekt.get<Application>(),
uri = get(URI_KEY)!!,
)
}
}
} }
init { init {
@@ -210,7 +204,7 @@ class RestoreBackupViewModel(
fun startRestore() { fun startRestore() {
BackupRestoreJob.start( BackupRestoreJob.start(
context = context, workManager = context.workManager,
uri = uri.toUri(), uri = uri.toUri(),
options = state.value.options, options = state.value.options,
) )
@@ -218,7 +212,7 @@ class RestoreBackupViewModel(
private fun validate(uri: Uri) { private fun validate(uri: Uri) {
val results = try { val results = try {
BackupFileValidator(context).validate(uri) backupFileValidator.validate(uri)
} catch (e: Exception) { } catch (e: Exception) {
setError( setError(
error = InvalidRestore(uri, e.message.toString()), error = InvalidRestore(uri, e.message.toString()),
@@ -16,7 +16,6 @@ import androidx.compose.ui.platform.LocalContext
import androidx.profileinstaller.ProfileVerifier import androidx.profileinstaller.ProfileVerifier
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.domain.base.BasePreferences
import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.more.settings.PreferenceScaffold import eu.kanade.presentation.more.settings.PreferenceScaffold
import eu.kanade.presentation.more.settings.screen.about.AboutScreen import eu.kanade.presentation.more.settings.screen.about.AboutScreen
@@ -26,11 +25,10 @@ import eu.kanade.tachiyomi.util.system.WebViewUtil
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import kotlinx.coroutines.guava.await import kotlinx.coroutines.guava.await
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import mihon.app.di.appGraph
import mihon.core.common.FeatureFlags import mihon.core.common.FeatureFlags
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class DebugInfoScreen : Screen() { class DebugInfoScreen : Screen() {
@@ -62,7 +60,7 @@ class DebugInfoScreen : Screen() {
val context = LocalContext.current val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val installationIdPref = remember { Injekt.get<BasePreferences>().installationId } val installationIdPref = remember { context.appGraph.basePreferences.installationId }
val installationId by installationIdPref.collectAsState() val installationId by installationIdPref.collectAsState()
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
@@ -1,6 +1,5 @@
package eu.kanade.presentation.more.settings.screen.debug package eu.kanade.presentation.more.settings.screen.debug
import android.app.Application
import android.content.Context import android.content.Context
import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
@@ -21,13 +20,15 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach import androidx.compose.ui.util.fastForEach
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import androidx.work.WorkInfo import androidx.work.WorkInfo
import androidx.work.WorkQuery import androidx.work.WorkQuery
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesIntoMap
import dev.zacsweers.metro.Inject
import dev.zacsweers.metrox.viewmodel.ViewModelKey
import dev.zacsweers.metrox.viewmodel.metroViewModel
import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.UiPreferences
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.components.AppBarActions
@@ -40,11 +41,11 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.datetime.TimeZone import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime import kotlinx.datetime.toLocalDateTime
import mihon.app.di.appGraph
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.plus import tachiyomi.presentation.core.util.plus
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import kotlin.time.Instant import kotlin.time.Instant
@@ -59,7 +60,7 @@ class WorkerInfoScreen : Screen() {
val context = LocalContext.current val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
val viewModel = viewModel<WorkerInfoViewModel>(factory = WorkerInfoViewModel.Factory) val viewModel = metroViewModel<WorkerInfoViewModel>()
val enqueued by viewModel.enqueued.collectAsState() val enqueued by viewModel.enqueued.collectAsState()
val finished by viewModel.finished.collectAsState() val finished by viewModel.finished.collectAsState()
val running by viewModel.running.collectAsState() val running by viewModel.running.collectAsState()
@@ -120,20 +121,13 @@ class WorkerInfoScreen : Screen() {
) )
} }
@Inject
@ViewModelKey
@ContributesIntoMap(AppScope::class)
class WorkerInfoViewModel( class WorkerInfoViewModel(
context: Context, private val context: Context,
) : ViewModel() { ) : ViewModel() {
companion object {
val Factory = viewModelFactory {
initializer {
WorkerInfoViewModel(
context = Injekt.get<Application>(),
)
}
}
}
private val workManager = context.workManager private val workManager = context.workManager
val finished = workManager val finished = workManager
@@ -169,7 +163,7 @@ class WorkerInfoScreen : Screen() {
.toLocalDateTime(TimeZone.currentSystemDefault()) .toLocalDateTime(TimeZone.currentSystemDefault())
.toDateTimestampString( .toDateTimestampString(
UiPreferences.dateFormat( UiPreferences.dateFormat(
Injekt.get<UiPreferences>().dateFormat.get(), context.appGraph.uiPreferences.dateFormat.get(),
), ),
) )
appendLine("Next scheduled run: $timestamp") appendLine("Next scheduled run: $timestamp")
@@ -1,5 +1,6 @@
package eu.kanade.presentation.reader package eu.kanade.presentation.reader
import android.content.Context
import androidx.compose.foundation.Canvas import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -13,6 +14,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import mihon.app.di.appGraph
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@@ -22,7 +24,7 @@ import kotlin.time.Duration.Companion.milliseconds
class DisplayRefreshHost { class DisplayRefreshHost {
internal var currentDisplayRefresh by mutableStateOf(false) internal var currentDisplayRefresh by mutableStateOf(false)
private val readerPreferences = Injekt.get<ReaderPreferences>() private val readerPreferences = Injekt.get<Context>().appGraph.readerPreferences
internal val flashMillis = readerPreferences.flashDurationMillis internal val flashMillis = readerPreferences.flashDurationMillis
internal val flashMode = readerPreferences.flashColor internal val flashMode = readerPreferences.flashColor
@@ -8,6 +8,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import eu.kanade.domain.manga.model.readerOrientation import eu.kanade.domain.manga.model.readerOrientation
import eu.kanade.domain.manga.model.readingMode import eu.kanade.domain.manga.model.readingMode
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
@@ -16,6 +17,7 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsViewModel
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
import eu.kanade.tachiyomi.ui.reader.viewer.webgpu.WebGpuViewer import eu.kanade.tachiyomi.ui.reader.viewer.webgpu.WebGpuViewer
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
import mihon.app.di.appGraph
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.CheckboxItem
import tachiyomi.presentation.core.components.HeadingItem import tachiyomi.presentation.core.components.HeadingItem
@@ -23,8 +25,6 @@ import tachiyomi.presentation.core.components.SettingsChipRow
import tachiyomi.presentation.core.components.SliderItem import tachiyomi.presentation.core.components.SliderItem
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.text.NumberFormat import java.text.NumberFormat
@Composable @Composable
@@ -43,7 +43,7 @@ internal fun ColumnScope.ReadingModePage(viewModel: ReaderSettingsViewModel) {
} }
} }
val default = Injekt.get<ReaderPreferences>().defaultReadingMode.get() val default = LocalContext.current.appGraph.readerPreferences.defaultReadingMode.get()
val resolved = ReadingMode.fromPreference( val resolved = ReadingMode.fromPreference(
when { when {
readingMode == ReadingMode.DEFAULT -> default readingMode == ReadingMode.DEFAULT -> default
@@ -7,7 +7,6 @@ import androidx.compose.material3.MaterialExpressiveTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.domain.ui.model.AppTheme import eu.kanade.domain.ui.model.AppTheme
import eu.kanade.presentation.theme.colorscheme.BaseColorScheme import eu.kanade.presentation.theme.colorscheme.BaseColorScheme
import eu.kanade.presentation.theme.colorscheme.CatppuccinColorScheme import eu.kanade.presentation.theme.colorscheme.CatppuccinColorScheme
@@ -25,8 +24,7 @@ import eu.kanade.presentation.theme.colorscheme.TidalWaveColorScheme
import eu.kanade.presentation.theme.colorscheme.TokyoNightColorScheme import eu.kanade.presentation.theme.colorscheme.TokyoNightColorScheme
import eu.kanade.presentation.theme.colorscheme.YinYangColorScheme import eu.kanade.presentation.theme.colorscheme.YinYangColorScheme
import eu.kanade.presentation.theme.colorscheme.YotsubaColorScheme import eu.kanade.presentation.theme.colorscheme.YotsubaColorScheme
import uy.kohesive.injekt.Injekt import mihon.app.di.appGraph
import uy.kohesive.injekt.api.get
@Composable @Composable
fun TachiyomiTheme( fun TachiyomiTheme(
@@ -34,7 +32,8 @@ fun TachiyomiTheme(
amoled: Boolean? = null, amoled: Boolean? = null,
content: @Composable () -> Unit, content: @Composable () -> Unit,
) { ) {
val uiPreferences = Injekt.get<UiPreferences>() val context = LocalContext.current
val uiPreferences = remember { context.appGraph.uiPreferences }
BaseTachiyomiTheme( BaseTachiyomiTheme(
appTheme = appTheme ?: uiPreferences.appTheme.get(), appTheme = appTheme ?: uiPreferences.appTheme.get(),
isAmoled = amoled ?: uiPreferences.themeDarkAmoled.get(), isAmoled = amoled ?: uiPreferences.themeDarkAmoled.get(),
+66 -33
View File
@@ -22,21 +22,21 @@ import coil3.request.allowRgb565
import coil3.request.crossfade import coil3.request.crossfade
import coil3.util.DebugLogger import coil3.util.DebugLogger
import dev.mihon.injekt.patchInjekt import dev.mihon.injekt.patchInjekt
import eu.kanade.domain.DomainModule import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.createGraphFactory
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.UiPreferences
import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode
import eu.kanade.tachiyomi.core.security.PrivacyPreferences import eu.kanade.tachiyomi.core.security.PrivacyPreferences
import eu.kanade.tachiyomi.crash.CrashActivity import eu.kanade.tachiyomi.crash.CrashActivity
import eu.kanade.tachiyomi.crash.GlobalExceptionHandler import eu.kanade.tachiyomi.crash.GlobalExceptionHandler
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.coil.BufferedSourceFetcher import eu.kanade.tachiyomi.data.coil.BufferedSourceFetcher
import eu.kanade.tachiyomi.data.coil.ImageDecoder import eu.kanade.tachiyomi.data.coil.ImageDecoder
import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher
import eu.kanade.tachiyomi.data.coil.MangaCoverKeyer import eu.kanade.tachiyomi.data.coil.MangaCoverKeyer
import eu.kanade.tachiyomi.data.coil.MangaKeyer import eu.kanade.tachiyomi.data.coil.MangaKeyer
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.di.AppModule
import eu.kanade.tachiyomi.di.PreferenceModule
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.NetworkPreferences import eu.kanade.tachiyomi.network.NetworkPreferences
import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate
@@ -44,6 +44,7 @@ import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.WebViewUtil import eu.kanade.tachiyomi.util.system.WebViewUtil
import eu.kanade.tachiyomi.util.system.animatorDurationScale import eu.kanade.tachiyomi.util.system.animatorDurationScale
import eu.kanade.tachiyomi.util.system.cancelNotification import eu.kanade.tachiyomi.util.system.cancelNotification
import eu.kanade.tachiyomi.util.system.isDebugBuildType
import eu.kanade.tachiyomi.util.system.notify import eu.kanade.tachiyomi.util.system.notify
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
@@ -51,34 +52,68 @@ import kotlinx.coroutines.flow.onEach
import logcat.AndroidLogcatLogger import logcat.AndroidLogcatLogger
import logcat.LogPriority import logcat.LogPriority
import logcat.LogcatLogger import logcat.LogcatLogger
import mihon.app.di.AppGraph
import mihon.app.di.injekt.MetroInteropModule
import mihon.core.metro.GraphProvider
import mihon.core.migration.Migration
import mihon.core.migration.Migrator import mihon.core.migration.Migrator
import mihon.core.migration.migrations.migrations
import mihon.telemetry.TelemetryConfig import mihon.telemetry.TelemetryConfig
import org.conscrypt.Conscrypt import org.conscrypt.Conscrypt
import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.i18n.stringResource
import tachiyomi.core.common.preference.Preference import tachiyomi.core.common.preference.Preference
import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.core.common.util.system.ImageUtil
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.source.service.SourceManager
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.widget.WidgetManager import tachiyomi.presentation.widget.WidgetManager
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.addSingleton
import uy.kohesive.injekt.injectLazy
import java.security.Security import java.security.Security
class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factory { class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factory, GraphProvider<AppGraph> {
private val basePreferences: BasePreferences by injectLazy() override val graph: AppGraph by lazy {
private val privacyPreferences: PrivacyPreferences by injectLazy() createGraphFactory<AppGraph.Factory>().create(context = this, isDebugBuild = isDebugBuildType)
private val networkPreferences: NetworkPreferences by injectLazy() }
@Inject private lateinit var preferenceStore: PreferenceStore
@Inject private lateinit var basePreferences: BasePreferences
@Inject private lateinit var privacyPreferences: PrivacyPreferences
@Inject private lateinit var networkPreferences: NetworkPreferences
@Inject private lateinit var uiPreferences: UiPreferences
@Inject private lateinit var coverCache: CoverCache
@Inject private lateinit var networkHelper: NetworkHelper
@Inject private lateinit var sourceManager: SourceManager
@Inject private lateinit var widgetManager: WidgetManager
@Inject private lateinit var injektMetroInteropModule: MetroInteropModule
@Inject private lateinit var migrations: Set<Migration>
private val disableIncognitoReceiver = DisableIncognitoReceiver() private val disableIncognitoReceiver = DisableIncognitoReceiver()
@SuppressLint("LaunchActivityFromNotification") @SuppressLint("LaunchActivityFromNotification")
override fun onCreate() { override fun onCreate() {
super<Application>.onCreate() super<Application>.onCreate()
patchInjekt()
// Must run before the graph is built, since injecting dependencies initializes WebView and the
// suffix can't be set once a provider exists in the process. Secondary processes die otherwise.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val process = getProcessName()
if (packageName != process) WebView.setDataDirectorySuffix(process)
}
graph.inject(this)
setupInjekt()
TelemetryConfig.init(applicationContext) TelemetryConfig.init(applicationContext)
GlobalExceptionHandler.initialize(applicationContext, CrashActivity::class.java) GlobalExceptionHandler.initialize(applicationContext, CrashActivity::class.java)
@@ -88,16 +123,6 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
Security.insertProviderAt(Conscrypt.newProvider(), 1) Security.insertProviderAt(Conscrypt.newProvider(), 1)
} }
// Avoid potential crashes
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val process = getProcessName()
if (packageName != process) WebView.setDataDirectorySuffix(process)
}
Injekt.importModule(PreferenceModule(this))
Injekt.importModule(AppModule(this))
Injekt.importModule(DomainModule())
setupNotificationChannels() setupNotificationChannels()
ProcessLifecycleOwner.get().lifecycle.addObserver(this) ProcessLifecycleOwner.get().lifecycle.addObserver(this)
@@ -143,10 +168,10 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
.onEach(TelemetryConfig::setCrashlyticsEnabled) .onEach(TelemetryConfig::setCrashlyticsEnabled)
.launchIn(scope) .launchIn(scope)
setAppCompatDelegateThemeMode(Injekt.get<UiPreferences>().themeMode.get()) setAppCompatDelegateThemeMode(uiPreferences.themeMode.get())
// Updates widget update // Updates widget update
WidgetManager(Injekt.get(), Injekt.get()).apply { init(scope) } widgetManager.init(scope)
if (!LogcatLogger.isInstalled) { if (!LogcatLogger.isInstalled) {
val minLogPriority = when { val minLogPriority = when {
@@ -161,14 +186,22 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
initializeMigrator() initializeMigrator()
} }
private fun setupInjekt() {
patchInjekt()
Injekt.addSingleton<Application>(this)
Injekt.addSingleton<Context>(this)
Injekt.importModule(injektMetroInteropModule)
}
private fun initializeMigrator() { private fun initializeMigrator() {
val preferenceStore = Injekt.get<PreferenceStore>()
val preference = preferenceStore.getInt(Preference.appStateKey("last_version_code"), 0) val preference = preferenceStore.getInt(Preference.appStateKey("last_version_code"), 0)
logcat { "Migration from ${preference.get()} to ${BuildConfig.VERSION_CODE}" } logcat {
"Migration from ${preference.get()} to ${BuildConfig.VERSION_CODE} with ${migrations.size} migration(s)"
}
Migrator.initialize( Migrator.initialize(
old = preference.get(), old = preference.get(),
new = BuildConfig.VERSION_CODE, new = BuildConfig.VERSION_CODE,
migrations = migrations, migrations = migrations.toList(),
onMigrationComplete = { onMigrationComplete = {
logcat { "Updating last version to ${BuildConfig.VERSION_CODE}" } logcat { "Updating last version to ${BuildConfig.VERSION_CODE}" }
preference.set(BuildConfig.VERSION_CODE) preference.set(BuildConfig.VERSION_CODE)
@@ -178,7 +211,7 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
override fun newImageLoader(context: Context): ImageLoader { override fun newImageLoader(context: Context): ImageLoader {
return ImageLoader.Builder(this).apply { return ImageLoader.Builder(this).apply {
val callFactoryLazy = lazy { Injekt.get<NetworkHelper>().client } val callFactoryLazy = lazy { networkHelper.client }
components { components {
// NetworkFetcher.Factory // NetworkFetcher.Factory
add(OkHttpNetworkFetcherFactory(callFactoryLazy::value)) add(OkHttpNetworkFetcherFactory(callFactoryLazy::value))
@@ -186,10 +219,10 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
add(ImageDecoder.Factory()) add(ImageDecoder.Factory())
// Fetcher.Factory // Fetcher.Factory
add(BufferedSourceFetcher.Factory()) add(BufferedSourceFetcher.Factory())
add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy)) add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy, coverCache, sourceManager))
add(MangaCoverFetcher.MangaFactory(callFactoryLazy)) add(MangaCoverFetcher.MangaFactory(callFactoryLazy, coverCache, sourceManager))
// Keyer // Keyer
add(MangaCoverKeyer()) add(MangaCoverKeyer(coverCache))
add(MangaKeyer()) add(MangaKeyer())
} }
@@ -211,11 +244,11 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
} }
override fun onStart(owner: LifecycleOwner) { override fun onStart(owner: LifecycleOwner) {
SecureActivityDelegate.onApplicationStart() SecureActivityDelegate.onApplicationStart(this)
} }
override fun onStop(owner: LifecycleOwner) { override fun onStop(owner: LifecycleOwner) {
SecureActivityDelegate.onApplicationStopped() SecureActivityDelegate.onApplicationStopped(this)
} }
override fun getPackageName(): String { override fun getPackageName(): String {
@@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.backup
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.data.backup.models.Backup import eu.kanade.tachiyomi.data.backup.models.Backup
import kotlinx.serialization.SerializationException import kotlinx.serialization.SerializationException
import kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.protobuf.ProtoBuf
@@ -10,13 +11,12 @@ import okio.gzip
import okio.source import okio.source
import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.i18n.stringResource
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.IOException import java.io.IOException
@Inject
class BackupDecoder( class BackupDecoder(
private val context: Context, private val context: Context,
private val parser: ProtoBuf = Injekt.get(), private val parser: ProtoBuf,
) { ) {
/** /**
* Decode a potentially-gzipped backup. * Decode a potentially-gzipped backup.
@@ -1,17 +1,15 @@
package eu.kanade.tachiyomi.data.backup package eu.kanade.tachiyomi.data.backup
import android.content.Context
import android.net.Uri import android.net.Uri
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.data.track.TrackerManager import eu.kanade.tachiyomi.data.track.TrackerManager
import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.service.SourceManager
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@Inject
class BackupFileValidator( class BackupFileValidator(
private val context: Context, private val sourceManager: SourceManager,
private val trackerManager: TrackerManager,
private val sourceManager: SourceManager = Injekt.get(), private val backupDecoder: BackupDecoder,
private val trackerManager: TrackerManager = Injekt.get(),
) { ) {
/** /**
@@ -21,7 +19,7 @@ class BackupFileValidator(
*/ */
fun validate(uri: Uri): Results { fun validate(uri: Uri): Results {
val backup = try { val backup = try {
BackupDecoder(context).decode(uri) backupDecoder.decode(uri)
} catch (e: Exception) { } catch (e: Exception) {
throw IllegalStateException(e) throw IllegalStateException(e)
} }
@@ -4,6 +4,7 @@ import android.content.Context
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.core.security.SecurityPreferences import eu.kanade.tachiyomi.core.security.SecurityPreferences
import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.notification.NotificationReceiver
@@ -16,14 +17,14 @@ import tachiyomi.core.common.i18n.pluralStringResource
import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.i18n.stringResource
import tachiyomi.core.common.storage.displayablePath import tachiyomi.core.common.storage.displayablePath
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import uy.kohesive.injekt.injectLazy
import java.io.File import java.io.File
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
class BackupNotifier(private val context: Context) { @Inject
class BackupNotifier(
private val preferences: SecurityPreferences by injectLazy() private val context: Context,
private val preferences: SecurityPreferences,
) {
private val progressNotificationBuilder = context.notificationBuilder( private val progressNotificationBuilder = context.notificationBuilder(
Notifications.CHANNEL_BACKUP_RESTORE_PROGRESS, Notifications.CHANNEL_BACKUP_RESTORE_PROGRESS,
) { ) {
@@ -16,6 +16,7 @@ import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import androidx.work.workDataOf import androidx.work.workDataOf
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.data.backup.BackupNotifier import eu.kanade.tachiyomi.data.backup.BackupNotifier
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
@@ -24,22 +25,33 @@ import eu.kanade.tachiyomi.util.system.isRunning
import eu.kanade.tachiyomi.util.system.setForegroundSafely import eu.kanade.tachiyomi.util.system.setForegroundSafely
import eu.kanade.tachiyomi.util.system.workManager import eu.kanade.tachiyomi.util.system.workManager
import logcat.LogPriority import logcat.LogPriority
import mihon.app.di.AppGraph
import mihon.app.di.appGraph
import mihon.core.metro.metroGraph
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.backup.service.BackupPreferences
import tachiyomi.domain.storage.service.StorageManager import tachiyomi.domain.storage.service.StorageManager
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class BackupCreateJob(private val context: Context, workerParams: WorkerParameters) : class BackupCreateJob(private val context: Context, workerParams: WorkerParameters) :
CoroutineWorker(context, workerParams) { CoroutineWorker(context, workerParams) {
private val notifier = BackupNotifier(context) private val graph: AppGraph = context.metroGraph()
@Inject
private lateinit var backupCreatorFactory: BackupCreator.Factory
@Inject
private lateinit var storageManager: StorageManager
@Inject private lateinit var notifier: BackupNotifier
override suspend fun doWork(): Result { override suspend fun doWork(): Result {
graph.inject(this)
val isAutoBackup = inputData.getBoolean(IS_AUTO_BACKUP_KEY, true) val isAutoBackup = inputData.getBoolean(IS_AUTO_BACKUP_KEY, true)
if (isAutoBackup && BackupRestoreJob.isRunning(context)) return Result.retry() if (isAutoBackup && BackupRestoreJob.isRunning(context.workManager)) return Result.retry()
val uri = inputData.getString(LOCATION_URI_KEY)?.toUri() val uri = inputData.getString(LOCATION_URI_KEY)?.toUri()
?: getAutomaticBackupLocation() ?: getAutomaticBackupLocation()
@@ -51,7 +63,7 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete
?: BackupOptions() ?: BackupOptions()
return try { return try {
val location = BackupCreator(context, isAutoBackup).backup(uri, options) val location = backupCreatorFactory.create(isAutoBackup = isAutoBackup).backup(uri, options)
if (!isAutoBackup) { if (!isAutoBackup) {
notifier.showBackupComplete(UniFile.fromUri(context, location.toUri())!!) notifier.showBackupComplete(UniFile.fromUri(context, location.toUri())!!)
} }
@@ -78,7 +90,6 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete
} }
private fun getAutomaticBackupLocation(): Uri? { private fun getAutomaticBackupLocation(): Uri? {
val storageManager = Injekt.get<StorageManager>()
return storageManager.getAutomaticBackupsDirectory()?.uri return storageManager.getAutomaticBackupsDirectory()?.uri
} }
@@ -88,7 +99,7 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete
} }
fun setupTask(context: Context, prefInterval: Int? = null) { fun setupTask(context: Context, prefInterval: Int? = null) {
val backupPreferences = Injekt.get<BackupPreferences>() val backupPreferences = context.appGraph.backupPreferences
val interval = prefInterval ?: backupPreferences.backupInterval.get() val interval = prefInterval ?: backupPreferences.backupInterval.get()
if (interval > 0) { if (interval > 0) {
val constraints = Constraints( val constraints = Constraints(
@@ -3,6 +3,9 @@ package eu.kanade.tachiyomi.data.backup.create
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedFactory
import dev.zacsweers.metro.AssistedInject
import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.data.backup.BackupFileValidator import eu.kanade.tachiyomi.data.backup.BackupFileValidator
import eu.kanade.tachiyomi.data.backup.create.creators.CategoriesBackupCreator import eu.kanade.tachiyomi.data.backup.create.creators.CategoriesBackupCreator
@@ -29,29 +32,31 @@ import tachiyomi.domain.manga.interactor.GetFavorites
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.repository.MangaRepository import tachiyomi.domain.manga.repository.MangaRepository
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.FileOutputStream import java.io.FileOutputStream
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
import kotlin.time.Clock import kotlin.time.Clock
@AssistedInject
class BackupCreator( class BackupCreator(
@Assisted private val isAutoBackup: Boolean,
private val context: Context, private val context: Context,
private val isAutoBackup: Boolean, private val parser: ProtoBuf,
private val getFavorites: GetFavorites,
private val parser: ProtoBuf = Injekt.get(), private val backupPreferences: BackupPreferences,
private val getFavorites: GetFavorites = Injekt.get(), private val mangaRepository: MangaRepository,
private val backupPreferences: BackupPreferences = Injekt.get(), private val categoriesBackupCreator: CategoriesBackupCreator,
private val mangaRepository: MangaRepository = Injekt.get(), private val mangaBackupCreator: MangaBackupCreator,
private val preferenceBackupCreator: PreferenceBackupCreator,
private val categoriesBackupCreator: CategoriesBackupCreator = CategoriesBackupCreator(), private val extensionStoresBackupCreator: ExtensionStoresBackupCreator,
private val mangaBackupCreator: MangaBackupCreator = MangaBackupCreator(), private val sourcesBackupCreator: SourcesBackupCreator,
private val preferenceBackupCreator: PreferenceBackupCreator = PreferenceBackupCreator(), private val backupFileValidator: BackupFileValidator,
private val extensionStoresBackupCreator: ExtensionStoresBackupCreator = ExtensionStoresBackupCreator(),
private val sourcesBackupCreator: SourcesBackupCreator = SourcesBackupCreator(),
) { ) {
@AssistedFactory
fun interface Factory {
fun create(isAutoBackup: Boolean): BackupCreator
}
suspend fun backup(uri: Uri, options: BackupOptions): String { suspend fun backup(uri: Uri, options: BackupOptions): String {
var file: UniFile? = null var file: UniFile? = null
@@ -105,7 +110,7 @@ class BackupCreator(
val fileUri = file.uri val fileUri = file.uri
// Make sure it's a valid backup file // Make sure it's a valid backup file
BackupFileValidator(context).validate(fileUri) backupFileValidator.validate(fileUri)
if (isAutoBackup) { if (isAutoBackup) {
backupPreferences.lastAutoBackupTimestamp.set(Clock.System.now().toEpochMilliseconds()) backupPreferences.lastAutoBackupTimestamp.set(Clock.System.now().toEpochMilliseconds())
@@ -1,14 +1,14 @@
package eu.kanade.tachiyomi.data.backup.create.creators package eu.kanade.tachiyomi.data.backup.create.creators
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.data.backup.models.BackupCategory import eu.kanade.tachiyomi.data.backup.models.BackupCategory
import eu.kanade.tachiyomi.data.backup.models.backupCategoryMapper import eu.kanade.tachiyomi.data.backup.models.backupCategoryMapper
import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.model.Category
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@Inject
class CategoriesBackupCreator( class CategoriesBackupCreator(
private val getCategories: GetCategories = Injekt.get(), private val getCategories: GetCategories,
) { ) {
suspend operator fun invoke(): List<BackupCategory> { suspend operator fun invoke(): List<BackupCategory> {
@@ -1,13 +1,13 @@
package eu.kanade.tachiyomi.data.backup.create.creators package eu.kanade.tachiyomi.data.backup.create.creators
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.data.backup.models.BackupExtensionStore import eu.kanade.tachiyomi.data.backup.models.BackupExtensionStore
import eu.kanade.tachiyomi.data.backup.models.backupExtensionStoreMapper import eu.kanade.tachiyomi.data.backup.models.backupExtensionStoreMapper
import mihon.domain.extension.interactor.GetExtensionStores import mihon.domain.extension.interactor.GetExtensionStores
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@Inject
class ExtensionStoresBackupCreator( class ExtensionStoresBackupCreator(
private val getExtensionStores: GetExtensionStores = Injekt.get(), private val getExtensionStores: GetExtensionStores,
) { ) {
suspend operator fun invoke(): List<BackupExtensionStore> { suspend operator fun invoke(): List<BackupExtensionStore> {
@@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.backup.create.creators
import app.cash.sqldelight.async.coroutines.awaitAsList import app.cash.sqldelight.async.coroutines.awaitAsList
import app.cash.sqldelight.async.coroutines.awaitAsOne import app.cash.sqldelight.async.coroutines.awaitAsOne
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.data.backup.create.BackupOptions import eu.kanade.tachiyomi.data.backup.create.BackupOptions
import eu.kanade.tachiyomi.data.backup.models.BackupChapter import eu.kanade.tachiyomi.data.backup.models.BackupChapter
import eu.kanade.tachiyomi.data.backup.models.BackupHistory import eu.kanade.tachiyomi.data.backup.models.BackupHistory
@@ -14,13 +15,12 @@ import tachiyomi.data.MemoColumnAdapter
import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.history.interactor.GetHistory import tachiyomi.domain.history.interactor.GetHistory
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@Inject
class MangaBackupCreator( class MangaBackupCreator(
private val database: Database = Injekt.get(), private val database: Database,
private val getCategories: GetCategories = Injekt.get(), private val getCategories: GetCategories,
private val getHistory: GetHistory = Injekt.get(), private val getHistory: GetHistory,
) { ) {
suspend operator fun invoke(mangas: List<Manga>, options: BackupOptions): List<BackupManga> { suspend operator fun invoke(mangas: List<Manga>, options: BackupOptions): List<BackupManga> {
@@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.data.backup.create.creators package eu.kanade.tachiyomi.data.backup.create.creators
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.data.backup.models.BackupPreference import eu.kanade.tachiyomi.data.backup.models.BackupPreference
import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences
import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue
@@ -14,12 +15,11 @@ import eu.kanade.tachiyomi.source.sourcePreferences
import tachiyomi.core.common.preference.Preference import tachiyomi.core.common.preference.Preference
import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.service.SourceManager
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@Inject
class PreferenceBackupCreator( class PreferenceBackupCreator(
private val sourceManager: SourceManager = Injekt.get(), private val sourceManager: SourceManager,
private val preferenceStore: PreferenceStore = Injekt.get(), private val preferenceStore: PreferenceStore,
) { ) {
fun createApp(includePrivatePreferences: Boolean): List<BackupPreference> { fun createApp(includePrivatePreferences: Boolean): List<BackupPreference> {
@@ -1,14 +1,14 @@
package eu.kanade.tachiyomi.data.backup.create.creators package eu.kanade.tachiyomi.data.backup.create.creators
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.data.backup.models.BackupManga import eu.kanade.tachiyomi.data.backup.models.BackupManga
import eu.kanade.tachiyomi.data.backup.models.BackupSource import eu.kanade.tachiyomi.data.backup.models.BackupSource
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.service.SourceManager
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@Inject
class SourcesBackupCreator( class SourcesBackupCreator(
private val sourceManager: SourceManager = Injekt.get(), private val sourceManager: SourceManager,
) { ) {
operator fun invoke(mangas: List<BackupManga>): List<BackupSource> { operator fun invoke(mangas: List<BackupManga>): List<BackupSource> {
@@ -9,16 +9,19 @@ import androidx.work.CoroutineWorker
import androidx.work.ExistingWorkPolicy import androidx.work.ExistingWorkPolicy
import androidx.work.ForegroundInfo import androidx.work.ForegroundInfo
import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import androidx.work.workDataOf import androidx.work.workDataOf
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.data.backup.BackupNotifier import eu.kanade.tachiyomi.data.backup.BackupNotifier
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.system.cancelNotification import eu.kanade.tachiyomi.util.system.cancelNotification
import eu.kanade.tachiyomi.util.system.isRunning import eu.kanade.tachiyomi.util.system.isRunning
import eu.kanade.tachiyomi.util.system.setForegroundSafely import eu.kanade.tachiyomi.util.system.setForegroundSafely
import eu.kanade.tachiyomi.util.system.workManager
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import logcat.LogPriority import logcat.LogPriority
import mihon.app.di.AppGraph
import mihon.core.metro.metroGraph
import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.i18n.stringResource
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
@@ -26,9 +29,15 @@ import tachiyomi.i18n.MR
class BackupRestoreJob(private val context: Context, workerParams: WorkerParameters) : class BackupRestoreJob(private val context: Context, workerParams: WorkerParameters) :
CoroutineWorker(context, workerParams) { CoroutineWorker(context, workerParams) {
private val notifier = BackupNotifier(context) private val graph: AppGraph = context.metroGraph()
@Inject private lateinit var backupRestorerFactory: BackupRestorer.Factory
@Inject private lateinit var notifier: BackupNotifier
override suspend fun doWork(): Result { override suspend fun doWork(): Result {
graph.inject(this)
val uri = inputData.getString(LOCATION_URI_KEY)?.toUri() val uri = inputData.getString(LOCATION_URI_KEY)?.toUri()
val options = inputData.getBooleanArray(OPTIONS_KEY)?.let { RestoreOptions.fromBooleanArray(it) } val options = inputData.getBooleanArray(OPTIONS_KEY)?.let { RestoreOptions.fromBooleanArray(it) }
@@ -41,7 +50,7 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet
setForegroundSafely() setForegroundSafely()
return try { return try {
BackupRestorer(context, notifier, isSync).restore(uri, options) backupRestorerFactory.create(notifier = notifier, isSync = isSync).restore(uri, options)
Result.success() Result.success()
} catch (e: Exception) { } catch (e: Exception) {
if (e is CancellationException) { if (e is CancellationException) {
@@ -70,12 +79,12 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet
} }
companion object { companion object {
fun isRunning(context: Context): Boolean { fun isRunning(workManager: WorkManager): Boolean {
return context.workManager.isRunning(TAG) return workManager.isRunning(TAG)
} }
fun start( fun start(
context: Context, workManager: WorkManager,
uri: Uri, uri: Uri,
options: RestoreOptions, options: RestoreOptions,
sync: Boolean = false, sync: Boolean = false,
@@ -89,11 +98,11 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet
.addTag(TAG) .addTag(TAG)
.setInputData(inputData) .setInputData(inputData)
.build() .build()
context.workManager.enqueueUniqueWork(TAG, ExistingWorkPolicy.KEEP, request) workManager.enqueueUniqueWork(TAG, ExistingWorkPolicy.KEEP, request)
} }
fun stop(context: Context) { fun stop(workManager: WorkManager) {
context.workManager.cancelUniqueWork(TAG) workManager.cancelUniqueWork(TAG)
} }
} }
} }
@@ -2,6 +2,9 @@ package eu.kanade.tachiyomi.data.backup.restore
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedFactory
import dev.zacsweers.metro.AssistedInject
import eu.kanade.tachiyomi.data.backup.BackupDecoder import eu.kanade.tachiyomi.data.backup.BackupDecoder
import eu.kanade.tachiyomi.data.backup.BackupNotifier import eu.kanade.tachiyomi.data.backup.BackupNotifier
import eu.kanade.tachiyomi.data.backup.models.BackupCategory import eu.kanade.tachiyomi.data.backup.models.BackupCategory
@@ -24,8 +27,6 @@ import tachiyomi.core.common.i18n.stringResource
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
import tachiyomi.data.Database import tachiyomi.data.Database
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
@@ -36,18 +37,25 @@ import kotlin.concurrent.atomics.ExperimentalAtomicApi
import kotlin.concurrent.atomics.incrementAndFetch import kotlin.concurrent.atomics.incrementAndFetch
@OptIn(ExperimentalAtomicApi::class) @OptIn(ExperimentalAtomicApi::class)
@AssistedInject
class BackupRestorer( class BackupRestorer(
@Assisted private val notifier: BackupNotifier,
@Assisted private val isSync: Boolean,
private val context: Context, private val context: Context,
private val notifier: BackupNotifier, private val database: Database,
private val isSync: Boolean, private val downloadCache: DownloadCache,
private val categoriesRestorer: CategoriesRestorer,
private val database: Database = Injekt.get(), private val preferenceRestorer: PreferenceRestorer,
private val categoriesRestorer: CategoriesRestorer = CategoriesRestorer(), private val extensionStoreRestorer: ExtensionStoreRestorer,
private val preferenceRestorer: PreferenceRestorer = PreferenceRestorer(context), private val mangaRestorer: MangaRestorer,
private val extensionStoreRestorer: ExtensionStoreRestorer = ExtensionStoreRestorer(), private val backupDecoder: BackupDecoder,
private val mangaRestorer: MangaRestorer = MangaRestorer(),
) { ) {
@AssistedFactory
fun interface Factory {
fun create(notifier: BackupNotifier, isSync: Boolean): BackupRestorer
}
private var restoreAmount = 0 private var restoreAmount = 0
private val restoreProgress = AtomicInt(0) private val restoreProgress = AtomicInt(0)
private val errors = CopyOnWriteArrayList<Pair<Date, String>>() private val errors = CopyOnWriteArrayList<Pair<Date, String>>()
@@ -65,7 +73,7 @@ class BackupRestorer(
// Invalidate download cache to ensure UI reflects any restored downloads // Invalidate download cache to ensure UI reflects any restored downloads
if (options.libraryEntries) { if (options.libraryEntries) {
try { try {
Injekt.get<DownloadCache>().invalidateCache() downloadCache.invalidateCache()
} catch (e: Exception) { } catch (e: Exception) {
logcat(LogPriority.ERROR, e) { "Failed to invalidate download cache after restore" } logcat(LogPriority.ERROR, e) { "Failed to invalidate download cache after restore" }
} }
@@ -85,7 +93,7 @@ class BackupRestorer(
} }
private suspend fun restoreFromFile(uri: Uri, options: RestoreOptions) { private suspend fun restoreFromFile(uri: Uri, options: RestoreOptions) {
val backup = BackupDecoder(context).decode(uri) val backup = backupDecoder.decode(uri)
// Store source mapping for error messages // Store source mapping for error messages
val backupMaps = backup.backupSources val backupMaps = backup.backupSources
@@ -1,16 +1,16 @@
package eu.kanade.tachiyomi.data.backup.restore.restorers package eu.kanade.tachiyomi.data.backup.restore.restorers
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.data.backup.models.BackupCategory import eu.kanade.tachiyomi.data.backup.models.BackupCategory
import tachiyomi.data.Database import tachiyomi.data.Database
import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@Inject
class CategoriesRestorer( class CategoriesRestorer(
private val database: Database = Injekt.get(), private val database: Database,
private val getCategories: GetCategories = Injekt.get(), private val getCategories: GetCategories,
private val libraryPreferences: LibraryPreferences = Injekt.get(), private val libraryPreferences: LibraryPreferences,
) { ) {
suspend operator fun invoke(backupCategories: List<BackupCategory>) { suspend operator fun invoke(backupCategories: List<BackupCategory>) {
@@ -1,12 +1,12 @@
package eu.kanade.tachiyomi.data.backup.restore.restorers package eu.kanade.tachiyomi.data.backup.restore.restorers
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.data.backup.models.BackupExtensionStore import eu.kanade.tachiyomi.data.backup.models.BackupExtensionStore
import tachiyomi.data.Database import tachiyomi.data.Database
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@Inject
class ExtensionStoreRestorer( class ExtensionStoreRestorer(
private val database: Database = Injekt.get(), private val database: Database,
) { ) {
suspend operator fun invoke( suspend operator fun invoke(
@@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.backup.restore.restorers
import app.cash.sqldelight.async.coroutines.awaitAsList import app.cash.sqldelight.async.coroutines.awaitAsList
import app.cash.sqldelight.async.coroutines.awaitAsOne import app.cash.sqldelight.async.coroutines.awaitAsOne
import app.cash.sqldelight.async.coroutines.awaitAsOneOrNull import app.cash.sqldelight.async.coroutines.awaitAsOneOrNull
import dev.zacsweers.metro.Inject
import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.tachiyomi.data.backup.models.BackupCategory import eu.kanade.tachiyomi.data.backup.models.BackupCategory
import eu.kanade.tachiyomi.data.backup.models.BackupChapter import eu.kanade.tachiyomi.data.backup.models.BackupChapter
@@ -23,21 +24,20 @@ import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.domain.track.interactor.GetTracks
import tachiyomi.domain.track.interactor.InsertTrack import tachiyomi.domain.track.interactor.InsertTrack
import tachiyomi.domain.track.model.Track import tachiyomi.domain.track.model.Track
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.Date import java.util.Date
import kotlin.math.max import kotlin.math.max
import kotlin.time.Clock import kotlin.time.Clock
@Inject
class MangaRestorer( class MangaRestorer(
private val database: Database = Injekt.get(), private val database: Database,
private val getCategories: GetCategories = Injekt.get(), private val getCategories: GetCategories,
private val getMangaByUrlAndSourceId: GetMangaByUrlAndSourceId = Injekt.get(), private val getMangaByUrlAndSourceId: GetMangaByUrlAndSourceId,
private val getChaptersByMangaId: GetChaptersByMangaId = Injekt.get(), private val getChaptersByMangaId: GetChaptersByMangaId,
private val updateManga: UpdateManga = Injekt.get(), private val updateManga: UpdateManga,
private val getTracks: GetTracks = Injekt.get(), private val getTracks: GetTracks,
private val insertTrack: InsertTrack = Injekt.get(), private val insertTrack: InsertTrack,
fetchInterval: FetchInterval = Injekt.get(), fetchInterval: FetchInterval,
) { ) {
private val timeZone = TimeZone.currentSystemDefault() private val timeZone = TimeZone.currentSystemDefault()
@@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.backup.restore.restorers
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
import eu.kanade.tachiyomi.data.backup.models.BackupCategory import eu.kanade.tachiyomi.data.backup.models.BackupCategory
import eu.kanade.tachiyomi.data.backup.models.BackupPreference import eu.kanade.tachiyomi.data.backup.models.BackupPreference
@@ -21,13 +22,12 @@ import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.model.Category
import tachiyomi.domain.download.service.DownloadPreferences import tachiyomi.domain.download.service.DownloadPreferences
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@Inject
class PreferenceRestorer( class PreferenceRestorer(
private val context: Context, private val context: Context,
private val getCategories: GetCategories = Injekt.get(), private val getCategories: GetCategories,
private val preferenceStore: PreferenceStore = Injekt.get(), private val preferenceStore: PreferenceStore,
) { ) {
suspend fun restoreApp( suspend fun restoreApp(
preferences: List<BackupPreference>, preferences: List<BackupPreference>,
@@ -45,7 +45,7 @@ class PreferenceRestorer(
suspend fun restoreSource(preferences: List<BackupSourcePreferences>) { suspend fun restoreSource(preferences: List<BackupSourcePreferences>) {
preferences.forEach { preferences.forEach {
val sourcePrefs = AndroidPreferenceStore(context, sourcePreferences(it.sourceKey)) val sourcePrefs = AndroidPreferenceStore(sourcePreferences(it.sourceKey))
restorePreferences(it.prefs, sourcePrefs) restorePreferences(it.prefs, sourcePrefs)
} }
} }
@@ -3,6 +3,9 @@ package eu.kanade.tachiyomi.data.cache
import android.content.Context import android.content.Context
import android.text.format.Formatter import android.text.format.Formatter
import com.jakewharton.disklrucache.DiskLruCache import com.jakewharton.disklrucache.DiskLruCache
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.SingleIn
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.storage.saveTo import eu.kanade.tachiyomi.util.storage.saveTo
@@ -24,6 +27,8 @@ import java.io.IOException
* *
* @param context the application context. * @param context the application context.
*/ */
@Inject
@SingleIn(AppScope::class)
class ChapterCache( class ChapterCache(
private val context: Context, private val context: Context,
private val json: Json, private val json: Json,
@@ -1,6 +1,9 @@
package eu.kanade.tachiyomi.data.cache package eu.kanade.tachiyomi.data.cache
import android.content.Context import android.content.Context
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.SingleIn
import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.DiskUtil
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import java.io.File import java.io.File
@@ -15,6 +18,8 @@ import java.io.InputStream
* @param context the application context. * @param context the application context.
* @constructor creates an instance of the cover cache. * @constructor creates an instance of the cover cache.
*/ */
@Inject
@SingleIn(AppScope::class)
class CoverCache(private val context: Context) { class CoverCache(private val context: Context) {
companion object { companion object {
@@ -31,7 +31,6 @@ import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.MangaCover import tachiyomi.domain.manga.model.MangaCover
import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.service.SourceManager
import uy.kohesive.injekt.injectLazy
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
@@ -299,11 +298,10 @@ class MangaCoverFetcher(
class MangaFactory( class MangaFactory(
private val callFactoryLazy: Lazy<Call.Factory>, private val callFactoryLazy: Lazy<Call.Factory>,
private val coverCache: CoverCache,
private val sourceManager: SourceManager,
) : Fetcher.Factory<Manga> { ) : Fetcher.Factory<Manga> {
private val coverCache: CoverCache by injectLazy()
private val sourceManager: SourceManager by injectLazy()
override fun create(data: Manga, options: Options, imageLoader: ImageLoader): Fetcher { override fun create(data: Manga, options: Options, imageLoader: ImageLoader): Fetcher {
return MangaCoverFetcher( return MangaCoverFetcher(
url = data.thumbnailUrl, url = data.thumbnailUrl,
@@ -321,11 +319,10 @@ class MangaCoverFetcher(
class MangaCoverFactory( class MangaCoverFactory(
private val callFactoryLazy: Lazy<Call.Factory>, private val callFactoryLazy: Lazy<Call.Factory>,
private val coverCache: CoverCache,
private val sourceManager: SourceManager,
) : Fetcher.Factory<MangaCover> { ) : Fetcher.Factory<MangaCover> {
private val coverCache: CoverCache by injectLazy()
private val sourceManager: SourceManager by injectLazy()
override fun create(data: MangaCover, options: Options, imageLoader: ImageLoader): Fetcher { override fun create(data: MangaCover, options: Options, imageLoader: ImageLoader): Fetcher {
return MangaCoverFetcher( return MangaCoverFetcher(
url = data.url, url = data.url,
@@ -5,8 +5,6 @@ import coil3.request.Options
import eu.kanade.domain.manga.model.hasCustomCover import eu.kanade.domain.manga.model.hasCustomCover
import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.cache.CoverCache
import tachiyomi.domain.manga.model.MangaCover import tachiyomi.domain.manga.model.MangaCover
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import tachiyomi.domain.manga.model.Manga as DomainManga import tachiyomi.domain.manga.model.Manga as DomainManga
class MangaKeyer : Keyer<DomainManga> { class MangaKeyer : Keyer<DomainManga> {
@@ -20,7 +18,7 @@ class MangaKeyer : Keyer<DomainManga> {
} }
class MangaCoverKeyer( class MangaCoverKeyer(
private val coverCache: CoverCache = Injekt.get(), private val coverCache: CoverCache,
) : Keyer<MangaCover> { ) : Keyer<MangaCover> {
override fun key(data: MangaCover, options: Options): String { override fun key(data: MangaCover, options: Options): String {
return if (coverCache.getCustomCoverFile(data.mangaId).exists()) { return if (coverCache.getCustomCoverFile(data.mangaId).exists()) {
@@ -1,9 +1,11 @@
package eu.kanade.tachiyomi.data.download package eu.kanade.tachiyomi.data.download
import android.app.Application
import android.content.Context import android.content.Context
import androidx.core.net.toUri import androidx.core.net.toUri
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.SingleIn
import eu.kanade.tachiyomi.extension.ExtensionManager import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
@@ -61,12 +63,14 @@ import kotlin.time.Duration.Companion.seconds
* defined in [renewInterval] as we don't have any control over the filesystem and the user can * defined in [renewInterval] as we don't have any control over the filesystem and the user can
* delete the folders at any time without the app noticing. * delete the folders at any time without the app noticing.
*/ */
@Inject
@SingleIn(AppScope::class)
class DownloadCache( class DownloadCache(
private val context: Context, private val context: Context,
private val provider: DownloadProvider = Injekt.get(), private val provider: DownloadProvider,
private val sourceManager: SourceManager = Injekt.get(), private val sourceManager: SourceManager,
private val extensionManager: ExtensionManager = Injekt.get(), private val extensionManager: ExtensionManager,
private val storageManager: StorageManager = Injekt.get(), private val storageManager: StorageManager,
) { ) {
private val scope = CoroutineScope(Dispatchers.IO) private val scope = CoroutineScope(Dispatchers.IO)
@@ -492,7 +496,7 @@ private object UniFileAsStringSerializer : KSerializer<UniFile?> {
override fun deserialize(decoder: Decoder): UniFile? { override fun deserialize(decoder: Decoder): UniFile? {
return if (decoder.decodeNotNullMark()) { return if (decoder.decodeNotNullMark()) {
UniFile.fromUri(Injekt.get<Application>(), decoder.decodeString().toUri()) UniFile.fromUri(Injekt.get<Context>(), decoder.decodeString().toUri())
} else { } else {
decoder.decodeNull() decoder.decodeNull()
} }
@@ -11,6 +11,7 @@ import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkInfo import androidx.work.WorkInfo
import androidx.work.WorkManager import androidx.work.WorkManager
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.system.NetworkState import eu.kanade.tachiyomi.util.system.NetworkState
@@ -24,9 +25,9 @@ import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import mihon.app.di.AppGraph
import mihon.core.metro.metroGraph
import tachiyomi.domain.download.service.DownloadPreferences import tachiyomi.domain.download.service.DownloadPreferences
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/** /**
* This worker is used to manage the downloader. The system can decide to stop the worker, in * This worker is used to manage the downloader. The system can decide to stop the worker, in
@@ -34,8 +35,15 @@ import uy.kohesive.injekt.api.get
*/ */
class DownloadJob(context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) { class DownloadJob(context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) {
private val downloadManager: DownloadManager = Injekt.get() private val graph: AppGraph = context.metroGraph()
private val downloadPreferences: DownloadPreferences = Injekt.get()
@Inject private lateinit var downloadManager: DownloadManager
@Inject private lateinit var downloadPreferences: DownloadPreferences
init {
graph.inject(this)
}
override suspend fun getForegroundInfo(): ForegroundInfo { override suspend fun getForegroundInfo(): ForegroundInfo {
val notification = applicationContext.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_PROGRESS) { val notification = applicationContext.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_PROGRESS) {
@@ -1,9 +1,13 @@
package eu.kanade.tachiyomi.data.download package eu.kanade.tachiyomi.data.download
import android.content.Context import android.content.Context
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.SingleIn
import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.online.HttpSource
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.drop
@@ -20,41 +24,37 @@ import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.ImageUtil
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.chapter.interactor.GetChapter
import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.download.service.DownloadPreferences import tachiyomi.domain.download.service.DownloadPreferences
import tachiyomi.domain.manga.interactor.GetManga
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.service.SourceManager
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/** /**
* This class is used to manage chapter downloads in the application. It must be instantiated once * This class is used to manage chapter downloads in the application. It must be instantiated once
* and retrieved through dependency injection. You can use this class to queue new chapters or query * and retrieved through dependency injection. You can use this class to queue new chapters or query
* downloaded chapters. * downloaded chapters.
*/ */
@Inject
@SingleIn(AppScope::class)
class DownloadManager( class DownloadManager(
private val context: Context, private val context: Context,
private val provider: DownloadProvider = Injekt.get(), private val provider: DownloadProvider,
private val cache: DownloadCache = Injekt.get(), private val cache: DownloadCache,
private val getCategories: GetCategories = Injekt.get(), private val getCategories: GetCategories,
private val sourceManager: SourceManager = Injekt.get(), private val getManga: GetManga,
private val downloadPreferences: DownloadPreferences = Injekt.get(), private val getChapter: GetChapter,
private val sourceManager: SourceManager,
private val downloadPreferences: DownloadPreferences,
private val downloader: Downloader,
private val pendingDeleter: DownloadPendingDeleter,
) { ) {
/**
* Downloader whose only task is to download chapters.
*/
private val downloader = Downloader(context, provider, cache)
val isRunning: Boolean val isRunning: Boolean
get() = downloader.isRunning get() = downloader.isRunning
/**
* Queue to delay the deletion of a list of chapters until triggered.
*/
private val pendingDeleter = DownloadPendingDeleter(context)
val queueState val queueState
get() = downloader.queueState get() = downloader.queueState
@@ -107,7 +107,7 @@ class DownloadManager(
fun startDownloadNow(chapterId: Long) { fun startDownloadNow(chapterId: Long) {
val existingDownload = getQueuedDownloadOrNull(chapterId) val existingDownload = getQueuedDownloadOrNull(chapterId)
// If not in queue try to start a new download // If not in queue try to start a new download
val toAdd = existingDownload ?: runBlocking { Download.fromChapterId(chapterId) } ?: return val toAdd = existingDownload ?: runBlocking { downloadFromChapterId(chapterId) } ?: return
queueState.value.toMutableList().apply { queueState.value.toMutableList().apply {
existingDownload?.let { remove(it) } existingDownload?.let { remove(it) }
add(0, toAdd) add(0, toAdd)
@@ -116,6 +116,14 @@ class DownloadManager(
startDownloads() startDownloads()
} }
private suspend fun downloadFromChapterId(chapterId: Long): Download? {
val chapter = getChapter.await(chapterId) ?: return null
val manga = getManga.await(chapter.mangaId) ?: return null
val source = sourceManager.get(manga.source) as? HttpSource ?: return null
return Download(source, manga, chapter)
}
/** /**
* Reorders the download queue. * Reorders the download queue.
* *
@@ -4,6 +4,7 @@ import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.core.security.SecurityPreferences import eu.kanade.tachiyomi.core.security.SecurityPreferences
import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.Download
@@ -16,7 +17,6 @@ import eu.kanade.tachiyomi.util.system.notificationBuilder
import eu.kanade.tachiyomi.util.system.notify import eu.kanade.tachiyomi.util.system.notify
import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.i18n.stringResource
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import uy.kohesive.injekt.injectLazy
import java.util.regex.Pattern import java.util.regex.Pattern
/** /**
@@ -24,9 +24,11 @@ import java.util.regex.Pattern
* *
* @param context context of application * @param context context of application
*/ */
internal class DownloadNotifier(private val context: Context) { @Inject
class DownloadNotifier(
private val preferences: SecurityPreferences by injectLazy() private val context: Context,
private val preferences: SecurityPreferences,
) {
private val progressNotificationBuilder by lazy { private val progressNotificationBuilder by lazy {
context.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_PROGRESS) { context.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_PROGRESS) {
@@ -2,21 +2,24 @@ package eu.kanade.tachiyomi.data.download
import android.content.Context import android.content.Context
import androidx.core.content.edit import androidx.core.content.edit
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.SingleIn
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/** /**
* Class used to keep a list of chapters for future deletion. * Class used to keep a list of chapters for future deletion.
* *
* @param context the application context. * @param context the application context.
*/ */
@Inject
@SingleIn(AppScope::class)
class DownloadPendingDeleter( class DownloadPendingDeleter(
context: Context, context: Context,
private val json: Json = Injekt.get(), private val json: Json,
) { ) {
/** /**
@@ -2,6 +2,9 @@ package eu.kanade.tachiyomi.data.download
import android.content.Context import android.content.Context
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.SingleIn
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.util.lang.Hash.md5 import eu.kanade.tachiyomi.util.lang.Hash.md5
import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.DiskUtil
@@ -14,8 +17,6 @@ import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.storage.service.StorageManager import tachiyomi.domain.storage.service.StorageManager
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.IOException import java.io.IOException
/** /**
@@ -24,10 +25,12 @@ import java.io.IOException
* *
* @param context the application context. * @param context the application context.
*/ */
@Inject
@SingleIn(AppScope::class)
class DownloadProvider( class DownloadProvider(
private val context: Context, private val context: Context,
private val storageManager: StorageManager = Injekt.get(), private val storageManager: StorageManager,
private val libraryPreferences: LibraryPreferences = Injekt.get(), private val libraryPreferences: LibraryPreferences,
) { ) {
private val downloadsDir: UniFile? private val downloadsDir: UniFile?
@@ -2,6 +2,9 @@ package eu.kanade.tachiyomi.data.download
import android.content.Context import android.content.Context
import androidx.core.content.edit import androidx.core.content.edit
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.SingleIn
import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
@@ -11,18 +14,18 @@ import tachiyomi.domain.chapter.interactor.GetChapter
import tachiyomi.domain.manga.interactor.GetManga import tachiyomi.domain.manga.interactor.GetManga
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.service.SourceManager
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/** /**
* This class is used to persist active downloads across application restarts. * This class is used to persist active downloads across application restarts.
*/ */
@Inject
@SingleIn(AppScope::class)
class DownloadStore( class DownloadStore(
context: Context, context: Context,
private val sourceManager: SourceManager = Injekt.get(), private val sourceManager: SourceManager,
private val json: Json = Injekt.get(), private val json: Json,
private val getManga: GetManga = Injekt.get(), private val getManga: GetManga,
private val getChapter: GetChapter = Injekt.get(), private val getChapter: GetChapter,
) { ) {
/** /**
@@ -2,6 +2,9 @@ package eu.kanade.tachiyomi.data.download
import android.content.Context import android.content.Context
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.SingleIn
import eu.kanade.domain.chapter.model.toSChapter import eu.kanade.domain.chapter.model.toSChapter
import eu.kanade.domain.manga.model.getComicInfo import eu.kanade.domain.manga.model.getComicInfo
import eu.kanade.tachiyomi.data.cache.ChapterCache import eu.kanade.tachiyomi.data.cache.ChapterCache
@@ -57,8 +60,6 @@ import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.service.SourceManager
import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.domain.track.interactor.GetTracks
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File import java.io.File
import java.util.Locale import java.util.Locale
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
@@ -68,34 +69,27 @@ import kotlin.time.Duration.Companion.seconds
* *
* Its queue contains the list of chapters to download. * Its queue contains the list of chapters to download.
*/ */
@Inject
@SingleIn(AppScope::class)
class Downloader( class Downloader(
private val context: Context, private val context: Context,
private val provider: DownloadProvider, private val provider: DownloadProvider,
private val cache: DownloadCache, private val cache: DownloadCache,
private val sourceManager: SourceManager = Injekt.get(), private val sourceManager: SourceManager,
private val chapterCache: ChapterCache = Injekt.get(), private val chapterCache: ChapterCache,
private val downloadPreferences: DownloadPreferences = Injekt.get(), private val downloadPreferences: DownloadPreferences,
private val xml: XML = Injekt.get(), private val xml: XML,
private val getCategories: GetCategories = Injekt.get(), private val getCategories: GetCategories,
private val getTracks: GetTracks = Injekt.get(), private val getTracks: GetTracks,
private val store: DownloadStore,
private val notifier: DownloadNotifier,
) { ) {
/**
* Store for persisting downloads across restarts.
*/
private val store = DownloadStore(context)
/** /**
* Queue where active downloads are kept. * Queue where active downloads are kept.
*/ */
private val _queueState = MutableStateFlow<List<Download>>(emptyList()) private val _queueState = MutableStateFlow<List<Download>>(emptyList())
val queueState = _queueState.asStateFlow() val queueState = _queueState.asStateFlow()
/**
* Notifier for the downloader state and progress.
*/
private val notifier by lazy { DownloadNotifier(context) }
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private var downloaderJob: Job? = null private var downloaderJob: Job? = null
@@ -10,13 +10,8 @@ import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import tachiyomi.domain.chapter.interactor.GetChapter
import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.manga.interactor.GetManga
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.source.service.SourceManager
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
data class Download( data class Download(
@@ -71,19 +66,4 @@ data class Download(
DOWNLOADED(3), DOWNLOADED(3),
ERROR(4), ERROR(4),
} }
companion object {
suspend fun fromChapterId(
chapterId: Long,
getChapter: GetChapter = Injekt.get(),
getManga: GetManga = Injekt.get(),
sourceManager: SourceManager = Injekt.get(),
): Download? {
val chapter = getChapter.await(chapterId) ?: return null
val manga = getManga.await(chapter.mangaId) ?: return null
val source = sourceManager.get(manga.source) as? HttpSource ?: return null
return Download(source, manga, chapter)
}
}
} }
@@ -15,9 +15,11 @@ import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.PeriodicWorkRequestBuilder import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkInfo import androidx.work.WorkInfo
import androidx.work.WorkManager
import androidx.work.WorkQuery import androidx.work.WorkQuery
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import androidx.work.workDataOf import androidx.work.workDataOf
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
@@ -38,6 +40,9 @@ import kotlinx.coroutines.sync.withPermit
import kotlinx.datetime.TimeZone import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime import kotlinx.datetime.toLocalDateTime
import logcat.LogPriority import logcat.LogPriority
import mihon.app.di.AppGraph
import mihon.app.di.appGraph
import mihon.core.metro.metroGraph
import mihon.domain.chapter.interactor.FilterChaptersForDownload import mihon.domain.chapter.interactor.FilterChaptersForDownload
import mihon.domain.source.interactor.UpdateMangaFromRemote import mihon.domain.source.interactor.UpdateMangaFromRemote
import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.i18n.stringResource
@@ -63,8 +68,6 @@ import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.source.model.SourceNotInstalledException import tachiyomi.domain.source.model.SourceNotInstalledException
import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.service.SourceManager
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File import java.io.File
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@@ -78,24 +81,34 @@ import kotlin.time.Clock
class LibraryUpdateJob(private val context: Context, workerParams: WorkerParameters) : class LibraryUpdateJob(private val context: Context, workerParams: WorkerParameters) :
CoroutineWorker(context, workerParams) { CoroutineWorker(context, workerParams) {
private val sourceManager: SourceManager = Injekt.get() private val graph: AppGraph = context.metroGraph()
private val libraryPreferences: LibraryPreferences = Injekt.get()
private val downloadManager: DownloadManager = Injekt.get()
private val getLibraryManga: GetLibraryManga = Injekt.get()
private val getManga: GetManga = Injekt.get()
private val fetchInterval: FetchInterval = Injekt.get()
private val filterChaptersForDownload: FilterChaptersForDownload = Injekt.get()
private val updateMangaFromRemote: UpdateMangaFromRemote = Injekt.get()
private val notifier = LibraryUpdateNotifier(context) @Inject private lateinit var sourceManager: SourceManager
@Inject private lateinit var libraryPreferences: LibraryPreferences
@Inject private lateinit var downloadManager: DownloadManager
@Inject private lateinit var getLibraryManga: GetLibraryManga
@Inject private lateinit var getManga: GetManga
@Inject private lateinit var fetchInterval: FetchInterval
@Inject private lateinit var filterChaptersForDownload: FilterChaptersForDownload
@Inject private lateinit var updateMangaFromRemote: UpdateMangaFromRemote
@Inject private lateinit var notifier: LibraryUpdateNotifier
private var mangaToUpdate: List<LibraryManga> = mutableListOf() private var mangaToUpdate: List<LibraryManga> = mutableListOf()
override suspend fun doWork(): Result { override suspend fun doWork(): Result {
graph.inject(this)
if (tags.contains(WORK_NAME_AUTO)) { if (tags.contains(WORK_NAME_AUTO)) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
val preferences = Injekt.get<LibraryPreferences>() val restrictions = libraryPreferences.autoUpdateDeviceRestrictions.get()
val restrictions = preferences.autoUpdateDeviceRestrictions.get()
if ((DEVICE_ONLY_ON_WIFI in restrictions) && !context.isConnectedToWifi()) { if ((DEVICE_ONLY_ON_WIFI in restrictions) && !context.isConnectedToWifi()) {
return Result.retry() return Result.retry()
} }
@@ -133,7 +146,6 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
} }
override suspend fun getForegroundInfo(): ForegroundInfo { override suspend fun getForegroundInfo(): ForegroundInfo {
val notifier = LibraryUpdateNotifier(context)
return ForegroundInfo( return ForegroundInfo(
Notifications.ID_LIBRARY_PROGRESS, Notifications.ID_LIBRARY_PROGRESS,
notifier.progressNotificationBuilder.build(), notifier.progressNotificationBuilder.build(),
@@ -420,7 +432,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
context: Context, context: Context,
prefInterval: Int? = null, prefInterval: Int? = null,
) { ) {
val preferences = Injekt.get<LibraryPreferences>() val preferences = context.appGraph.libraryPreferences
val interval = prefInterval ?: preferences.autoUpdateInterval.get() val interval = prefInterval ?: preferences.autoUpdateInterval.get()
if (interval > 0) { if (interval > 0) {
val restrictions = preferences.autoUpdateDeviceRestrictions.get() val restrictions = preferences.autoUpdateDeviceRestrictions.get()
@@ -469,11 +481,10 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
} }
fun startNow( fun startNow(
context: Context, workManager: WorkManager,
category: Category? = null, category: Category? = null,
): Boolean { ): Boolean {
val wm = context.workManager if (workManager.isRunning(TAG)) {
if (wm.isRunning(TAG)) {
// Already running either as a scheduled or manual job // Already running either as a scheduled or manual job
return false return false
} }
@@ -486,20 +497,20 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
.addTag(WORK_NAME_MANUAL) .addTag(WORK_NAME_MANUAL)
.setInputData(inputData) .setInputData(inputData)
.build() .build()
wm.enqueueUniqueWork(WORK_NAME_MANUAL, ExistingWorkPolicy.KEEP, request) workManager.enqueueUniqueWork(WORK_NAME_MANUAL, ExistingWorkPolicy.KEEP, request)
return true return true
} }
fun stop(context: Context) { fun stop(context: Context) {
val wm = context.workManager val workManager = context.workManager
val workQuery = WorkQuery.Builder.fromTags(listOf(TAG)) val workQuery = WorkQuery.Builder.fromTags(listOf(TAG))
.addStates(listOf(WorkInfo.State.RUNNING)) .addStates(listOf(WorkInfo.State.RUNNING))
.build() .build()
wm.getWorkInfos(workQuery).get() workManager.getWorkInfos(workQuery).get()
// Should only return one work but just in case // Should only return one work but just in case
.forEach { .forEach {
wm.cancelWorkById(it.id) workManager.cancelWorkById(it.id)
// Re-enqueue cancelled scheduled work // Re-enqueue cancelled scheduled work
if (it.tags.contains(WORK_NAME_AUTO)) { if (it.tags.contains(WORK_NAME_AUTO)) {
@@ -14,6 +14,7 @@ import coil3.imageLoader
import coil3.request.ImageRequest import coil3.request.ImageRequest
import coil3.request.transformations import coil3.request.transformations
import coil3.transform.CircleCropTransformation import coil3.transform.CircleCropTransformation
import dev.zacsweers.metro.Inject
import eu.kanade.presentation.util.formatChapterNumber import eu.kanade.presentation.util.formatChapterNumber
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.core.security.SecurityPreferences import eu.kanade.tachiyomi.core.security.SecurityPreferences
@@ -37,15 +38,14 @@ import tachiyomi.domain.library.model.LibraryManga
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.service.SourceManager
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.math.RoundingMode import java.math.RoundingMode
import java.text.NumberFormat import java.text.NumberFormat
@Inject
class LibraryUpdateNotifier( class LibraryUpdateNotifier(
private val context: Context, private val context: Context,
private val securityPreferences: SecurityPreferences = Injekt.get(), private val securityPreferences: SecurityPreferences,
private val sourceManager: SourceManager = Injekt.get(), private val sourceManager: SourceManager,
) { ) {
private val percentFormatter = NumberFormat.getPercentInstance().apply { private val percentFormatter = NumberFormat.getPercentInstance().apply {
@@ -7,13 +7,12 @@ import androidx.work.CoroutineWorker
import androidx.work.ExistingWorkPolicy import androidx.work.ExistingWorkPolicy
import androidx.work.ForegroundInfo import androidx.work.ForegroundInfo
import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkInfo import androidx.work.WorkManager
import androidx.work.WorkQuery
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.system.isRunning import eu.kanade.tachiyomi.util.system.isRunning
import eu.kanade.tachiyomi.util.system.setForegroundSafely import eu.kanade.tachiyomi.util.system.setForegroundSafely
import eu.kanade.tachiyomi.util.system.workManager
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
@@ -22,6 +21,8 @@ import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit import kotlinx.coroutines.sync.withPermit
import logcat.LogPriority import logcat.LogPriority
import mihon.app.di.AppGraph
import mihon.core.metro.metroGraph
import mihon.domain.source.interactor.UpdateMangaFromRemote import mihon.domain.source.interactor.UpdateMangaFromRemote
import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.lang.withIOContext
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
@@ -29,8 +30,6 @@ import tachiyomi.domain.library.model.LibraryManga
import tachiyomi.domain.manga.interactor.GetLibraryManga import tachiyomi.domain.manga.interactor.GetLibraryManga
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.service.SourceManager
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
import kotlin.concurrent.atomics.AtomicInt import kotlin.concurrent.atomics.AtomicInt
import kotlin.concurrent.atomics.ExperimentalAtomicApi import kotlin.concurrent.atomics.ExperimentalAtomicApi
@@ -40,15 +39,21 @@ import kotlin.concurrent.atomics.fetchAndIncrement
class MetadataUpdateJob(private val context: Context, workerParams: WorkerParameters) : class MetadataUpdateJob(private val context: Context, workerParams: WorkerParameters) :
CoroutineWorker(context, workerParams) { CoroutineWorker(context, workerParams) {
private val sourceManager: SourceManager = Injekt.get() private val graph: AppGraph = context.metroGraph()
private val getLibraryManga: GetLibraryManga = Injekt.get()
private val updateMangaFromRemote: UpdateMangaFromRemote = Injekt.get()
private val notifier = LibraryUpdateNotifier(context) @Inject private lateinit var sourceManager: SourceManager
@Inject private lateinit var getLibraryManga: GetLibraryManga
@Inject private lateinit var updateMangaFromRemote: UpdateMangaFromRemote
@Inject private lateinit var notifier: LibraryUpdateNotifier
private var mangaToUpdate: List<LibraryManga> = mutableListOf() private var mangaToUpdate: List<LibraryManga> = mutableListOf()
override suspend fun doWork(): Result { override suspend fun doWork(): Result {
graph.inject(this)
setForegroundSafely() setForegroundSafely()
addMangaToQueue() addMangaToQueue()
@@ -72,7 +77,6 @@ class MetadataUpdateJob(private val context: Context, workerParams: WorkerParame
} }
override suspend fun getForegroundInfo(): ForegroundInfo { override suspend fun getForegroundInfo(): ForegroundInfo {
val notifier = LibraryUpdateNotifier(context)
return ForegroundInfo( return ForegroundInfo(
Notifications.ID_LIBRARY_PROGRESS, Notifications.ID_LIBRARY_PROGRESS,
notifier.progressNotificationBuilder.build(), notifier.progressNotificationBuilder.build(),
@@ -168,9 +172,8 @@ class MetadataUpdateJob(private val context: Context, workerParams: WorkerParame
private const val MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD = 60 private const val MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD = 60
fun startNow(context: Context): Boolean { fun startNow(workManager: WorkManager): Boolean {
val wm = context.workManager if (workManager.isRunning(TAG)) {
if (wm.isRunning(TAG)) {
// Already running either as a scheduled or manual job // Already running either as a scheduled or manual job
return false return false
} }
@@ -178,21 +181,9 @@ class MetadataUpdateJob(private val context: Context, workerParams: WorkerParame
.addTag(TAG) .addTag(TAG)
.addTag(WORK_NAME_MANUAL) .addTag(WORK_NAME_MANUAL)
.build() .build()
wm.enqueueUniqueWork(WORK_NAME_MANUAL, ExistingWorkPolicy.KEEP, request) workManager.enqueueUniqueWork(WORK_NAME_MANUAL, ExistingWorkPolicy.KEEP, request)
return true return true
} }
fun stop(context: Context) {
val wm = context.workManager
val workQuery = WorkQuery.Builder.fromTags(listOf(TAG))
.addStates(listOf(WorkInfo.State.RUNNING))
.build()
wm.getWorkInfos(workQuery).get()
// Should only return one work but just in case
.forEach {
wm.cancelWorkById(it.id)
}
}
} }
} }
@@ -6,6 +6,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import androidx.core.net.toUri import androidx.core.net.toUri
import dev.zacsweers.metro.Inject
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
@@ -16,7 +17,9 @@ import eu.kanade.tachiyomi.util.system.getParcelableExtraCompat
import eu.kanade.tachiyomi.util.system.notificationManager import eu.kanade.tachiyomi.util.system.notificationManager
import eu.kanade.tachiyomi.util.system.toShareIntent import eu.kanade.tachiyomi.util.system.toShareIntent
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.system.workManager
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import mihon.app.di.appGraph
import tachiyomi.core.common.Constants import tachiyomi.core.common.Constants
import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.domain.chapter.interactor.GetChapter import tachiyomi.domain.chapter.interactor.GetChapter
@@ -28,9 +31,6 @@ import tachiyomi.domain.manga.interactor.GetManga
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.service.SourceManager
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
/** /**
@@ -40,12 +40,21 @@ import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
*/ */
class NotificationReceiver : BroadcastReceiver() { class NotificationReceiver : BroadcastReceiver() {
private val getManga: GetManga by injectLazy() @Inject private lateinit var getManga: GetManga
private val getChapter: GetChapter by injectLazy()
private val updateChapter: UpdateChapter by injectLazy() @Inject private lateinit var getChapter: GetChapter
private val downloadManager: DownloadManager by injectLazy()
@Inject private lateinit var updateChapter: UpdateChapter
@Inject private lateinit var downloadManager: DownloadManager
@Inject private lateinit var downloadPreferences: DownloadPreferences
@Inject private lateinit var sourceManager: SourceManager
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
context.appGraph.inject(this)
when (intent.action) { when (intent.action) {
// Dismiss notification // Dismiss notification
ACTION_DISMISS_NOTIFICATION -> dismissNotification(context, intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)) ACTION_DISMISS_NOTIFICATION -> dismissNotification(context, intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1))
@@ -161,7 +170,7 @@ class NotificationReceiver : BroadcastReceiver() {
* @param context context of application * @param context context of application
*/ */
private fun cancelRestore(context: Context) { private fun cancelRestore(context: Context) {
BackupRestoreJob.stop(context) BackupRestoreJob.stop(context.workManager)
} }
/** /**
@@ -180,9 +189,6 @@ class NotificationReceiver : BroadcastReceiver() {
* @param mangaId id of manga * @param mangaId id of manga
*/ */
private fun markAsRead(chapterUrls: Array<String>, mangaId: Long) { private fun markAsRead(chapterUrls: Array<String>, mangaId: Long) {
val downloadPreferences: DownloadPreferences = Injekt.get()
val sourceManager: SourceManager = Injekt.get()
launchIO { launchIO {
val toUpdate = chapterUrls.mapNotNull { getChapter.await(it, mangaId) } val toUpdate = chapterUrls.mapNotNull { getChapter.await(it, mangaId) }
.map { .map {
@@ -11,6 +11,9 @@ import android.webkit.MimeTypeMap
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.content.contentValuesOf import androidx.core.content.contentValuesOf
import androidx.core.net.toUri import androidx.core.net.toUri
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.SingleIn
import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.storage.cacheImageDir import eu.kanade.tachiyomi.util.storage.cacheImageDir
import eu.kanade.tachiyomi.util.storage.getUriCompat import eu.kanade.tachiyomi.util.storage.getUriCompat
@@ -26,6 +29,8 @@ import java.io.File
import java.io.InputStream import java.io.InputStream
import kotlin.time.Clock import kotlin.time.Clock
@Inject
@SingleIn(AppScope::class)
class ImageSaver( class ImageSaver(
val context: Context, val context: Context,
) { ) {

Some files were not shown because too many files have changed in this diff Show More