diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 511cc05f9..8f3cc96e1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -12,6 +12,7 @@ plugins { alias(mihonx.plugins.compose) alias(mihonx.plugins.spotless) + alias(libs.plugins.metro) alias(libs.plugins.aboutLibraries) alias(libs.plugins.androidx.baselineProfile) alias(libs.plugins.kotlin.serialization) @@ -209,6 +210,7 @@ dependencies { implementation(projects.i18n) implementation(projects.core.archive) implementation(projects.core.common) + implementation(projects.core.metro) implementation(projects.coreMetadata) implementation(projects.sourceApi) implementation(projects.sourceLocal) @@ -283,6 +285,9 @@ dependencies { // Dependency injection implementation(libs.injekt) + implementation(libs.metro.runtime) + implementation(libs.metrox.viewmodel) + implementation(libs.metrox.viewmodel.compose) // Image loading implementation(libs.bundles.coil) diff --git a/app/src/main/java/eu/kanade/core/util/SourceUtil.kt b/app/src/main/java/eu/kanade/core/util/SourceUtil.kt index c976e1e6a..9c7a0f273 100644 --- a/app/src/main/java/eu/kanade/core/util/SourceUtil.kt +++ b/app/src/main/java/eu/kanade/core/util/SourceUtil.kt @@ -3,11 +3,11 @@ package eu.kanade.core.util import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember -import tachiyomi.domain.source.service.SourceManager -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +import androidx.compose.ui.platform.LocalContext +import mihon.app.di.appGraph @Composable fun ifSourcesLoaded(): Boolean { - return remember { Injekt.get().isInitialized }.collectAsState().value + val context = LocalContext.current + return remember { context.appGraph.sourceManager.isInitialized }.collectAsState().value } diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt deleted file mode 100644 index 729c86edd..000000000 --- a/app/src/main/java/eu/kanade/domain/DomainModule.kt +++ /dev/null @@ -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 { 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 { 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 { ReleaseServiceImpl(get(), get()) } - addFactory { GetApplicationRelease(get()) } - - addSingletonFactory { 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 { 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 { 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 { UpdatesRepositoryImpl(get()) } - addFactory { GetUpdates(get()) } - - addSingletonFactory { SourceRepositoryImpl(get(), get()) } - addSingletonFactory { 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 { 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()) } - } -} diff --git a/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt b/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt index ecaf9e811..79b42a1e9 100644 --- a/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt +++ b/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt @@ -2,10 +2,15 @@ package eu.kanade.domain.base import android.content.Context 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.PreferenceStore import tachiyomi.i18n.MR +@Inject +@SingleIn(AppScope::class) class BasePreferences( val context: Context, preferenceStore: PreferenceStore, diff --git a/app/src/main/java/eu/kanade/domain/chapter/interactor/GetAvailableScanlators.kt b/app/src/main/java/eu/kanade/domain/chapter/interactor/GetAvailableScanlators.kt index 13bd35e1f..5b1ef0082 100644 --- a/app/src/main/java/eu/kanade/domain/chapter/interactor/GetAvailableScanlators.kt +++ b/app/src/main/java/eu/kanade/domain/chapter/interactor/GetAvailableScanlators.kt @@ -1,9 +1,11 @@ package eu.kanade.domain.chapter.interactor +import dev.zacsweers.metro.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import tachiyomi.domain.chapter.repository.ChapterRepository +@Inject class GetAvailableScanlators( private val repository: ChapterRepository, ) { diff --git a/app/src/main/java/eu/kanade/domain/chapter/interactor/SetReadStatus.kt b/app/src/main/java/eu/kanade/domain/chapter/interactor/SetReadStatus.kt index 7bf1eecb3..1ecf3e5ba 100644 --- a/app/src/main/java/eu/kanade/domain/chapter/interactor/SetReadStatus.kt +++ b/app/src/main/java/eu/kanade/domain/chapter/interactor/SetReadStatus.kt @@ -1,5 +1,6 @@ package eu.kanade.domain.chapter.interactor +import dev.zacsweers.metro.Inject import eu.kanade.domain.download.interactor.DeleteDownload import logcat.LogPriority 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.repository.MangaRepository +@Inject class SetReadStatus( private val downloadPreferences: DownloadPreferences, private val deleteDownload: DeleteDownload, diff --git a/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt b/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt index f094e076a..9231cbac4 100644 --- a/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt +++ b/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt @@ -1,5 +1,6 @@ package eu.kanade.domain.chapter.interactor +import dev.zacsweers.metro.Inject import eu.kanade.domain.chapter.model.copyFromSChapter import eu.kanade.domain.chapter.model.toSChapter import eu.kanade.domain.manga.interactor.GetExcludedScanlators @@ -29,6 +30,7 @@ import java.lang.Long.max import java.util.TreeSet import kotlin.time.Clock +@Inject class SyncChaptersWithSource( private val downloadManager: DownloadManager, private val downloadProvider: DownloadProvider, diff --git a/app/src/main/java/eu/kanade/domain/download/interactor/DeleteDownload.kt b/app/src/main/java/eu/kanade/domain/download/interactor/DeleteDownload.kt index 18a2bf3f0..14fab2a20 100644 --- a/app/src/main/java/eu/kanade/domain/download/interactor/DeleteDownload.kt +++ b/app/src/main/java/eu/kanade/domain/download/interactor/DeleteDownload.kt @@ -1,11 +1,13 @@ package eu.kanade.domain.download.interactor +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.data.download.DownloadManager import tachiyomi.core.common.util.lang.withNonCancellableContext import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.service.SourceManager +@Inject class DeleteDownload( private val sourceManager: SourceManager, private val downloadManager: DownloadManager, diff --git a/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionLanguages.kt b/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionLanguages.kt index 4e5c7c926..21ce800da 100644 --- a/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionLanguages.kt +++ b/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionLanguages.kt @@ -1,11 +1,13 @@ package eu.kanade.domain.extension.interactor +import dev.zacsweers.metro.Inject import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.tachiyomi.extension.ExtensionManager import eu.kanade.tachiyomi.util.system.LocaleHelper import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine +@Inject class GetExtensionLanguages( private val preferences: SourcePreferences, private val extensionManager: ExtensionManager, diff --git a/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionSources.kt b/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionSources.kt index 76d2abe01..e1381710e 100644 --- a/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionSources.kt +++ b/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionSources.kt @@ -1,11 +1,13 @@ package eu.kanade.domain.extension.interactor +import dev.zacsweers.metro.Inject import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.source.Source import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +@Inject class GetExtensionSources( private val preferences: SourcePreferences, ) { diff --git a/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionsByType.kt b/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionsByType.kt index ce689a7f4..ff305cf09 100644 --- a/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionsByType.kt +++ b/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionsByType.kt @@ -1,5 +1,6 @@ package eu.kanade.domain.extension.interactor +import dev.zacsweers.metro.Inject import eu.kanade.domain.extension.model.Extensions import eu.kanade.domain.source.service.SourcePreferences 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.combine +@Inject class GetExtensionsByType( private val preferences: SourcePreferences, private val extensionManager: ExtensionManager, diff --git a/app/src/main/java/eu/kanade/domain/extension/interactor/TrustExtension.kt b/app/src/main/java/eu/kanade/domain/extension/interactor/TrustExtension.kt index 3b4351be5..4a5b31b14 100644 --- a/app/src/main/java/eu/kanade/domain/extension/interactor/TrustExtension.kt +++ b/app/src/main/java/eu/kanade/domain/extension/interactor/TrustExtension.kt @@ -2,10 +2,12 @@ package eu.kanade.domain.extension.interactor import android.content.pm.PackageInfo import androidx.core.content.pm.PackageInfoCompat +import dev.zacsweers.metro.Inject import eu.kanade.domain.source.service.SourcePreferences import mihon.domain.extension.repository.ExtensionStoreRepository import tachiyomi.core.common.preference.getAndSet +@Inject class TrustExtension( private val repository: ExtensionStoreRepository, private val preferences: SourcePreferences, diff --git a/app/src/main/java/eu/kanade/domain/manga/interactor/GetExcludedScanlators.kt b/app/src/main/java/eu/kanade/domain/manga/interactor/GetExcludedScanlators.kt index 464421292..f6dd5c758 100644 --- a/app/src/main/java/eu/kanade/domain/manga/interactor/GetExcludedScanlators.kt +++ b/app/src/main/java/eu/kanade/domain/manga/interactor/GetExcludedScanlators.kt @@ -1,11 +1,13 @@ package eu.kanade.domain.manga.interactor import app.cash.sqldelight.async.coroutines.awaitAsList +import dev.zacsweers.metro.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import tachiyomi.data.Database import tachiyomi.data.subscribeToList +@Inject class GetExcludedScanlators( private val database: Database, ) { diff --git a/app/src/main/java/eu/kanade/domain/manga/interactor/SetExcludedScanlators.kt b/app/src/main/java/eu/kanade/domain/manga/interactor/SetExcludedScanlators.kt index 3783e6c7a..37b2778f2 100644 --- a/app/src/main/java/eu/kanade/domain/manga/interactor/SetExcludedScanlators.kt +++ b/app/src/main/java/eu/kanade/domain/manga/interactor/SetExcludedScanlators.kt @@ -1,8 +1,10 @@ package eu.kanade.domain.manga.interactor import app.cash.sqldelight.async.coroutines.awaitAsList +import dev.zacsweers.metro.Inject import tachiyomi.data.Database +@Inject class SetExcludedScanlators( private val database: Database, ) { diff --git a/app/src/main/java/eu/kanade/domain/manga/interactor/SetMangaViewerFlags.kt b/app/src/main/java/eu/kanade/domain/manga/interactor/SetMangaViewerFlags.kt index 083d26e98..820d41373 100644 --- a/app/src/main/java/eu/kanade/domain/manga/interactor/SetMangaViewerFlags.kt +++ b/app/src/main/java/eu/kanade/domain/manga/interactor/SetMangaViewerFlags.kt @@ -1,10 +1,12 @@ 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.ReadingMode import tachiyomi.domain.manga.model.MangaUpdate import tachiyomi.domain.manga.repository.MangaRepository +@Inject class SetMangaViewerFlags( private val mangaRepository: MangaRepository, ) { diff --git a/app/src/main/java/eu/kanade/domain/manga/interactor/UpdateManga.kt b/app/src/main/java/eu/kanade/domain/manga/interactor/UpdateManga.kt index 20c51b3a7..b84251b83 100644 --- a/app/src/main/java/eu/kanade/domain/manga/interactor/UpdateManga.kt +++ b/app/src/main/java/eu/kanade/domain/manga/interactor/UpdateManga.kt @@ -1,5 +1,6 @@ package eu.kanade.domain.manga.interactor +import dev.zacsweers.metro.Inject import kotlinx.datetime.LocalDateTime import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime @@ -9,6 +10,7 @@ import tachiyomi.domain.manga.model.MangaUpdate import tachiyomi.domain.manga.repository.MangaRepository import kotlin.time.Clock +@Inject class UpdateManga( private val mangaRepository: MangaRepository, private val fetchInterval: FetchInterval, diff --git a/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt b/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt index ececb06d8..d204bca91 100644 --- a/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt +++ b/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt @@ -1,10 +1,11 @@ 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.source.model.SManga import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode +import mihon.app.di.appGraph import tachiyomi.core.common.preference.TriState import tachiyomi.core.metadata.comicinfo.ComicInfo import tachiyomi.core.metadata.comicinfo.ComicInfoPublishingStatus @@ -22,7 +23,7 @@ val Manga.readerOrientation: Long val Manga.downloadedFilter: TriState get() { - if (Injekt.get().downloadedOnly.get()) return TriState.ENABLED_IS + if (Injekt.get().appGraph.basePreferences.downloadedOnly.get()) return TriState.ENABLED_IS return when (downloadedFilterRaw) { Manga.CHAPTER_SHOW_DOWNLOADED -> TriState.ENABLED_IS Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriState.ENABLED_NOT diff --git a/app/src/main/java/eu/kanade/domain/source/interactor/GetEnabledSources.kt b/app/src/main/java/eu/kanade/domain/source/interactor/GetEnabledSources.kt index 104897c62..e16ef07a5 100644 --- a/app/src/main/java/eu/kanade/domain/source/interactor/GetEnabledSources.kt +++ b/app/src/main/java/eu/kanade/domain/source/interactor/GetEnabledSources.kt @@ -1,5 +1,6 @@ package eu.kanade.domain.source.interactor +import dev.zacsweers.metro.Inject import eu.kanade.domain.source.service.SourcePreferences import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -10,6 +11,7 @@ import tachiyomi.domain.source.model.Source import tachiyomi.domain.source.repository.SourceRepository import tachiyomi.source.local.isLocal +@Inject class GetEnabledSources( private val repository: SourceRepository, private val preferences: SourcePreferences, diff --git a/app/src/main/java/eu/kanade/domain/source/interactor/GetIncognitoState.kt b/app/src/main/java/eu/kanade/domain/source/interactor/GetIncognitoState.kt index d39a5ad5f..2a9bcabbc 100644 --- a/app/src/main/java/eu/kanade/domain/source/interactor/GetIncognitoState.kt +++ b/app/src/main/java/eu/kanade/domain/source/interactor/GetIncognitoState.kt @@ -1,5 +1,6 @@ package eu.kanade.domain.source.interactor +import dev.zacsweers.metro.Inject import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.tachiyomi.extension.ExtensionManager @@ -7,6 +8,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +@Inject class GetIncognitoState( private val basePreferences: BasePreferences, private val sourcePreferences: SourcePreferences, diff --git a/app/src/main/java/eu/kanade/domain/source/interactor/GetLanguagesWithSources.kt b/app/src/main/java/eu/kanade/domain/source/interactor/GetLanguagesWithSources.kt index 69587a4c5..543cfa0b7 100644 --- a/app/src/main/java/eu/kanade/domain/source/interactor/GetLanguagesWithSources.kt +++ b/app/src/main/java/eu/kanade/domain/source/interactor/GetLanguagesWithSources.kt @@ -1,5 +1,6 @@ package eu.kanade.domain.source.interactor +import dev.zacsweers.metro.Inject import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.tachiyomi.util.system.LocaleHelper import kotlinx.coroutines.flow.Flow @@ -8,6 +9,7 @@ import tachiyomi.domain.source.model.Source import tachiyomi.domain.source.repository.SourceRepository import java.util.SortedMap +@Inject class GetLanguagesWithSources( private val repository: SourceRepository, private val preferences: SourcePreferences, diff --git a/app/src/main/java/eu/kanade/domain/source/interactor/GetSourcesWithFavoriteCount.kt b/app/src/main/java/eu/kanade/domain/source/interactor/GetSourcesWithFavoriteCount.kt index cb6debd7e..be0c24bdb 100644 --- a/app/src/main/java/eu/kanade/domain/source/interactor/GetSourcesWithFavoriteCount.kt +++ b/app/src/main/java/eu/kanade/domain/source/interactor/GetSourcesWithFavoriteCount.kt @@ -1,5 +1,6 @@ package eu.kanade.domain.source.interactor +import dev.zacsweers.metro.Inject import eu.kanade.domain.source.service.SourcePreferences import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -9,6 +10,7 @@ import tachiyomi.domain.source.repository.SourceRepository import tachiyomi.source.local.isLocal import java.util.Collections +@Inject class GetSourcesWithFavoriteCount( private val repository: SourceRepository, private val preferences: SourcePreferences, diff --git a/app/src/main/java/eu/kanade/domain/source/interactor/SetMigrateSorting.kt b/app/src/main/java/eu/kanade/domain/source/interactor/SetMigrateSorting.kt index 9a5e08177..05e3b583e 100644 --- a/app/src/main/java/eu/kanade/domain/source/interactor/SetMigrateSorting.kt +++ b/app/src/main/java/eu/kanade/domain/source/interactor/SetMigrateSorting.kt @@ -1,7 +1,9 @@ package eu.kanade.domain.source.interactor +import dev.zacsweers.metro.Inject import eu.kanade.domain.source.service.SourcePreferences +@Inject class SetMigrateSorting( private val preferences: SourcePreferences, ) { diff --git a/app/src/main/java/eu/kanade/domain/source/interactor/ToggleIncognito.kt b/app/src/main/java/eu/kanade/domain/source/interactor/ToggleIncognito.kt index 7816171ea..386fc3319 100644 --- a/app/src/main/java/eu/kanade/domain/source/interactor/ToggleIncognito.kt +++ b/app/src/main/java/eu/kanade/domain/source/interactor/ToggleIncognito.kt @@ -1,8 +1,10 @@ package eu.kanade.domain.source.interactor +import dev.zacsweers.metro.Inject import eu.kanade.domain.source.service.SourcePreferences import tachiyomi.core.common.preference.getAndSet +@Inject class ToggleIncognito( private val preferences: SourcePreferences, ) { diff --git a/app/src/main/java/eu/kanade/domain/source/interactor/ToggleLanguage.kt b/app/src/main/java/eu/kanade/domain/source/interactor/ToggleLanguage.kt index 6af4d0085..1c84eff31 100644 --- a/app/src/main/java/eu/kanade/domain/source/interactor/ToggleLanguage.kt +++ b/app/src/main/java/eu/kanade/domain/source/interactor/ToggleLanguage.kt @@ -1,8 +1,10 @@ package eu.kanade.domain.source.interactor +import dev.zacsweers.metro.Inject import eu.kanade.domain.source.service.SourcePreferences import tachiyomi.core.common.preference.getAndSet +@Inject class ToggleLanguage( val preferences: SourcePreferences, ) { diff --git a/app/src/main/java/eu/kanade/domain/source/interactor/ToggleSource.kt b/app/src/main/java/eu/kanade/domain/source/interactor/ToggleSource.kt index 5a2b6be0f..4115b6762 100644 --- a/app/src/main/java/eu/kanade/domain/source/interactor/ToggleSource.kt +++ b/app/src/main/java/eu/kanade/domain/source/interactor/ToggleSource.kt @@ -1,9 +1,11 @@ package eu.kanade.domain.source.interactor +import dev.zacsweers.metro.Inject import eu.kanade.domain.source.service.SourcePreferences import tachiyomi.core.common.preference.getAndSet import tachiyomi.domain.source.model.Source +@Inject class ToggleSource( private val preferences: SourcePreferences, ) { diff --git a/app/src/main/java/eu/kanade/domain/source/interactor/ToggleSourcePin.kt b/app/src/main/java/eu/kanade/domain/source/interactor/ToggleSourcePin.kt index e9fbfd1bc..90ff73d92 100644 --- a/app/src/main/java/eu/kanade/domain/source/interactor/ToggleSourcePin.kt +++ b/app/src/main/java/eu/kanade/domain/source/interactor/ToggleSourcePin.kt @@ -1,9 +1,11 @@ package eu.kanade.domain.source.interactor +import dev.zacsweers.metro.Inject import eu.kanade.domain.source.service.SourcePreferences import tachiyomi.core.common.preference.getAndSet import tachiyomi.domain.source.model.Source +@Inject class ToggleSourcePin( private val preferences: SourcePreferences, ) { diff --git a/app/src/main/java/eu/kanade/domain/source/model/Source.kt b/app/src/main/java/eu/kanade/domain/source/model/Source.kt index 0e3919718..636894a6d 100644 --- a/app/src/main/java/eu/kanade/domain/source/model/Source.kt +++ b/app/src/main/java/eu/kanade/domain/source/model/Source.kt @@ -1,16 +1,17 @@ package eu.kanade.domain.source.model +import android.content.Context import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.asImageBitmap import androidx.core.graphics.drawable.toBitmap -import eu.kanade.tachiyomi.extension.ExtensionManager +import mihon.app.di.appGraph import tachiyomi.domain.source.model.Source import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get val Source.icon: ImageBitmap? get() { - return Injekt.get().getAppIconForSource(id) + return Injekt.get().appGraph.extensionManager.getAppIconForSource(id) ?.toBitmap() ?.asImageBitmap() } diff --git a/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt b/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt index d861014ea..80ad1ff88 100644 --- a/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt +++ b/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt @@ -1,5 +1,8 @@ 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.tachiyomi.util.system.LocaleHelper import mihon.domain.migration.models.MigrationFlag @@ -9,6 +12,8 @@ import tachiyomi.core.common.preference.getEnum import tachiyomi.core.common.preference.getLongArray import tachiyomi.domain.library.model.LibraryDisplayMode +@Inject +@SingleIn(AppScope::class) class SourcePreferences( preferenceStore: PreferenceStore, ) { diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt b/app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt index 6357f4093..44dc760a0 100644 --- a/app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt +++ b/app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt @@ -1,5 +1,6 @@ package eu.kanade.domain.track.interactor +import dev.zacsweers.metro.Inject import eu.kanade.domain.track.model.toDbTrack import eu.kanade.domain.track.model.toDomainTrack 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.manga.model.Manga import tachiyomi.domain.track.interactor.InsertTrack -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +@Inject class AddTracks( private val insertTrack: InsertTrack, private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack, private val getChaptersByMangaId: GetChaptersByMangaId, private val trackerManager: TrackerManager, + private val getHistory: GetHistory, ) { // TODO: update all trackers based on common data @@ -55,7 +56,7 @@ class AddTracks( } if (track.startDate <= 0) { - val firstReadChapterDate = Injekt.get().await(mangaId) + val firstReadChapterDate = getHistory.await(mangaId) .sortedBy { it.readAt } .firstOrNull() ?.readAt diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/RefreshTracks.kt b/app/src/main/java/eu/kanade/domain/track/interactor/RefreshTracks.kt index 3d1e337ff..10f671b20 100644 --- a/app/src/main/java/eu/kanade/domain/track/interactor/RefreshTracks.kt +++ b/app/src/main/java/eu/kanade/domain/track/interactor/RefreshTracks.kt @@ -1,5 +1,6 @@ package eu.kanade.domain.track.interactor +import dev.zacsweers.metro.Inject import eu.kanade.domain.track.model.toDbTrack import eu.kanade.domain.track.model.toDomainTrack 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.InsertTrack +@Inject class RefreshTracks( private val getTracks: GetTracks, private val trackerManager: TrackerManager, diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/SyncChapterProgressWithTrack.kt b/app/src/main/java/eu/kanade/domain/track/interactor/SyncChapterProgressWithTrack.kt index 0c9564091..edc8c4826 100644 --- a/app/src/main/java/eu/kanade/domain/track/interactor/SyncChapterProgressWithTrack.kt +++ b/app/src/main/java/eu/kanade/domain/track/interactor/SyncChapterProgressWithTrack.kt @@ -1,5 +1,6 @@ package eu.kanade.domain.track.interactor +import dev.zacsweers.metro.Inject import eu.kanade.domain.track.model.toDbTrack import eu.kanade.tachiyomi.data.track.EnhancedTracker import eu.kanade.tachiyomi.data.track.Tracker @@ -12,6 +13,7 @@ import tachiyomi.domain.track.interactor.InsertTrack import tachiyomi.domain.track.model.Track import kotlin.math.max +@Inject class SyncChapterProgressWithTrack( private val updateChapter: UpdateChapter, private val insertTrack: InsertTrack, diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt b/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt index fdf24ec4e..4f350a4fb 100644 --- a/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt +++ b/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt @@ -1,6 +1,7 @@ package eu.kanade.domain.track.interactor import android.content.Context +import dev.zacsweers.metro.Inject import eu.kanade.domain.track.model.toDbTrack import eu.kanade.domain.track.model.toDomainTrack 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.InsertTrack +@Inject class TrackChapter( private val getTracks: GetTracks, private val trackerManager: TrackerManager, diff --git a/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt b/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt index 50589ae9d..21f7a64ea 100644 --- a/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt +++ b/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt @@ -8,30 +8,36 @@ import androidx.work.ExistingWorkPolicy import androidx.work.NetworkType import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters +import dev.zacsweers.metro.Inject import eu.kanade.domain.track.interactor.TrackChapter import eu.kanade.domain.track.store.DelayedTrackingStore import eu.kanade.tachiyomi.util.system.workManager 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.system.logcat import tachiyomi.domain.track.interactor.GetTracks -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.util.concurrent.TimeUnit class DelayedTrackingUpdateJob(private val context: Context, workerParams: WorkerParameters) : 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 { + graph.inject(this) + if (runAttemptCount > 3) { return Result.failure() } - val getTracks = Injekt.get() - val trackChapter = Injekt.get() - - val delayedTrackingStore = Injekt.get() - withIOContext { delayedTrackingStore.getItems() .mapNotNull { diff --git a/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt b/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt index 610f0f1a8..03dd4cdbc 100644 --- a/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt +++ b/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt @@ -1,5 +1,8 @@ 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.tachiyomi.data.track.Tracker 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.getEnum +@Inject +@SingleIn(AppScope::class) class TrackPreferences( private val preferenceStore: PreferenceStore, ) { diff --git a/app/src/main/java/eu/kanade/domain/track/store/DelayedTrackingStore.kt b/app/src/main/java/eu/kanade/domain/track/store/DelayedTrackingStore.kt index 10779a219..e8788f851 100644 --- a/app/src/main/java/eu/kanade/domain/track/store/DelayedTrackingStore.kt +++ b/app/src/main/java/eu/kanade/domain/track/store/DelayedTrackingStore.kt @@ -2,9 +2,14 @@ package eu.kanade.domain.track.store import android.content.Context import androidx.core.content.edit +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import logcat.LogPriority import tachiyomi.core.common.util.system.logcat +@Inject +@SingleIn(AppScope::class) class DelayedTrackingStore(context: Context) { /** diff --git a/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt b/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt index 84e405dee..354b1890f 100644 --- a/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt +++ b/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt @@ -1,5 +1,8 @@ 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.TabletUiMode import eu.kanade.domain.ui.model.ThemeMode @@ -12,6 +15,8 @@ import java.time.format.DateTimeFormatter import java.time.format.FormatStyle import java.util.Locale +@Inject +@SingleIn(AppScope::class) class UiPreferences( preferenceStore: PreferenceStore, ) { diff --git a/app/src/main/java/eu/kanade/presentation/components/DateText.kt b/app/src/main/java/eu/kanade/presentation/components/DateText.kt index 85e70bb03..9953f3861 100644 --- a/app/src/main/java/eu/kanade/presentation/components/DateText.kt +++ b/app/src/main/java/eu/kanade/presentation/components/DateText.kt @@ -8,10 +8,9 @@ import eu.kanade.tachiyomi.util.lang.toRelativeString import kotlinx.datetime.LocalDate import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime +import mihon.app.di.appGraph import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.time.Instant @Composable @@ -32,7 +31,7 @@ fun relativeDateText( ): String { val context = LocalContext.current - val preferences = remember { Injekt.get() } + val preferences = remember { context.appGraph.uiPreferences } val relativeTime = remember { preferences.relativeTime.get() } val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat.get()) } diff --git a/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt b/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt index 5be7a2ca5..1e6696105 100644 --- a/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt @@ -9,14 +9,15 @@ import androidx.compose.material.icons.outlined.BugReport import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.PreviewLightDark import eu.kanade.presentation.theme.TachiyomiPreviewTheme -import eu.kanade.tachiyomi.util.CrashLogUtil import kotlinx.coroutines.launch +import mihon.app.di.appGraph import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource @@ -29,6 +30,7 @@ fun CrashScreen( ) { val scope = rememberCoroutineScope() val context = LocalContext.current + val crashLogUtil = remember { context.appGraph.crashLogUtil } InfoScreen( icon = Icons.Outlined.BugReport, @@ -37,7 +39,7 @@ fun CrashScreen( acceptText = stringResource(MR.strings.pref_dump_crash_logs), onAcceptClick = { scope.launch { - CrashLogUtil(context).dumpLogs(exception) + crashLogUtil.dumpLogs(exception) } }, rejectText = stringResource(MR.strings.crash_screen_restart_application), diff --git a/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt index e4f9082d0..4aeb7b5e0 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt @@ -26,11 +26,12 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp -import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.manga.model.downloadedFilter import eu.kanade.presentation.components.TabbedDialog import eu.kanade.presentation.components.TabbedDialogPaddings +import mihon.app.di.appGraph import tachiyomi.core.common.preference.TriState import tachiyomi.domain.manga.model.Manga import tachiyomi.i18n.MR @@ -40,8 +41,6 @@ import tachiyomi.presentation.core.components.SortItem import tachiyomi.presentation.core.components.TriStateItem import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.theme.active -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get @Composable fun ChapterSettingsDialog( @@ -65,7 +64,8 @@ fun ChapterSettingsDialog( ) } - val downloadedOnly = remember { Injekt.get().downloadedOnly.get() } + val context = LocalContext.current + val downloadedOnly = remember { context.appGraph.basePreferences.downloadedOnly.get() } TabbedDialog( onDismissRequest = onDismissRequest, diff --git a/app/src/main/java/eu/kanade/presentation/manga/DuplicateMangaDialog.kt b/app/src/main/java/eu/kanade/presentation/manga/DuplicateMangaDialog.kt index da5b03dc2..043f93720 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/DuplicateMangaDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/DuplicateMangaDialog.kt @@ -63,10 +63,10 @@ import eu.kanade.presentation.more.settings.LocalPreferenceMinHeight import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.model.SManga +import mihon.app.di.appGraph import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.MangaWithChapterCount import tachiyomi.domain.source.model.StubSource -import tachiyomi.domain.source.service.SourceManager import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.Badge 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.stringResource import tachiyomi.presentation.core.util.secondaryItemAlpha -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get @Composable fun DuplicateMangaDialog( @@ -86,7 +84,8 @@ fun DuplicateMangaDialog( onMigrate: (manga: Manga) -> Unit, modifier: Modifier = Modifier, ) { - val sourceManager = remember { Injekt.get() } + val context = LocalContext.current + val sourceManager = remember { context.appGraph.sourceManager } val minHeight = LocalPreferenceMinHeight.current val horizontalPadding = PaddingValues(horizontal = TabbedDialogPaddings.Horizontal) val horizontalPaddingModifier = Modifier.padding(horizontalPadding) diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt index ec2043c6e..5c639840d 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt @@ -30,6 +30,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalView import androidx.compose.ui.unit.DpOffset @@ -48,7 +49,6 @@ import coil3.imageLoader import coil3.request.CachePolicy import coil3.request.ImageRequest import coil3.size.Size -import eu.kanade.domain.base.BasePreferences import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBarActions 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.ui.reader.viewer.ReaderPageImageView import kotlinx.coroutines.runBlocking +import mihon.app.di.appGraph import tachiyomi.domain.manga.model.Manga import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.clickableNoIndication -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get @Composable fun MangaCoverDialog( @@ -75,7 +74,7 @@ fun MangaCoverDialog( onEditClick: ((EditCoverAction) -> Unit)?, onDismissRequest: () -> Unit, ) { - val useNewRenderer = Injekt.get().highQualityRenderer.get() + val useNewRenderer = LocalContext.current.appGraph.basePreferences.highQualityRenderer.get() val view = LocalView.current Dialog( diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt index 6e38d194a..9c2d8d95b 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt @@ -86,13 +86,13 @@ import coil3.request.crossfade import com.mikepenz.markdown.model.markdownAnnotator import com.mikepenz.markdown.model.markdownAnnotatorConfig import com.mikepenz.markdown.utils.getUnescapedTextInNode -import eu.kanade.domain.ui.UiPreferences import eu.kanade.presentation.components.DropdownMenu import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.util.system.copyToClipboard import kotlinx.datetime.TimeZone import kotlinx.datetime.daysUntil +import mihon.app.di.appGraph import org.intellij.markdown.MarkdownElementTypes import org.intellij.markdown.MarkdownTokenTypes 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.util.clickableNoIndication import tachiyomi.presentation.core.util.secondaryItemAlpha -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.math.roundToInt import kotlin.time.Clock import kotlin.time.Instant @@ -619,7 +617,8 @@ private fun MangaSummary( onEditNotesClicked: () -> Unit, modifier: Modifier = Modifier, ) { - val preferences = remember { Injekt.get() } + val context = LocalContext.current + val preferences = remember { context.appGraph.uiPreferences } val loadImages = remember { preferences.imagesInDescription.get() } val animProgress by animateFloatAsState( targetValue = if (expanded) 1f else 0f, diff --git a/app/src/main/java/eu/kanade/presentation/more/onboarding/PermissionStep.kt b/app/src/main/java/eu/kanade/presentation/more/onboarding/PermissionStep.kt index 07160337c..f5f68f06a 100644 --- a/app/src/main/java/eu/kanade/presentation/more/onboarding/PermissionStep.kt +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/PermissionStep.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -36,19 +37,16 @@ import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.compose.LocalLifecycleOwner 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.telemetryIncluded +import mihon.app.di.appGraph import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.secondaryItemAlpha -import uy.kohesive.injekt.injectLazy internal class PermissionStep : OnboardingStep { - private val privacyPreferences: PrivacyPreferences by injectLazy() - private var notificationGranted by mutableStateOf(false) private var batteryGranted by mutableStateOf(false) @@ -57,6 +55,7 @@ internal class PermissionStep : OnboardingStep { @Composable override fun Content() { val context = LocalContext.current + val privacyPreferences = remember { context.appGraph.privacyPreferences } val lifecycleOwner = LocalLifecycleOwner.current val installGranted = rememberRequestPackageInstallsPermissionState() diff --git a/app/src/main/java/eu/kanade/presentation/more/onboarding/StorageStep.kt b/app/src/main/java/eu/kanade/presentation/more/onboarding/StorageStep.kt index 0abef4c08..803c8e39b 100644 --- a/app/src/main/java/eu/kanade/presentation/more/onboarding/StorageStep.kt +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/StorageStep.kt @@ -12,6 +12,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import 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.tachiyomi.util.system.toast import kotlinx.coroutines.flow.collectLatest -import tachiyomi.domain.storage.service.StoragePreferences +import mihon.app.di.appGraph import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.Button import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get internal class StorageStep : OnboardingStep { - private val storagePref = Injekt.get().baseStorageDirectory - private var _isComplete by mutableStateOf(false) override val isComplete: Boolean @@ -42,6 +39,7 @@ internal class StorageStep : OnboardingStep { val context = LocalContext.current val handler = LocalUriHandler.current + val storagePref = remember { context.appGraph.storagePreferences.baseStorageDirectory } val pickStorageLocation = SettingsDataScreen.storageLocationPicker(storagePref) Column( diff --git a/app/src/main/java/eu/kanade/presentation/more/onboarding/ThemeStep.kt b/app/src/main/java/eu/kanade/presentation/more/onboarding/ThemeStep.kt index 8fb18a4e8..55d507ed1 100644 --- a/app/src/main/java/eu/kanade/presentation/more/onboarding/ThemeStep.kt +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/ThemeStep.kt @@ -3,22 +3,22 @@ package eu.kanade.presentation.more.onboarding import androidx.compose.foundation.layout.Column import androidx.compose.runtime.Composable 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.presentation.more.settings.widget.AppThemeModePreferenceWidget import eu.kanade.presentation.more.settings.widget.AppThemePreferenceWidget +import mihon.app.di.appGraph import tachiyomi.presentation.core.util.collectAsState -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get internal class ThemeStep : OnboardingStep { override val isComplete: Boolean = true - private val uiPreferences: UiPreferences = Injekt.get() - @Composable override fun Content() { + val context = LocalContext.current + val uiPreferences = remember { context.appGraph.uiPreferences } val themeModePref = uiPreferences.themeMode val themeMode by themeModePref.collectAsState() diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt index 1da71e31e..19802e96e 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt @@ -25,13 +25,10 @@ import androidx.core.net.toUri import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow 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.screen.advanced.ClearDatabaseScreen 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.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkPreferences import eu.kanade.tachiyomi.network.PREF_DOH_360 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_SHECAN 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.isShizukuInstalled import eu.kanade.tachiyomi.util.system.powerManager import eu.kanade.tachiyomi.util.system.setDefaultSettings import eu.kanade.tachiyomi.util.system.toast +import eu.kanade.tachiyomi.util.system.workManager import kotlinx.coroutines.launch import logcat.LogPriority +import mihon.app.di.appGraph import okhttp3.Headers import tachiyomi.core.common.util.lang.launchNonCancellable import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.library.service.LibraryPreferences -import tachiyomi.domain.manga.interactor.ResetViewerFlags import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.collectAsState -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.io.File object SettingsAdvancedScreen : SearchableSettings { @@ -80,9 +75,11 @@ object SettingsAdvancedScreen : SearchableSettings { val context = LocalContext.current val navigator = LocalNavigator.currentOrThrow - val basePreferences = remember { Injekt.get() } - val networkPreferences = remember { Injekt.get() } - val libraryPreferences = remember { Injekt.get() } + val graph = remember { context.appGraph } + val basePreferences = remember { graph.basePreferences } + val networkPreferences = remember { graph.networkPreferences } + val libraryPreferences = remember { graph.libraryPreferences } + val crashLogUtil = remember { graph.crashLogUtil } return listOf( Preference.PreferenceItem.TextPreference( @@ -90,7 +87,7 @@ object SettingsAdvancedScreen : SearchableSettings { subtitle = stringResource(MR.strings.pref_dump_crash_logs_summary), onClick = { scope.launch { - CrashLogUtil(context).dumpLogs() + crashLogUtil.dumpLogs() } }, ), @@ -180,7 +177,7 @@ object SettingsAdvancedScreen : SearchableSettings { title = stringResource(MR.strings.pref_invalidate_download_cache), subtitle = stringResource(MR.strings.pref_invalidate_download_cache_summary), onClick = { - Injekt.get().invalidateCache() + context.appGraph.downloadCache.invalidateCache() context.toast(MR.strings.download_cache_invalidated) }, ), @@ -198,7 +195,7 @@ object SettingsAdvancedScreen : SearchableSettings { networkPreferences: NetworkPreferences, ): Preference.PreferenceGroup { val context = LocalContext.current - val networkHelper = remember { Injekt.get() } + val networkHelper = remember { context.appGraph.networkHelper } val userAgentPref = networkPreferences.defaultUserAgent val userAgent by userAgentPref.collectAsState() @@ -295,14 +292,14 @@ object SettingsAdvancedScreen : SearchableSettings { preferenceItems = listOf( Preference.PreferenceItem.TextPreference( title = stringResource(MR.strings.pref_refresh_library_covers), - onClick = { MetadataUpdateJob.startNow(context) }, + onClick = { MetadataUpdateJob.startNow(context.workManager) }, ), Preference.PreferenceItem.TextPreference( title = stringResource(MR.strings.pref_reset_viewer_flags), subtitle = stringResource(MR.strings.pref_reset_viewer_flags_summary), onClick = { scope.launchNonCancellable { - val success = Injekt.get().await() + val success = context.appGraph.resetViewerFlags.await() withUIContext { val message = if (success) { MR.strings.pref_reset_viewer_flags_success @@ -352,7 +349,7 @@ object SettingsAdvancedScreen : SearchableSettings { val uriHandler = LocalUriHandler.current val extensionInstallerPref = basePreferences.extensionInstaller var shizukuMissing by rememberSaveable { mutableStateOf(false) } - val trustExtension = remember { Injekt.get() } + val trustExtension = remember { context.appGraph.trustExtension } if (shizukuMissing) { val dismiss = { shizukuMissing = false } diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt index 7afd0ea1c..e0a8a94a1 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt @@ -22,11 +22,10 @@ import eu.kanade.tachiyomi.util.system.toast import kotlinx.datetime.TimeZone import kotlinx.datetime.toJavaLocalDateTime import kotlinx.datetime.toLocalDateTime +import mihon.app.di.appGraph import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.collectAsState -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.time.Clock object SettingsAppearanceScreen : SearchableSettings { @@ -37,7 +36,8 @@ object SettingsAppearanceScreen : SearchableSettings { @Composable override fun getPreferences(): List { - val uiPreferences = remember { Injekt.get() } + val context = LocalContext.current + val uiPreferences = remember { context.appGraph.uiPreferences } return listOf( getThemeGroup(uiPreferences = uiPreferences), diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt index 0c401d855..3c0895a68 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt @@ -9,17 +9,14 @@ import androidx.compose.ui.platform.LocalContext import androidx.fragment.app.FragmentActivity import cafe.adriel.voyager.navigator.LocalNavigator 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.screen.browse.ExtensionStoresScreen 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.i18n.MR import tachiyomi.presentation.core.i18n.pluralStringResource import tachiyomi.presentation.core.i18n.stringResource -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get object SettingsBrowseScreen : SearchableSettings { @@ -32,8 +29,8 @@ object SettingsBrowseScreen : SearchableSettings { val context = LocalContext.current val navigator = LocalNavigator.currentOrThrow - val sourcePreferences = remember { Injekt.get() } - val getExtensionStoreCountAsFlow = remember { Injekt.get() } + val sourcePreferences = remember { context.appGraph.sourcePreferences } + val getExtensionStoreCountAsFlow = remember { context.appGraph.getExtensionStoreCountAsFlow } val reposCount by getExtensionStoreCountAsFlow().collectAsState(0) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt index 4e1f89ed1..bcca0462a 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt @@ -51,30 +51,27 @@ import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding import eu.kanade.presentation.util.relativeTimeSpanString import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob 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.ExportOptions import eu.kanade.tachiyomi.util.system.DeviceUtil import eu.kanade.tachiyomi.util.system.toast +import eu.kanade.tachiyomi.util.system.workManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import logcat.LogPriority +import mihon.app.di.appGraph import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.storage.displayablePath import tachiyomi.core.common.util.lang.launchNonCancellable import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.system.logcat 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.storage.service.StoragePreferences import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.TextButton import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.collectAsState -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get object SettingsDataScreen : SearchableSettings { @@ -98,8 +95,9 @@ object SettingsDataScreen : SearchableSettings { @Composable override fun getPreferences(): List { - val backupPreferences = Injekt.get() - val storagePreferences = Injekt.get() + val context = LocalContext.current + val backupPreferences = remember { context.appGraph.backupPreferences } + val storagePreferences = remember { context.appGraph.storagePreferences } return listOf( getStorageLocationPref(storagePreferences = storagePreferences), @@ -230,7 +228,7 @@ object SettingsDataScreen : SearchableSettings { modifier = Modifier.fillMaxHeight(), checked = false, onCheckedChange = { - if (!BackupRestoreJob.isRunning(context)) { + if (!BackupRestoreJob.isRunning(context.workManager)) { if (DeviceUtil.isMiui && DeviceUtil.isMiuiOptimizationDisabled()) { context.toast(MR.strings.restore_miui_warning) } @@ -279,9 +277,9 @@ object SettingsDataScreen : SearchableSettings { private fun getDataGroup(): Preference.PreferenceGroup { val context = LocalContext.current val scope = rememberCoroutineScope() - val libraryPreferences = remember { Injekt.get() } + val libraryPreferences = remember { context.appGraph.libraryPreferences } - val chapterCache = remember { Injekt.get() } + val chapterCache = remember { context.appGraph.chapterCache } var cacheReadableSizeSema by remember { mutableIntStateOf(0) } val cacheReadableSize = remember(cacheReadableSizeSema) { chapterCache.readableSize } @@ -341,7 +339,7 @@ object SettingsDataScreen : SearchableSettings { val context = LocalContext.current val scope = rememberCoroutineScope() - val getFavorites = remember { Injekt.get() } + val getFavorites = remember { context.appGraph.getFavorites } var favorites by remember { mutableStateOf>(emptyList()) } LaunchedEffect(Unit) { favorites = getFavorites.await() diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt index 3a5328fde..f203363d6 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt @@ -8,19 +8,18 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.util.fastMap import eu.kanade.presentation.category.visualName import eu.kanade.presentation.more.settings.Preference 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.download.service.DownloadPreferences import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.pluralStringResource import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.collectAsState -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get object SettingsDownloadScreen : SearchableSettings { @@ -30,10 +29,11 @@ object SettingsDownloadScreen : SearchableSettings { @Composable override fun getPreferences(): List { - val getCategories = remember { Injekt.get() } + val context = LocalContext.current + val getCategories = remember { context.appGraph.getCategories } val allCategories by getCategories.subscribe().collectAsState(initial = emptyList()) - val downloadPreferences = remember { Injekt.get() } + val downloadPreferences = remember { context.appGraph.downloadPreferences } val parallelSourceLimit by downloadPreferences.parallelSourceLimit.collectAsState() val parallelPageLimit by downloadPreferences.parallelPageLimit.collectAsState() return listOf( diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt index 531c7b6dc..2e1688174 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt @@ -21,8 +21,7 @@ import eu.kanade.presentation.more.settings.widget.TriStateListDialog import eu.kanade.tachiyomi.data.library.LibraryUpdateJob import eu.kanade.tachiyomi.ui.category.CategoryScreen import kotlinx.coroutines.launch -import tachiyomi.domain.category.interactor.GetCategories -import tachiyomi.domain.category.interactor.ResetCategoryFlags +import mihon.app.di.appGraph import tachiyomi.domain.category.model.Category import tachiyomi.domain.library.service.LibraryPreferences 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.stringResource import tachiyomi.presentation.core.util.collectAsState -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get object SettingsLibraryScreen : SearchableSettings { @@ -49,8 +46,9 @@ object SettingsLibraryScreen : SearchableSettings { @Composable override fun getPreferences(): List { - val getCategories = remember { Injekt.get() } - val libraryPreferences = remember { Injekt.get() } + val context = LocalContext.current + val getCategories = remember { context.appGraph.getCategories } + val libraryPreferences = remember { context.appGraph.libraryPreferences } val allCategories by getCategories.subscribe().collectAsState(initial = emptyList()) return listOf( @@ -66,6 +64,7 @@ object SettingsLibraryScreen : SearchableSettings { allCategories: List, libraryPreferences: LibraryPreferences, ): Preference.PreferenceGroup { + val context = LocalContext.current val scope = rememberCoroutineScope() val userCategoriesCount = allCategories.filterNot(Category::isSystemCategory).size @@ -98,7 +97,7 @@ object SettingsLibraryScreen : SearchableSettings { onValueChanged = { if (!it) { scope.launch { - Injekt.get().await() + context.appGraph.resetCategoryFlags.await() } } true @@ -172,7 +171,9 @@ object SettingsLibraryScreen : SearchableSettings { enabled = autoUpdateInterval > 0, onValueChanged = { // 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 }, ), diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt index bae7edce5..4b4514620 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt @@ -4,18 +4,18 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView import eu.kanade.presentation.more.settings.Preference import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode import eu.kanade.tachiyomi.util.system.hasDisplayCutout +import mihon.app.di.appGraph import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.pluralStringResource import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.collectAsState -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.text.NumberFormat object SettingsReaderScreen : SearchableSettings { @@ -26,7 +26,8 @@ object SettingsReaderScreen : SearchableSettings { @Composable override fun getPreferences(): List { - val readerPref = remember { Injekt.get() } + val context = LocalContext.current + val readerPref = remember { context.appGraph.readerPreferences } return listOf( Preference.PreferenceItem.ListPreference( diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt index fb1249337..2814efb4f 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt @@ -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.isAuthenticationSupported import eu.kanade.tachiyomi.util.system.telemetryIncluded +import mihon.app.di.appGraph import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.pluralStringResource import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.collectAsState -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get object SettingsSecurityScreen : SearchableSettings { @@ -28,8 +27,9 @@ object SettingsSecurityScreen : SearchableSettings { @Composable override fun getPreferences(): List { - val securityPreferences = remember { Injekt.get() } - val privacyPreferences = remember { Injekt.get() } + val context = LocalContext.current + val securityPreferences = remember { context.appGraph.securityPreferences } + val privacyPreferences = remember { context.appGraph.privacyPreferences } return buildList(2) { add(getSecurityGroup(securityPreferences)) if (!telemetryIncluded) return@buildList diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt index c141a74c0..3819716ac 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt @@ -44,11 +44,9 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import dev.icerock.moko.resources.StringResource import eu.kanade.domain.track.model.AutoTrackState -import eu.kanade.domain.track.service.TrackPreferences import eu.kanade.presentation.more.settings.Preference import eu.kanade.tachiyomi.data.track.EnhancedTracker 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.bangumi.BangumiApi 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.util.system.openInBrowser 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.withUIContext -import tachiyomi.domain.source.service.SourceManager import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get object SettingsTrackingScreen : SearchableSettings { @@ -86,9 +82,9 @@ object SettingsTrackingScreen : SearchableSettings { @Composable override fun getPreferences(): List { val context = LocalContext.current - val trackPreferences = remember { Injekt.get() } - val trackerManager = remember { Injekt.get() } - val sourceManager = remember { Injekt.get() } + val trackPreferences = remember { context.appGraph.trackPreferences } + val trackerManager = remember { context.appGraph.trackerManager } + val sourceManager = remember { context.appGraph.sourceManager } var dialog by remember { mutableStateOf(null) } dialog?.run { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt index ce9743b01..70c2e4304 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt @@ -30,10 +30,8 @@ import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget import eu.kanade.presentation.util.LocalBackPress import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.BuildConfig -import eu.kanade.tachiyomi.data.updater.AppUpdateChecker import eu.kanade.tachiyomi.data.updater.RELEASE_URL 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.system.copyToClipboard import eu.kanade.tachiyomi.util.system.isFossBuildType @@ -44,6 +42,7 @@ import kotlinx.coroutines.launch import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime import logcat.LogPriority +import mihon.app.di.appGraph import tachiyomi.core.common.Constants import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.lang.withUIContext @@ -74,6 +73,7 @@ object AboutScreen : Screen() { val handleBack = LocalBackPress.current val navigator = LocalNavigator.currentOrThrow var isCheckingUpdates by remember { mutableStateOf(false) } + val crashLogUtil = remember { context.appGraph.crashLogUtil } Scaffold( topBar = { scrollBehavior -> @@ -98,7 +98,7 @@ object AboutScreen : Screen() { title = stringResource(MR.strings.version), subtitle = getVersionName(withBuildDate = true), onPreferenceClick = { - val deviceInfo = CrashLogUtil(context).getDebugInfo() + val deviceInfo = crashLogUtil.getDebugInfo() context.copyToClipboard("Debug information", deviceInfo) }, ) @@ -217,7 +217,7 @@ object AboutScreen : Screen() { onAvailableUpdate: (GetApplicationRelease.Result.NewUpdate) -> Unit, onFinish: () -> Unit, ) { - val updateChecker = AppUpdateChecker() + val updateChecker = context.appGraph.updateChecker withUIContext { try { when (val result = withIOContext { updateChecker.checkForUpdate(forceCheck = true) }) { @@ -279,7 +279,7 @@ object AboutScreen : Screen() { .toLocalDateTime(TimeZone.currentSystemDefault()) .toDateTimestampString( UiPreferences.dateFormat( - Injekt.get().dateFormat.get(), + Injekt.get().appGraph.uiPreferences.dateFormat.get(), ), ) } catch (_: Exception) { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt index 5db2acb7b..9e89672da 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt @@ -32,9 +32,14 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.util.fastMap import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator 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.components.AppBar 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.LoadingScreen import tachiyomi.presentation.core.util.selectedBackground -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get class ClearDatabaseScreen : Screen() { @@ -69,7 +72,7 @@ class ClearDatabaseScreen : Screen() { override fun Content() { val context = LocalContext.current val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel() + val viewModel = metroViewModel() val state by viewModel.state.collectAsState() val scope = rememberCoroutineScope() @@ -222,14 +225,17 @@ class ClearDatabaseScreen : Screen() { } } -class ClearDatabaseViewModel : ViewModel() { +@Inject +@ViewModelKey +@ContributesIntoMap(AppScope::class, binding = binding()) +class ClearDatabaseViewModel( + private val database: Database, + private val getSourcesWithNonLibraryManga: GetSourcesWithNonLibraryManga, +) : ViewModel() { val state: StateFlow field = MutableStateFlow(State.Loading) - private val getSourcesWithNonLibraryManga: GetSourcesWithNonLibraryManga = Injekt.get() - private val database: Database = Injekt.get() - init { viewModelScope.launchIO { getSourcesWithNonLibraryManga.subscribe() diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionStoresScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionStoresScreen.kt index 570fc6768..02aea8685 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionStoresScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionStoresScreen.kt @@ -8,6 +8,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator 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.ExtensionStoreCreateDialog import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionStoreDeleteDialog @@ -26,7 +27,7 @@ class ExtensionStoresScreen( val context = LocalContext.current val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel() + val viewModel = metroViewModel() val state by viewModel.state.collectAsStateWithLifecycle() LaunchedEffect(url) { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionStoresViewModel.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionStoresViewModel.kt index 792c9c967..4cc6ccd96 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionStoresViewModel.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionStoresViewModel.kt @@ -3,6 +3,11 @@ package eu.kanade.presentation.more.settings.screen.browse import androidx.compose.runtime.Immutable import androidx.lifecycle.ViewModel 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 kotlinx.coroutines.Dispatchers 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.model.ExtensionStore import tachiyomi.core.common.util.lang.launchIO -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.time.Duration.Companion.seconds +@Inject +@ViewModelKey +@ContributesIntoMap(AppScope::class, binding = binding()) class ExtensionStoresViewModel( - private val getExtensionStores: GetExtensionStores = Injekt.get(), - private val addExtensionStore: AddExtensionStore = Injekt.get(), - private val removeExtensionStore: RemoveExtensionStore = Injekt.get(), - private val updateExtensionStores: UpdateExtensionStores = Injekt.get(), - private val extensionManager: ExtensionManager = Injekt.get(), + private val getExtensionStores: GetExtensionStores, + private val addExtensionStore: AddExtensionStore, + private val removeExtensionStore: RemoveExtensionStore, + private val updateExtensionStores: UpdateExtensionStores, + private val extensionManager: ExtensionManager, ) : ViewModel() { private val dialog = MutableStateFlow(null) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/CreateBackupScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/CreateBackupScreen.kt index de8167d24..ecfa66185 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/CreateBackupScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/CreateBackupScreen.kt @@ -12,9 +12,14 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator 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.WarningBanner import eu.kanade.presentation.util.Screen @@ -39,7 +44,7 @@ class CreateBackupScreen : Screen() { override fun Content() { val context = LocalContext.current val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel() + val viewModel = metroViewModel() val state by viewModel.state.collectAsState() val chooseBackupDir = rememberLauncherForActivityResult( @@ -73,7 +78,7 @@ class CreateBackupScreen : Screen() { if (!BackupCreateJob.isManualJobRunning(context)) { try { chooseBackupDir.launch(BackupCreator.getFilename()) - } catch (e: ActivityNotFoundException) { + } catch (_: ActivityNotFoundException) { context.toast(MR.strings.file_picker_error) } } else { @@ -121,6 +126,9 @@ class CreateBackupScreen : Screen() { } } +@Inject +@ViewModelKey +@ContributesIntoMap(AppScope::class, binding = binding()) class CreateBackupViewModel : ViewModel() { val state: StateFlow diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/RestoreBackupScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/RestoreBackupScreen.kt index 734be7c9b..02d201874 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/RestoreBackupScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/RestoreBackupScreen.kt @@ -1,6 +1,5 @@ package eu.kanade.presentation.more.settings.screen.data -import android.app.Application import android.content.Context import android.net.Uri 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.core.net.toUri 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.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.WarningBanner 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.RestoreOptions import eu.kanade.tachiyomi.util.system.DeviceUtil +import eu.kanade.tachiyomi.util.system.workManager import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow 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.padding import tachiyomi.presentation.core.i18n.stringResource -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get class RestoreBackupScreen( private val uri: String, @@ -54,12 +56,8 @@ class RestoreBackupScreen( @Composable override fun Content() { val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel( - factory = RestoreBackupViewModel.Factory, - extras = CreationExtras { - set(RestoreBackupViewModel.URI_KEY, uri) - }, - ) + val viewModel = + assistedMetroViewModel { create(uri = uri) } val state by viewModel.state.collectAsState() Scaffold( @@ -175,25 +173,21 @@ class RestoreBackupScreen( } } +@AssistedInject class RestoreBackupViewModel( + @Assisted private val uri: String, + private val backupFileValidator: BackupFileValidator, private val context: Context, - private val uri: String, ) : ViewModel() { val state: StateFlow field = MutableStateFlow(State()) - companion object { - val URI_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - RestoreBackupViewModel( - context = Injekt.get(), - uri = get(URI_KEY)!!, - ) - } - } + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(uri: String): RestoreBackupViewModel } init { @@ -210,7 +204,7 @@ class RestoreBackupViewModel( fun startRestore() { BackupRestoreJob.start( - context = context, + workManager = context.workManager, uri = uri.toUri(), options = state.value.options, ) @@ -218,7 +212,7 @@ class RestoreBackupViewModel( private fun validate(uri: Uri) { val results = try { - BackupFileValidator(context).validate(uri) + backupFileValidator.validate(uri) } catch (e: Exception) { setError( error = InvalidRestore(uri, e.message.toString()), diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/DebugInfoScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/DebugInfoScreen.kt index f1cd38f5f..84f4f1638 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/DebugInfoScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/DebugInfoScreen.kt @@ -16,7 +16,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.profileinstaller.ProfileVerifier import cafe.adriel.voyager.navigator.LocalNavigator 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.PreferenceScaffold 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 kotlinx.coroutines.guava.await import kotlinx.coroutines.launch +import mihon.app.di.appGraph import mihon.core.common.FeatureFlags import tachiyomi.i18n.MR import tachiyomi.presentation.core.util.collectAsState -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get class DebugInfoScreen : Screen() { @@ -62,7 +60,7 @@ class DebugInfoScreen : Screen() { val context = LocalContext.current val scope = rememberCoroutineScope() - val installationIdPref = remember { Injekt.get().installationId } + val installationIdPref = remember { context.appGraph.basePreferences.installationId } val installationId by installationIdPref.collectAsState() return Preference.PreferenceGroup( diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt index 3b21b38d3..e2b0c9dd0 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt @@ -1,6 +1,5 @@ package eu.kanade.presentation.more.settings.screen.debug -import android.app.Application import android.content.Context import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.PaddingValues @@ -21,13 +20,15 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.util.fastForEach import androidx.lifecycle.ViewModel 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.WorkQuery import cafe.adriel.voyager.navigator.LocalNavigator 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.presentation.components.AppBar import eu.kanade.presentation.components.AppBarActions @@ -40,11 +41,11 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime +import mihon.app.di.appGraph import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.plus -import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import kotlin.time.Instant @@ -59,7 +60,7 @@ class WorkerInfoScreen : Screen() { val context = LocalContext.current val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel(factory = WorkerInfoViewModel.Factory) + val viewModel = metroViewModel() val enqueued by viewModel.enqueued.collectAsState() val finished by viewModel.finished.collectAsState() val running by viewModel.running.collectAsState() @@ -120,20 +121,13 @@ class WorkerInfoScreen : Screen() { ) } + @Inject + @ViewModelKey + @ContributesIntoMap(AppScope::class) class WorkerInfoViewModel( - context: Context, + private val context: Context, ) : ViewModel() { - companion object { - val Factory = viewModelFactory { - initializer { - WorkerInfoViewModel( - context = Injekt.get(), - ) - } - } - } - private val workManager = context.workManager val finished = workManager @@ -169,7 +163,7 @@ class WorkerInfoScreen : Screen() { .toLocalDateTime(TimeZone.currentSystemDefault()) .toDateTimestampString( UiPreferences.dateFormat( - Injekt.get().dateFormat.get(), + context.appGraph.uiPreferences.dateFormat.get(), ), ) appendLine("Next scheduled run: $timestamp") diff --git a/app/src/main/java/eu/kanade/presentation/reader/DisplayRefreshHost.kt b/app/src/main/java/eu/kanade/presentation/reader/DisplayRefreshHost.kt index 08f7c4323..132b337ad 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/DisplayRefreshHost.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/DisplayRefreshHost.kt @@ -1,5 +1,6 @@ package eu.kanade.presentation.reader +import android.content.Context import androidx.compose.foundation.Canvas import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable @@ -13,6 +14,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import kotlinx.coroutines.delay +import mihon.app.di.appGraph import tachiyomi.presentation.core.util.collectAsState import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -22,7 +24,7 @@ import kotlin.time.Duration.Companion.milliseconds class DisplayRefreshHost { internal var currentDisplayRefresh by mutableStateOf(false) - private val readerPreferences = Injekt.get() + private val readerPreferences = Injekt.get().appGraph.readerPreferences internal val flashMillis = readerPreferences.flashDurationMillis internal val flashMode = readerPreferences.flashColor diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt index b4e3bf576..d757c1be3 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext import eu.kanade.domain.manga.model.readerOrientation import eu.kanade.domain.manga.model.readingMode 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.viewer.webgpu.WebGpuViewer import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer +import mihon.app.di.appGraph import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.CheckboxItem 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.i18n.stringResource import tachiyomi.presentation.core.util.collectAsState -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.text.NumberFormat @Composable @@ -43,7 +43,7 @@ internal fun ColumnScope.ReadingModePage(viewModel: ReaderSettingsViewModel) { } } - val default = Injekt.get().defaultReadingMode.get() + val default = LocalContext.current.appGraph.readerPreferences.defaultReadingMode.get() val resolved = ReadingMode.fromPreference( when { readingMode == ReadingMode.DEFAULT -> default diff --git a/app/src/main/java/eu/kanade/presentation/theme/TachiyomiTheme.kt b/app/src/main/java/eu/kanade/presentation/theme/TachiyomiTheme.kt index 2890f49f5..f0961b740 100644 --- a/app/src/main/java/eu/kanade/presentation/theme/TachiyomiTheme.kt +++ b/app/src/main/java/eu/kanade/presentation/theme/TachiyomiTheme.kt @@ -7,7 +7,6 @@ import androidx.compose.material3.MaterialExpressiveTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext -import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.model.AppTheme import eu.kanade.presentation.theme.colorscheme.BaseColorScheme 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.YinYangColorScheme import eu.kanade.presentation.theme.colorscheme.YotsubaColorScheme -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +import mihon.app.di.appGraph @Composable fun TachiyomiTheme( @@ -34,7 +32,8 @@ fun TachiyomiTheme( amoled: Boolean? = null, content: @Composable () -> Unit, ) { - val uiPreferences = Injekt.get() + val context = LocalContext.current + val uiPreferences = remember { context.appGraph.uiPreferences } BaseTachiyomiTheme( appTheme = appTheme ?: uiPreferences.appTheme.get(), isAmoled = amoled ?: uiPreferences.themeDarkAmoled.get(), diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 96f4b7a40..c9bfaf353 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -22,21 +22,21 @@ import coil3.request.allowRgb565 import coil3.request.crossfade import coil3.util.DebugLogger 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.ui.UiPreferences import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode import eu.kanade.tachiyomi.core.security.PrivacyPreferences import eu.kanade.tachiyomi.crash.CrashActivity 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.ImageDecoder import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher import eu.kanade.tachiyomi.data.coil.MangaCoverKeyer import eu.kanade.tachiyomi.data.coil.MangaKeyer 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.NetworkPreferences 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.animatorDurationScale import eu.kanade.tachiyomi.util.system.cancelNotification +import eu.kanade.tachiyomi.util.system.isDebugBuildType import eu.kanade.tachiyomi.util.system.notify import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.launchIn @@ -51,34 +52,68 @@ import kotlinx.coroutines.flow.onEach import logcat.AndroidLogcatLogger import logcat.LogPriority 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.migrations.migrations import mihon.telemetry.TelemetryConfig import org.conscrypt.Conscrypt import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.preference.Preference import tachiyomi.core.common.preference.PreferenceStore -import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.logcat +import tachiyomi.domain.source.service.SourceManager import tachiyomi.i18n.MR import tachiyomi.presentation.widget.WidgetManager import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get -import uy.kohesive.injekt.injectLazy +import uy.kohesive.injekt.api.addSingleton import java.security.Security -class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factory { +class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factory, GraphProvider { - private val basePreferences: BasePreferences by injectLazy() - private val privacyPreferences: PrivacyPreferences by injectLazy() - private val networkPreferences: NetworkPreferences by injectLazy() + override val graph: AppGraph by lazy { + createGraphFactory().create(context = this, isDebugBuild = isDebugBuildType) + } + + @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 private val disableIncognitoReceiver = DisableIncognitoReceiver() @SuppressLint("LaunchActivityFromNotification") override fun onCreate() { super.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) GlobalExceptionHandler.initialize(applicationContext, CrashActivity::class.java) @@ -88,16 +123,6 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor 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() ProcessLifecycleOwner.get().lifecycle.addObserver(this) @@ -143,10 +168,10 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor .onEach(TelemetryConfig::setCrashlyticsEnabled) .launchIn(scope) - setAppCompatDelegateThemeMode(Injekt.get().themeMode.get()) + setAppCompatDelegateThemeMode(uiPreferences.themeMode.get()) // Updates widget update - WidgetManager(Injekt.get(), Injekt.get()).apply { init(scope) } + widgetManager.init(scope) if (!LogcatLogger.isInstalled) { val minLogPriority = when { @@ -161,14 +186,22 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor initializeMigrator() } + private fun setupInjekt() { + patchInjekt() + Injekt.addSingleton(this) + Injekt.addSingleton(this) + Injekt.importModule(injektMetroInteropModule) + } + private fun initializeMigrator() { - val preferenceStore = Injekt.get() 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( old = preference.get(), new = BuildConfig.VERSION_CODE, - migrations = migrations, + migrations = migrations.toList(), onMigrationComplete = { logcat { "Updating last version to ${BuildConfig.VERSION_CODE}" } preference.set(BuildConfig.VERSION_CODE) @@ -178,7 +211,7 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor override fun newImageLoader(context: Context): ImageLoader { return ImageLoader.Builder(this).apply { - val callFactoryLazy = lazy { Injekt.get().client } + val callFactoryLazy = lazy { networkHelper.client } components { // NetworkFetcher.Factory add(OkHttpNetworkFetcherFactory(callFactoryLazy::value)) @@ -186,10 +219,10 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor add(ImageDecoder.Factory()) // Fetcher.Factory add(BufferedSourceFetcher.Factory()) - add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy)) - add(MangaCoverFetcher.MangaFactory(callFactoryLazy)) + add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy, coverCache, sourceManager)) + add(MangaCoverFetcher.MangaFactory(callFactoryLazy, coverCache, sourceManager)) // Keyer - add(MangaCoverKeyer()) + add(MangaCoverKeyer(coverCache)) add(MangaKeyer()) } @@ -211,11 +244,11 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor } override fun onStart(owner: LifecycleOwner) { - SecureActivityDelegate.onApplicationStart() + SecureActivityDelegate.onApplicationStart(this) } override fun onStop(owner: LifecycleOwner) { - SecureActivityDelegate.onApplicationStopped() + SecureActivityDelegate.onApplicationStopped(this) } override fun getPackageName(): String { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupDecoder.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupDecoder.kt index 55553082f..5aa85c6f7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupDecoder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupDecoder.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.backup import android.content.Context import android.net.Uri +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.data.backup.models.Backup import kotlinx.serialization.SerializationException import kotlinx.serialization.protobuf.ProtoBuf @@ -10,13 +11,12 @@ import okio.gzip import okio.source import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.io.IOException +@Inject class BackupDecoder( private val context: Context, - private val parser: ProtoBuf = Injekt.get(), + private val parser: ProtoBuf, ) { /** * Decode a potentially-gzipped backup. diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupFileValidator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupFileValidator.kt index 5a7b87ce9..a2d618041 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupFileValidator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupFileValidator.kt @@ -1,17 +1,15 @@ package eu.kanade.tachiyomi.data.backup -import android.content.Context import android.net.Uri +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.data.track.TrackerManager import tachiyomi.domain.source.service.SourceManager -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +@Inject class BackupFileValidator( - private val context: Context, - - private val sourceManager: SourceManager = Injekt.get(), - private val trackerManager: TrackerManager = Injekt.get(), + private val sourceManager: SourceManager, + private val trackerManager: TrackerManager, + private val backupDecoder: BackupDecoder, ) { /** @@ -21,7 +19,7 @@ class BackupFileValidator( */ fun validate(uri: Uri): Results { val backup = try { - BackupDecoder(context).decode(uri) + backupDecoder.decode(uri) } catch (e: Exception) { throw IllegalStateException(e) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupNotifier.kt index 000242c0b..40db11465 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupNotifier.kt @@ -4,6 +4,7 @@ import android.content.Context import android.graphics.BitmapFactory import androidx.core.app.NotificationCompat import com.hippo.unifile.UniFile +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.core.security.SecurityPreferences 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.storage.displayablePath import tachiyomi.i18n.MR -import uy.kohesive.injekt.injectLazy import java.io.File import kotlin.time.Duration.Companion.milliseconds -class BackupNotifier(private val context: Context) { - - private val preferences: SecurityPreferences by injectLazy() - +@Inject +class BackupNotifier( + private val context: Context, + private val preferences: SecurityPreferences, +) { private val progressNotificationBuilder = context.notificationBuilder( Notifications.CHANNEL_BACKUP_RESTORE_PROGRESS, ) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreateJob.kt index 8a0af5dba..65dc7a323 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreateJob.kt @@ -16,6 +16,7 @@ import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkerParameters import androidx.work.workDataOf import com.hippo.unifile.UniFile +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.data.backup.BackupNotifier import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob 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.workManager 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.domain.backup.service.BackupPreferences import tachiyomi.domain.storage.service.StorageManager -import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.util.concurrent.TimeUnit class BackupCreateJob(private val context: Context, workerParams: WorkerParameters) : 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 { + graph.inject(this) + 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() ?: getAutomaticBackupLocation() @@ -51,7 +63,7 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete ?: BackupOptions() return try { - val location = BackupCreator(context, isAutoBackup).backup(uri, options) + val location = backupCreatorFactory.create(isAutoBackup = isAutoBackup).backup(uri, options) if (!isAutoBackup) { notifier.showBackupComplete(UniFile.fromUri(context, location.toUri())!!) } @@ -78,7 +90,6 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete } private fun getAutomaticBackupLocation(): Uri? { - val storageManager = Injekt.get() return storageManager.getAutomaticBackupsDirectory()?.uri } @@ -88,7 +99,7 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete } fun setupTask(context: Context, prefInterval: Int? = null) { - val backupPreferences = Injekt.get() + val backupPreferences = context.appGraph.backupPreferences val interval = prefInterval ?: backupPreferences.backupInterval.get() if (interval > 0) { val constraints = Constraints( diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt index a7f457093..8f085a54c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt @@ -3,6 +3,9 @@ package eu.kanade.tachiyomi.data.backup.create import android.content.Context import android.net.Uri 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.data.backup.BackupFileValidator 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.repository.MangaRepository import tachiyomi.i18n.MR -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.io.FileOutputStream import java.text.SimpleDateFormat import java.util.Date import java.util.Locale import kotlin.time.Clock +@AssistedInject class BackupCreator( + @Assisted private val isAutoBackup: Boolean, private val context: Context, - private val isAutoBackup: Boolean, - - private val parser: ProtoBuf = Injekt.get(), - private val getFavorites: GetFavorites = Injekt.get(), - private val backupPreferences: BackupPreferences = Injekt.get(), - private val mangaRepository: MangaRepository = Injekt.get(), - - private val categoriesBackupCreator: CategoriesBackupCreator = CategoriesBackupCreator(), - private val mangaBackupCreator: MangaBackupCreator = MangaBackupCreator(), - private val preferenceBackupCreator: PreferenceBackupCreator = PreferenceBackupCreator(), - private val extensionStoresBackupCreator: ExtensionStoresBackupCreator = ExtensionStoresBackupCreator(), - private val sourcesBackupCreator: SourcesBackupCreator = SourcesBackupCreator(), + private val parser: ProtoBuf, + private val getFavorites: GetFavorites, + private val backupPreferences: BackupPreferences, + private val mangaRepository: MangaRepository, + private val categoriesBackupCreator: CategoriesBackupCreator, + private val mangaBackupCreator: MangaBackupCreator, + private val preferenceBackupCreator: PreferenceBackupCreator, + private val extensionStoresBackupCreator: ExtensionStoresBackupCreator, + private val sourcesBackupCreator: SourcesBackupCreator, + private val backupFileValidator: BackupFileValidator, ) { + @AssistedFactory + fun interface Factory { + fun create(isAutoBackup: Boolean): BackupCreator + } suspend fun backup(uri: Uri, options: BackupOptions): String { var file: UniFile? = null @@ -105,7 +110,7 @@ class BackupCreator( val fileUri = file.uri // Make sure it's a valid backup file - BackupFileValidator(context).validate(fileUri) + backupFileValidator.validate(fileUri) if (isAutoBackup) { backupPreferences.lastAutoBackupTimestamp.set(Clock.System.now().toEpochMilliseconds()) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/CategoriesBackupCreator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/CategoriesBackupCreator.kt index 945f32d86..c404f6456 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/CategoriesBackupCreator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/CategoriesBackupCreator.kt @@ -1,14 +1,14 @@ 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.backupCategoryMapper import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.category.model.Category -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +@Inject class CategoriesBackupCreator( - private val getCategories: GetCategories = Injekt.get(), + private val getCategories: GetCategories, ) { suspend operator fun invoke(): List { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/ExtensionStoresBackupCreator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/ExtensionStoresBackupCreator.kt index 0ca4aa59e..cc7d8c900 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/ExtensionStoresBackupCreator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/ExtensionStoresBackupCreator.kt @@ -1,13 +1,13 @@ 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.backupExtensionStoreMapper import mihon.domain.extension.interactor.GetExtensionStores -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +@Inject class ExtensionStoresBackupCreator( - private val getExtensionStores: GetExtensionStores = Injekt.get(), + private val getExtensionStores: GetExtensionStores, ) { suspend operator fun invoke(): List { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt index 76a82fbb5..aebb47a6e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt @@ -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.awaitAsOne +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.data.backup.create.BackupOptions import eu.kanade.tachiyomi.data.backup.models.BackupChapter 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.history.interactor.GetHistory import tachiyomi.domain.manga.model.Manga -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +@Inject class MangaBackupCreator( - private val database: Database = Injekt.get(), - private val getCategories: GetCategories = Injekt.get(), - private val getHistory: GetHistory = Injekt.get(), + private val database: Database, + private val getCategories: GetCategories, + private val getHistory: GetHistory, ) { suspend operator fun invoke(mangas: List, options: BackupOptions): List { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/PreferenceBackupCreator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/PreferenceBackupCreator.kt index e77a32f40..ba221f40f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/PreferenceBackupCreator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/PreferenceBackupCreator.kt @@ -1,5 +1,6 @@ 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.BackupSourcePreferences 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.PreferenceStore import tachiyomi.domain.source.service.SourceManager -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +@Inject class PreferenceBackupCreator( - private val sourceManager: SourceManager = Injekt.get(), - private val preferenceStore: PreferenceStore = Injekt.get(), + private val sourceManager: SourceManager, + private val preferenceStore: PreferenceStore, ) { fun createApp(includePrivatePreferences: Boolean): List { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/SourcesBackupCreator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/SourcesBackupCreator.kt index d3bc573a7..6e7d668e7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/SourcesBackupCreator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/SourcesBackupCreator.kt @@ -1,14 +1,14 @@ 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.BackupSource import eu.kanade.tachiyomi.source.Source import tachiyomi.domain.source.service.SourceManager -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +@Inject class SourcesBackupCreator( - private val sourceManager: SourceManager = Injekt.get(), + private val sourceManager: SourceManager, ) { operator fun invoke(mangas: List): List { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestoreJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestoreJob.kt index 0ecf97e06..f0f0be56e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestoreJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestoreJob.kt @@ -9,16 +9,19 @@ import androidx.work.CoroutineWorker import androidx.work.ExistingWorkPolicy import androidx.work.ForegroundInfo import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager import androidx.work.WorkerParameters import androidx.work.workDataOf +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.data.backup.BackupNotifier import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.util.system.cancelNotification import eu.kanade.tachiyomi.util.system.isRunning import eu.kanade.tachiyomi.util.system.setForegroundSafely -import eu.kanade.tachiyomi.util.system.workManager import kotlinx.coroutines.CancellationException import logcat.LogPriority +import mihon.app.di.AppGraph +import mihon.core.metro.metroGraph import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.util.system.logcat import tachiyomi.i18n.MR @@ -26,9 +29,15 @@ import tachiyomi.i18n.MR class BackupRestoreJob(private val context: Context, workerParams: WorkerParameters) : 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 { + graph.inject(this) + val uri = inputData.getString(LOCATION_URI_KEY)?.toUri() val options = inputData.getBooleanArray(OPTIONS_KEY)?.let { RestoreOptions.fromBooleanArray(it) } @@ -41,7 +50,7 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet setForegroundSafely() return try { - BackupRestorer(context, notifier, isSync).restore(uri, options) + backupRestorerFactory.create(notifier = notifier, isSync = isSync).restore(uri, options) Result.success() } catch (e: Exception) { if (e is CancellationException) { @@ -70,12 +79,12 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet } companion object { - fun isRunning(context: Context): Boolean { - return context.workManager.isRunning(TAG) + fun isRunning(workManager: WorkManager): Boolean { + return workManager.isRunning(TAG) } fun start( - context: Context, + workManager: WorkManager, uri: Uri, options: RestoreOptions, sync: Boolean = false, @@ -89,11 +98,11 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet .addTag(TAG) .setInputData(inputData) .build() - context.workManager.enqueueUniqueWork(TAG, ExistingWorkPolicy.KEEP, request) + workManager.enqueueUniqueWork(TAG, ExistingWorkPolicy.KEEP, request) } - fun stop(context: Context) { - context.workManager.cancelUniqueWork(TAG) + fun stop(workManager: WorkManager) { + workManager.cancelUniqueWork(TAG) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt index ce21be407..f29fc91ce 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt @@ -2,6 +2,9 @@ package eu.kanade.tachiyomi.data.backup.restore import android.content.Context 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.BackupNotifier 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.data.Database import tachiyomi.i18n.MR -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.io.File import java.text.SimpleDateFormat import java.util.Date @@ -36,18 +37,25 @@ import kotlin.concurrent.atomics.ExperimentalAtomicApi import kotlin.concurrent.atomics.incrementAndFetch @OptIn(ExperimentalAtomicApi::class) +@AssistedInject class BackupRestorer( + @Assisted private val notifier: BackupNotifier, + @Assisted private val isSync: Boolean, private val context: Context, - private val notifier: BackupNotifier, - private val isSync: Boolean, - - private val database: Database = Injekt.get(), - private val categoriesRestorer: CategoriesRestorer = CategoriesRestorer(), - private val preferenceRestorer: PreferenceRestorer = PreferenceRestorer(context), - private val extensionStoreRestorer: ExtensionStoreRestorer = ExtensionStoreRestorer(), - private val mangaRestorer: MangaRestorer = MangaRestorer(), + private val database: Database, + private val downloadCache: DownloadCache, + private val categoriesRestorer: CategoriesRestorer, + private val preferenceRestorer: PreferenceRestorer, + private val extensionStoreRestorer: ExtensionStoreRestorer, + private val mangaRestorer: MangaRestorer, + private val backupDecoder: BackupDecoder, ) { + @AssistedFactory + fun interface Factory { + fun create(notifier: BackupNotifier, isSync: Boolean): BackupRestorer + } + private var restoreAmount = 0 private val restoreProgress = AtomicInt(0) private val errors = CopyOnWriteArrayList>() @@ -65,7 +73,7 @@ class BackupRestorer( // Invalidate download cache to ensure UI reflects any restored downloads if (options.libraryEntries) { try { - Injekt.get().invalidateCache() + downloadCache.invalidateCache() } catch (e: Exception) { 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) { - val backup = BackupDecoder(context).decode(uri) + val backup = backupDecoder.decode(uri) // Store source mapping for error messages val backupMaps = backup.backupSources diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/CategoriesRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/CategoriesRestorer.kt index 083590333..16d747e8b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/CategoriesRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/CategoriesRestorer.kt @@ -1,16 +1,16 @@ package eu.kanade.tachiyomi.data.backup.restore.restorers +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.data.backup.models.BackupCategory import tachiyomi.data.Database import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.library.service.LibraryPreferences -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +@Inject class CategoriesRestorer( - private val database: Database = Injekt.get(), - private val getCategories: GetCategories = Injekt.get(), - private val libraryPreferences: LibraryPreferences = Injekt.get(), + private val database: Database, + private val getCategories: GetCategories, + private val libraryPreferences: LibraryPreferences, ) { suspend operator fun invoke(backupCategories: List) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/ExtensionStoreRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/ExtensionStoreRestorer.kt index a8fc4ddcf..7b895b159 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/ExtensionStoreRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/ExtensionStoreRestorer.kt @@ -1,12 +1,12 @@ package eu.kanade.tachiyomi.data.backup.restore.restorers +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.data.backup.models.BackupExtensionStore import tachiyomi.data.Database -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +@Inject class ExtensionStoreRestorer( - private val database: Database = Injekt.get(), + private val database: Database, ) { suspend operator fun invoke( diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt index 5257fdeb8..2c12099c6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt @@ -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.awaitAsOne import app.cash.sqldelight.async.coroutines.awaitAsOneOrNull +import dev.zacsweers.metro.Inject import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.tachiyomi.data.backup.models.BackupCategory 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.InsertTrack import tachiyomi.domain.track.model.Track -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.util.Date import kotlin.math.max import kotlin.time.Clock +@Inject class MangaRestorer( - private val database: Database = Injekt.get(), - private val getCategories: GetCategories = Injekt.get(), - private val getMangaByUrlAndSourceId: GetMangaByUrlAndSourceId = Injekt.get(), - private val getChaptersByMangaId: GetChaptersByMangaId = Injekt.get(), - private val updateManga: UpdateManga = Injekt.get(), - private val getTracks: GetTracks = Injekt.get(), - private val insertTrack: InsertTrack = Injekt.get(), - fetchInterval: FetchInterval = Injekt.get(), + private val database: Database, + private val getCategories: GetCategories, + private val getMangaByUrlAndSourceId: GetMangaByUrlAndSourceId, + private val getChaptersByMangaId: GetChaptersByMangaId, + private val updateManga: UpdateManga, + private val getTracks: GetTracks, + private val insertTrack: InsertTrack, + fetchInterval: FetchInterval, ) { private val timeZone = TimeZone.currentSystemDefault() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/PreferenceRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/PreferenceRestorer.kt index 0417a6963..766262d68 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/PreferenceRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/PreferenceRestorer.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.backup.restore.restorers import android.content.Context import android.util.Log +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob import eu.kanade.tachiyomi.data.backup.models.BackupCategory 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.download.service.DownloadPreferences import tachiyomi.domain.library.service.LibraryPreferences -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +@Inject class PreferenceRestorer( private val context: Context, - private val getCategories: GetCategories = Injekt.get(), - private val preferenceStore: PreferenceStore = Injekt.get(), + private val getCategories: GetCategories, + private val preferenceStore: PreferenceStore, ) { suspend fun restoreApp( preferences: List, @@ -45,7 +45,7 @@ class PreferenceRestorer( suspend fun restoreSource(preferences: List) { preferences.forEach { - val sourcePrefs = AndroidPreferenceStore(context, sourcePreferences(it.sourceKey)) + val sourcePrefs = AndroidPreferenceStore(sourcePreferences(it.sourceKey)) restorePreferences(it.prefs, sourcePrefs) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt index 2913b2fdb..dfeb784fc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt @@ -3,6 +3,9 @@ package eu.kanade.tachiyomi.data.cache import android.content.Context import android.text.format.Formatter 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.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.saveTo @@ -24,6 +27,8 @@ import java.io.IOException * * @param context the application context. */ +@Inject +@SingleIn(AppScope::class) class ChapterCache( private val context: Context, private val json: Json, diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt index 73d2e0833..7ed4761c7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt @@ -1,6 +1,9 @@ package eu.kanade.tachiyomi.data.cache 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 tachiyomi.domain.manga.model.Manga import java.io.File @@ -15,6 +18,8 @@ import java.io.InputStream * @param context the application context. * @constructor creates an instance of the cover cache. */ +@Inject +@SingleIn(AppScope::class) class CoverCache(private val context: Context) { companion object { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt index 8ccd6d4ba..6d960a432 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt @@ -31,7 +31,6 @@ import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.MangaCover import tachiyomi.domain.source.service.SourceManager -import uy.kohesive.injekt.injectLazy import java.io.File import java.io.IOException @@ -299,11 +298,10 @@ class MangaCoverFetcher( class MangaFactory( private val callFactoryLazy: Lazy, + private val coverCache: CoverCache, + private val sourceManager: SourceManager, ) : Fetcher.Factory { - private val coverCache: CoverCache by injectLazy() - private val sourceManager: SourceManager by injectLazy() - override fun create(data: Manga, options: Options, imageLoader: ImageLoader): Fetcher { return MangaCoverFetcher( url = data.thumbnailUrl, @@ -321,11 +319,10 @@ class MangaCoverFetcher( class MangaCoverFactory( private val callFactoryLazy: Lazy, + private val coverCache: CoverCache, + private val sourceManager: SourceManager, ) : Fetcher.Factory { - private val coverCache: CoverCache by injectLazy() - private val sourceManager: SourceManager by injectLazy() - override fun create(data: MangaCover, options: Options, imageLoader: ImageLoader): Fetcher { return MangaCoverFetcher( url = data.url, diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt index 56e0291aa..c4059127d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt @@ -5,8 +5,6 @@ import coil3.request.Options import eu.kanade.domain.manga.model.hasCustomCover import eu.kanade.tachiyomi.data.cache.CoverCache 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 class MangaKeyer : Keyer { @@ -20,7 +18,7 @@ class MangaKeyer : Keyer { } class MangaCoverKeyer( - private val coverCache: CoverCache = Injekt.get(), + private val coverCache: CoverCache, ) : Keyer { override fun key(data: MangaCover, options: Options): String { return if (coverCache.getCustomCoverFile(data.mangaId).exists()) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt index abc60e9ce..9167fca8b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt @@ -1,9 +1,11 @@ package eu.kanade.tachiyomi.data.download -import android.app.Application import android.content.Context import androidx.core.net.toUri 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.source.Source 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 * delete the folders at any time without the app noticing. */ +@Inject +@SingleIn(AppScope::class) class DownloadCache( private val context: Context, - private val provider: DownloadProvider = Injekt.get(), - private val sourceManager: SourceManager = Injekt.get(), - private val extensionManager: ExtensionManager = Injekt.get(), - private val storageManager: StorageManager = Injekt.get(), + private val provider: DownloadProvider, + private val sourceManager: SourceManager, + private val extensionManager: ExtensionManager, + private val storageManager: StorageManager, ) { private val scope = CoroutineScope(Dispatchers.IO) @@ -492,7 +496,7 @@ private object UniFileAsStringSerializer : KSerializer { override fun deserialize(decoder: Decoder): UniFile? { return if (decoder.decodeNotNullMark()) { - UniFile.fromUri(Injekt.get(), decoder.decodeString().toUri()) + UniFile.fromUri(Injekt.get(), decoder.decodeString().toUri()) } else { decoder.decodeNull() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadJob.kt index b503629d6..8bebccdb7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadJob.kt @@ -11,6 +11,7 @@ import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkInfo import androidx.work.WorkManager import androidx.work.WorkerParameters +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.notification.Notifications 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.map import kotlinx.coroutines.flow.onEach +import mihon.app.di.AppGraph +import mihon.core.metro.metroGraph 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 @@ -34,8 +35,15 @@ import uy.kohesive.injekt.api.get */ class DownloadJob(context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) { - private val downloadManager: DownloadManager = Injekt.get() - private val downloadPreferences: DownloadPreferences = Injekt.get() + private val graph: AppGraph = context.metroGraph() + + @Inject private lateinit var downloadManager: DownloadManager + + @Inject private lateinit var downloadPreferences: DownloadPreferences + + init { + graph.inject(this) + } override suspend fun getForegroundInfo(): ForegroundInfo { val notification = applicationContext.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_PROGRESS) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt index 8255b22c8..3dfc6e471 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt @@ -1,9 +1,13 @@ package eu.kanade.tachiyomi.data.download 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.source.Source import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.online.HttpSource import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.asFlow 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.logcat import tachiyomi.domain.category.interactor.GetCategories +import tachiyomi.domain.chapter.interactor.GetChapter import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.download.service.DownloadPreferences +import tachiyomi.domain.manga.interactor.GetManga import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.service.SourceManager 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 * and retrieved through dependency injection. You can use this class to queue new chapters or query * downloaded chapters. */ +@Inject +@SingleIn(AppScope::class) class DownloadManager( private val context: Context, - private val provider: DownloadProvider = Injekt.get(), - private val cache: DownloadCache = Injekt.get(), - private val getCategories: GetCategories = Injekt.get(), - private val sourceManager: SourceManager = Injekt.get(), - private val downloadPreferences: DownloadPreferences = Injekt.get(), + private val provider: DownloadProvider, + private val cache: DownloadCache, + private val getCategories: GetCategories, + private val getManga: GetManga, + 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 get() = downloader.isRunning - /** - * Queue to delay the deletion of a list of chapters until triggered. - */ - private val pendingDeleter = DownloadPendingDeleter(context) - val queueState get() = downloader.queueState @@ -107,7 +107,7 @@ class DownloadManager( fun startDownloadNow(chapterId: Long) { val existingDownload = getQueuedDownloadOrNull(chapterId) // 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 { existingDownload?.let { remove(it) } add(0, toAdd) @@ -116,6 +116,14 @@ class DownloadManager( 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. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt index 47f48c38e..93a460dc9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt @@ -4,6 +4,7 @@ import android.app.PendingIntent import android.content.Context import android.graphics.BitmapFactory import androidx.core.app.NotificationCompat +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.core.security.SecurityPreferences 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 tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR -import uy.kohesive.injekt.injectLazy import java.util.regex.Pattern /** @@ -24,9 +24,11 @@ import java.util.regex.Pattern * * @param context context of application */ -internal class DownloadNotifier(private val context: Context) { - - private val preferences: SecurityPreferences by injectLazy() +@Inject +class DownloadNotifier( + private val context: Context, + private val preferences: SecurityPreferences, +) { private val progressNotificationBuilder by lazy { context.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_PROGRESS) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadPendingDeleter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadPendingDeleter.kt index 26e09a7d5..fd53d0c0c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadPendingDeleter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadPendingDeleter.kt @@ -2,21 +2,24 @@ package eu.kanade.tachiyomi.data.download import android.content.Context 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.json.Json import tachiyomi.domain.chapter.model.Chapter 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. * * @param context the application context. */ +@Inject +@SingleIn(AppScope::class) class DownloadPendingDeleter( context: Context, - private val json: Json = Injekt.get(), + private val json: Json, ) { /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt index 32de76feb..e4d2ec18c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt @@ -2,6 +2,9 @@ package eu.kanade.tachiyomi.data.download import android.content.Context 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.util.lang.Hash.md5 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.storage.service.StorageManager import tachiyomi.i18n.MR -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.io.IOException /** @@ -24,10 +25,12 @@ import java.io.IOException * * @param context the application context. */ +@Inject +@SingleIn(AppScope::class) class DownloadProvider( private val context: Context, - private val storageManager: StorageManager = Injekt.get(), - private val libraryPreferences: LibraryPreferences = Injekt.get(), + private val storageManager: StorageManager, + private val libraryPreferences: LibraryPreferences, ) { private val downloadsDir: UniFile? diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadStore.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadStore.kt index cd4d81a88..e5a8e5bb0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadStore.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadStore.kt @@ -2,6 +2,9 @@ package eu.kanade.tachiyomi.data.download import android.content.Context 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.source.online.HttpSource import kotlinx.coroutines.runBlocking @@ -11,18 +14,18 @@ import tachiyomi.domain.chapter.interactor.GetChapter import tachiyomi.domain.manga.interactor.GetManga import tachiyomi.domain.manga.model.Manga 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. */ +@Inject +@SingleIn(AppScope::class) class DownloadStore( context: Context, - private val sourceManager: SourceManager = Injekt.get(), - private val json: Json = Injekt.get(), - private val getManga: GetManga = Injekt.get(), - private val getChapter: GetChapter = Injekt.get(), + private val sourceManager: SourceManager, + private val json: Json, + private val getManga: GetManga, + private val getChapter: GetChapter, ) { /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index a1e5544ed..410e764a7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -2,6 +2,9 @@ package eu.kanade.tachiyomi.data.download import android.content.Context 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.manga.model.getComicInfo 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.track.interactor.GetTracks import tachiyomi.i18n.MR -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.io.File import java.util.Locale 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. */ +@Inject +@SingleIn(AppScope::class) class Downloader( private val context: Context, private val provider: DownloadProvider, private val cache: DownloadCache, - private val sourceManager: SourceManager = Injekt.get(), - private val chapterCache: ChapterCache = Injekt.get(), - private val downloadPreferences: DownloadPreferences = Injekt.get(), - private val xml: XML = Injekt.get(), - private val getCategories: GetCategories = Injekt.get(), - private val getTracks: GetTracks = Injekt.get(), + private val sourceManager: SourceManager, + private val chapterCache: ChapterCache, + private val downloadPreferences: DownloadPreferences, + private val xml: XML, + private val getCategories: GetCategories, + 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. */ private val _queueState = MutableStateFlow>(emptyList()) 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 var downloaderJob: Job? = null diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/model/Download.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/model/Download.kt index de5913865..f178da58c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/model/Download.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/model/Download.kt @@ -10,13 +10,8 @@ import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.flow -import tachiyomi.domain.chapter.interactor.GetChapter import tachiyomi.domain.chapter.model.Chapter -import tachiyomi.domain.manga.interactor.GetManga 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 data class Download( @@ -71,19 +66,4 @@ data class Download( DOWNLOADED(3), 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) - } - } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt index f208966fd..7e94c0fa7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt @@ -15,9 +15,11 @@ import androidx.work.NetworkType import androidx.work.OneTimeWorkRequestBuilder import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkInfo +import androidx.work.WorkManager import androidx.work.WorkQuery import androidx.work.WorkerParameters import androidx.work.workDataOf +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.source.model.SManga @@ -38,6 +40,9 @@ import kotlinx.coroutines.sync.withPermit import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime 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.source.interactor.UpdateMangaFromRemote 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.service.SourceManager import tachiyomi.i18n.MR -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.io.File import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.TimeUnit @@ -78,24 +81,34 @@ import kotlin.time.Clock class LibraryUpdateJob(private val context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) { - private val sourceManager: SourceManager = Injekt.get() - 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 graph: AppGraph = context.metroGraph() - 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 = mutableListOf() override suspend fun doWork(): Result { + graph.inject(this) + if (tags.contains(WORK_NAME_AUTO)) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { - val preferences = Injekt.get() - val restrictions = preferences.autoUpdateDeviceRestrictions.get() + val restrictions = libraryPreferences.autoUpdateDeviceRestrictions.get() if ((DEVICE_ONLY_ON_WIFI in restrictions) && !context.isConnectedToWifi()) { return Result.retry() } @@ -133,7 +146,6 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet } override suspend fun getForegroundInfo(): ForegroundInfo { - val notifier = LibraryUpdateNotifier(context) return ForegroundInfo( Notifications.ID_LIBRARY_PROGRESS, notifier.progressNotificationBuilder.build(), @@ -420,7 +432,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet context: Context, prefInterval: Int? = null, ) { - val preferences = Injekt.get() + val preferences = context.appGraph.libraryPreferences val interval = prefInterval ?: preferences.autoUpdateInterval.get() if (interval > 0) { val restrictions = preferences.autoUpdateDeviceRestrictions.get() @@ -469,11 +481,10 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet } fun startNow( - context: Context, + workManager: WorkManager, category: Category? = null, ): Boolean { - val wm = context.workManager - if (wm.isRunning(TAG)) { + if (workManager.isRunning(TAG)) { // Already running either as a scheduled or manual job return false } @@ -486,20 +497,20 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet .addTag(WORK_NAME_MANUAL) .setInputData(inputData) .build() - wm.enqueueUniqueWork(WORK_NAME_MANUAL, ExistingWorkPolicy.KEEP, request) + workManager.enqueueUniqueWork(WORK_NAME_MANUAL, ExistingWorkPolicy.KEEP, request) return true } fun stop(context: Context) { - val wm = context.workManager + val workManager = context.workManager val workQuery = WorkQuery.Builder.fromTags(listOf(TAG)) .addStates(listOf(WorkInfo.State.RUNNING)) .build() - wm.getWorkInfos(workQuery).get() + workManager.getWorkInfos(workQuery).get() // Should only return one work but just in case .forEach { - wm.cancelWorkById(it.id) + workManager.cancelWorkById(it.id) // Re-enqueue cancelled scheduled work if (it.tags.contains(WORK_NAME_AUTO)) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt index b7f60fdd8..a9f01350c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt @@ -14,6 +14,7 @@ import coil3.imageLoader import coil3.request.ImageRequest import coil3.request.transformations import coil3.transform.CircleCropTransformation +import dev.zacsweers.metro.Inject import eu.kanade.presentation.util.formatChapterNumber import eu.kanade.tachiyomi.R 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.source.service.SourceManager import tachiyomi.i18n.MR -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.math.RoundingMode import java.text.NumberFormat +@Inject class LibraryUpdateNotifier( private val context: Context, - private val securityPreferences: SecurityPreferences = Injekt.get(), - private val sourceManager: SourceManager = Injekt.get(), + private val securityPreferences: SecurityPreferences, + private val sourceManager: SourceManager, ) { private val percentFormatter = NumberFormat.getPercentInstance().apply { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/MetadataUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/MetadataUpdateJob.kt index 966de7fe9..8ce0bd4c6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/MetadataUpdateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/MetadataUpdateJob.kt @@ -7,13 +7,12 @@ import androidx.work.CoroutineWorker import androidx.work.ExistingWorkPolicy import androidx.work.ForegroundInfo import androidx.work.OneTimeWorkRequestBuilder -import androidx.work.WorkInfo -import androidx.work.WorkQuery +import androidx.work.WorkManager import androidx.work.WorkerParameters +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.util.system.isRunning import eu.kanade.tachiyomi.util.system.setForegroundSafely -import eu.kanade.tachiyomi.util.system.workManager import kotlinx.coroutines.CancellationException import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll @@ -22,6 +21,8 @@ import kotlinx.coroutines.ensureActive import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit import logcat.LogPriority +import mihon.app.di.AppGraph +import mihon.core.metro.metroGraph import mihon.domain.source.interactor.UpdateMangaFromRemote import tachiyomi.core.common.util.lang.withIOContext 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.model.Manga import tachiyomi.domain.source.service.SourceManager -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.util.concurrent.CopyOnWriteArrayList import kotlin.concurrent.atomics.AtomicInt import kotlin.concurrent.atomics.ExperimentalAtomicApi @@ -40,15 +39,21 @@ import kotlin.concurrent.atomics.fetchAndIncrement class MetadataUpdateJob(private val context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) { - private val sourceManager: SourceManager = Injekt.get() - private val getLibraryManga: GetLibraryManga = Injekt.get() - private val updateMangaFromRemote: UpdateMangaFromRemote = Injekt.get() + private val graph: AppGraph = context.metroGraph() - 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 = mutableListOf() override suspend fun doWork(): Result { + graph.inject(this) + setForegroundSafely() addMangaToQueue() @@ -72,7 +77,6 @@ class MetadataUpdateJob(private val context: Context, workerParams: WorkerParame } override suspend fun getForegroundInfo(): ForegroundInfo { - val notifier = LibraryUpdateNotifier(context) return ForegroundInfo( Notifications.ID_LIBRARY_PROGRESS, 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 - fun startNow(context: Context): Boolean { - val wm = context.workManager - if (wm.isRunning(TAG)) { + fun startNow(workManager: WorkManager): Boolean { + if (workManager.isRunning(TAG)) { // Already running either as a scheduled or manual job return false } @@ -178,21 +181,9 @@ class MetadataUpdateJob(private val context: Context, workerParams: WorkerParame .addTag(TAG) .addTag(WORK_NAME_MANUAL) .build() - wm.enqueueUniqueWork(WORK_NAME_MANUAL, ExistingWorkPolicy.KEEP, request) + workManager.enqueueUniqueWork(WORK_NAME_MANUAL, ExistingWorkPolicy.KEEP, request) 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) - } - } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt index 7689f471f..5bd3f30e9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt @@ -6,6 +6,7 @@ import android.content.Context import android.content.Intent import android.net.Uri import androidx.core.net.toUri +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob import eu.kanade.tachiyomi.data.download.DownloadManager 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.toShareIntent import eu.kanade.tachiyomi.util.system.toast +import eu.kanade.tachiyomi.util.system.workManager import kotlinx.coroutines.runBlocking +import mihon.app.di.appGraph import tachiyomi.core.common.Constants import tachiyomi.core.common.util.lang.launchIO 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.source.service.SourceManager 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 /** @@ -40,12 +40,21 @@ import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID */ class NotificationReceiver : BroadcastReceiver() { - private val getManga: GetManga by injectLazy() - private val getChapter: GetChapter by injectLazy() - private val updateChapter: UpdateChapter by injectLazy() - private val downloadManager: DownloadManager by injectLazy() + @Inject private lateinit var getManga: GetManga + + @Inject private lateinit var getChapter: GetChapter + + @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) { + context.appGraph.inject(this) + when (intent.action) { // Dismiss notification ACTION_DISMISS_NOTIFICATION -> dismissNotification(context, intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)) @@ -161,7 +170,7 @@ class NotificationReceiver : BroadcastReceiver() { * @param context context of application */ private fun cancelRestore(context: Context) { - BackupRestoreJob.stop(context) + BackupRestoreJob.stop(context.workManager) } /** @@ -180,9 +189,6 @@ class NotificationReceiver : BroadcastReceiver() { * @param mangaId id of manga */ private fun markAsRead(chapterUrls: Array, mangaId: Long) { - val downloadPreferences: DownloadPreferences = Injekt.get() - val sourceManager: SourceManager = Injekt.get() - launchIO { val toUpdate = chapterUrls.mapNotNull { getChapter.await(it, mangaId) } .map { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt index be52184a5..9c3f202c6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt @@ -11,6 +11,9 @@ import android.webkit.MimeTypeMap import androidx.annotation.RequiresApi import androidx.core.content.contentValuesOf 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.cacheImageDir import eu.kanade.tachiyomi.util.storage.getUriCompat @@ -26,6 +29,8 @@ import java.io.File import java.io.InputStream import kotlin.time.Clock +@Inject +@SingleIn(AppScope::class) class ImageSaver( val context: Context, ) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt index 57d775acf..76def2146 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt @@ -1,6 +1,6 @@ package eu.kanade.tachiyomi.data.track -import android.app.Application +import android.content.Context import androidx.annotation.CallSuper import eu.kanade.domain.track.interactor.AddTracks import eu.kanade.domain.track.model.toDomainTrack @@ -11,6 +11,7 @@ import eu.kanade.tachiyomi.util.system.toast import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import logcat.LogPriority +import mihon.app.di.appGraph import okhttp3.OkHttpClient import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.lang.withUIContext @@ -18,7 +19,6 @@ import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.track.interactor.InsertTrack import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import uy.kohesive.injekt.injectLazy import tachiyomi.domain.track.model.Track as DomainTrack abstract class BaseTracker( @@ -26,10 +26,14 @@ abstract class BaseTracker( override val name: String, ) : Tracker { - val trackPreferences: TrackPreferences by injectLazy() - val networkService: NetworkHelper by injectLazy() - private val addTracks: AddTracks by injectLazy() - private val insertTrack: InsertTrack by injectLazy() + protected val appGraph get() = Injekt.get().appGraph + + val trackPreferences: TrackPreferences by lazy { appGraph.trackPreferences } + val networkService: NetworkHelper by lazy { appGraph.networkHelper } + + private val context: Context by lazy { appGraph.context } + private val addTracks: AddTracks by lazy { appGraph.addTracks } + private val insertTrack: InsertTrack by lazy { appGraph.insertTrack } override val client: OkHttpClient get() = networkService.client @@ -83,7 +87,9 @@ abstract class BaseTracker( try { addTracks.bind(this, item, mangaId) } catch (e: Throwable) { - withUIContext { Injekt.get().toast(e.message) } + withUIContext { + context.toast(e.message) + } } } @@ -139,7 +145,9 @@ abstract class BaseTracker( } } catch (e: Exception) { logcat(LogPriority.ERROR, e) { "Failed to update remote track data id=$id" } - withUIContext { Injekt.get().toast(e.message) } + withUIContext { + context.toast(e.message) + } } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt index 6db63d3f0..95c98ff13 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackerManager.kt @@ -1,5 +1,8 @@ package eu.kanade.tachiyomi.data.track +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import eu.kanade.tachiyomi.data.track.anilist.Anilist import eu.kanade.tachiyomi.data.track.bangumi.Bangumi import eu.kanade.tachiyomi.data.track.hikka.Hikka @@ -13,6 +16,8 @@ import eu.kanade.tachiyomi.data.track.shikimori.Shikimori import eu.kanade.tachiyomi.data.track.suwayomi.Suwayomi import kotlinx.coroutines.flow.combine +@Inject +@SingleIn(AppScope::class) class TrackerManager { companion object { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt index 03f4fa8c9..0d12ba086 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt @@ -10,7 +10,6 @@ import eu.kanade.tachiyomi.data.track.anilist.dto.ALOAuth import eu.kanade.tachiyomi.data.track.model.TrackSearch import kotlinx.serialization.json.Json import tachiyomi.i18n.MR -import uy.kohesive.injekt.injectLazy import tachiyomi.domain.track.model.Track as DomainTrack class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker { @@ -32,7 +31,7 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker { private const val SEARCH_ID_PREFIX = "id:" } - private val json: Json by injectLazy() + private val json: Json by lazy { appGraph.json } private val interceptor by lazy { AnilistInterceptor(this, getPassword()) } @@ -42,7 +41,7 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker { override val supportsPrivateTracking: Boolean = true - private val scorePreference = trackPreferences.anilistScoreType + private val scorePreference by lazy { trackPreferences.anilistScoreType } init { // If the preference is an int from APIv1, logout user to force using APIv2 @@ -135,7 +134,7 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker { else -> "😊" } - else -> track.toApiScore() + else -> track.toApiScore(appGraph.trackPreferences) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistUtils.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistUtils.kt index 59d0937a6..1a6f02dea 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistUtils.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistUtils.kt @@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.data.track.anilist import eu.kanade.domain.track.service.TrackPreferences import eu.kanade.tachiyomi.data.database.models.Track -import uy.kohesive.injekt.injectLazy import tachiyomi.domain.track.model.Track as DomainTrack fun Track.toApiStatus() = when (status) { @@ -15,9 +14,7 @@ fun Track.toApiStatus() = when (status) { else -> throw NotImplementedError("Unknown status: $status") } -private val preferences: TrackPreferences by injectLazy() - -fun DomainTrack.toApiScore(): String = when (preferences.anilistScoreType.get()) { +fun DomainTrack.toApiScore(preferences: TrackPreferences): String = when (preferences.anilistScoreType.get()) { // 10 point "POINT_10" -> (score.toInt() / 10).toString() // 100 point diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/Kavita.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/Kavita.kt index ab66e0324..5e91912a9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/Kavita.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/Kavita.kt @@ -12,7 +12,6 @@ import eu.kanade.tachiyomi.source.sourcePreferences import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.service.SourceManager import tachiyomi.i18n.MR -import uy.kohesive.injekt.injectLazy import java.security.MessageDigest import tachiyomi.domain.track.model.Track as DomainTrack @@ -29,7 +28,7 @@ class Kavita(id: Long) : BaseTracker(id, "Kavita"), EnhancedTracker { private val interceptor by lazy { KavitaInterceptor(this) } val api by lazy { KavitaApi(client, interceptor) } - private val sourceManager: SourceManager by injectLazy() + private val sourceManager: SourceManager by lazy { appGraph.sourceManager } override fun getLogo(): Int = R.drawable.brand_kavita diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/Komga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/Komga.kt index 8eb1a0f09..ae0ea63fd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/Komga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/Komga.kt @@ -21,10 +21,11 @@ class Komga(id: Long) : BaseTracker(id, "Komga"), EnhancedTracker { const val COMPLETED = 3L } - override val client: OkHttpClient = + override val client: OkHttpClient by lazy { networkService.client.newBuilder() .dns(Dns.SYSTEM) // don't use DNS over HTTPS as it breaks IP addressing .build() + } val api by lazy { KomgaApi(id, client) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/Suwayomi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/Suwayomi.kt index 56fa46c4c..50d297b83 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/Suwayomi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/Suwayomi.kt @@ -13,7 +13,7 @@ import tachiyomi.domain.track.model.Track as DomainTrack class Suwayomi(id: Long) : BaseTracker(id, "Suwayomi"), EnhancedTracker { - val api by lazy { SuwayomiApi(id) } + val api by lazy { SuwayomiApi(id, appGraph.sourceManager) } override fun getLogo() = R.drawable.brand_suwayomi diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiApi.kt index 7c2c96751..f1b91f22d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiApi.kt @@ -23,11 +23,12 @@ import tachiyomi.domain.source.service.SourceManager import uy.kohesive.injekt.injectLazy import java.security.MessageDigest -class SuwayomiApi(private val trackId: Long) { +class SuwayomiApi( + private val trackId: Long, + private val sourceManager: SourceManager, +) { private val json: Json by injectLazy() - - private val sourceManager: SourceManager by injectLazy() private val source: HttpSource by lazy { (sourceManager.get(sourceId) as HttpSource) } private val configurableSource: ConfigurableSource by lazy { (sourceManager.get(sourceId) as ConfigurableSource) } private val client: OkHttpClient by lazy { source.client } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt index f1d450a23..99ace8785 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt @@ -1,15 +1,16 @@ package eu.kanade.tachiyomi.data.updater +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.util.system.isFossBuildType import eu.kanade.tachiyomi.util.system.isNightlyBuildType import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.domain.release.interactor.GetApplicationRelease -import uy.kohesive.injekt.injectLazy -class AppUpdateChecker { - - private val getApplicationRelease: GetApplicationRelease by injectLazy() +@Inject +class AppUpdateChecker( + private val getApplicationRelease: GetApplicationRelease, +) { suspend fun checkForUpdate(forceCheck: Boolean = false): GetApplicationRelease.Result { // Disable app update checks for older Android versions that we're going to drop support for diff --git a/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt b/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt deleted file mode 100644 index ed3f940d8..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt +++ /dev/null @@ -1,146 +0,0 @@ -package eu.kanade.tachiyomi.di - -import android.app.Application -import android.content.Context -import androidx.core.content.ContextCompat -import androidx.sqlite.driver.bundled.BundledSQLiteDriver -import app.cash.sqldelight.db.SqlDriver -import com.eygraber.sqldelight.androidx.driver.AndroidxSqliteConfiguration -import com.eygraber.sqldelight.androidx.driver.AndroidxSqliteDatabaseType -import com.eygraber.sqldelight.androidx.driver.AndroidxSqliteDriver -import com.eygraber.sqldelight.androidx.driver.FileProvider -import eu.kanade.domain.track.store.DelayedTrackingStore -import eu.kanade.tachiyomi.data.cache.ChapterCache -import eu.kanade.tachiyomi.data.cache.CoverCache -import eu.kanade.tachiyomi.data.download.DownloadCache -import eu.kanade.tachiyomi.data.download.DownloadManager -import eu.kanade.tachiyomi.data.download.DownloadProvider -import eu.kanade.tachiyomi.data.saver.ImageSaver -import eu.kanade.tachiyomi.data.track.TrackerManager -import eu.kanade.tachiyomi.extension.ExtensionManager -import eu.kanade.tachiyomi.network.JavaScriptEngine -import eu.kanade.tachiyomi.network.NetworkHelper -import eu.kanade.tachiyomi.source.AndroidSourceManager -import kotlinx.serialization.json.Json -import kotlinx.serialization.protobuf.ProtoBuf -import nl.adaptivity.xmlutil.XmlDeclMode -import nl.adaptivity.xmlutil.core.XmlVersion -import nl.adaptivity.xmlutil.serialization.XML -import tachiyomi.core.common.storage.AndroidStorageFolderProvider -import tachiyomi.data.Chapters -import tachiyomi.data.Database -import tachiyomi.data.DateColumnAdapter -import tachiyomi.data.History -import tachiyomi.data.Mangas -import tachiyomi.data.MemoColumnAdapter -import tachiyomi.data.StringListColumnAdapter -import tachiyomi.data.UpdateStrategyColumnAdapter -import tachiyomi.domain.source.service.SourceManager -import tachiyomi.domain.storage.service.StorageManager -import tachiyomi.source.local.image.LocalCoverManager -import tachiyomi.source.local.io.LocalSourceFileSystem -import uy.kohesive.injekt.api.InjektModule -import uy.kohesive.injekt.api.InjektRegistrar -import uy.kohesive.injekt.api.addSingleton -import uy.kohesive.injekt.api.addSingletonFactory -import uy.kohesive.injekt.api.get -import java.lang.ref.WeakReference - -private val lock = Any() - -class AppModule(val app: Application) : InjektModule { - - private var sqlDriverRef: WeakReference? = null - - override fun InjektRegistrar.registerInjectables() { - addSingleton(app) - addSingleton(app) - - addSingletonFactory { - synchronized(lock) { - sqlDriverRef?.get()?.let { return@synchronized it } - - AndroidxSqliteDriver( - driver = BundledSQLiteDriver(), - databaseType = AndroidxSqliteDatabaseType.FileProvider(app, "tachiyomi.db"), - schema = Database.Schema, - configuration = AndroidxSqliteConfiguration( - isForeignKeyConstraintsEnabled = true, - ), - ) - .also { sqlDriverRef = WeakReference(it) } - } - } - addSingletonFactory { - Database( - driver = get(), - historyAdapter = History.Adapter( - last_readAdapter = DateColumnAdapter, - ), - mangasAdapter = Mangas.Adapter( - genreAdapter = StringListColumnAdapter, - update_strategyAdapter = UpdateStrategyColumnAdapter, - memoAdapter = MemoColumnAdapter, - ), - chaptersAdapter = Chapters.Adapter( - memoAdapter = MemoColumnAdapter, - ), - ) - } - - addSingletonFactory { - Json { - ignoreUnknownKeys = true - explicitNulls = false - } - } - addSingletonFactory { - XML.v1 { - policy { - ignoreUnknownChildren() - autoPolymorphic = true - } - xmlDeclMode = XmlDeclMode.Charset - xmlVersion = XmlVersion.XML10 - setIndent(2) - } - } - addSingletonFactory { - ProtoBuf - } - - addSingletonFactory { ChapterCache(app, get()) } - addSingletonFactory { CoverCache(app) } - - addSingletonFactory { NetworkHelper(app, get()) } - addSingletonFactory { JavaScriptEngine(app) } - - addSingletonFactory { AndroidSourceManager(app, get(), get()) } - addSingletonFactory { ExtensionManager(app) } - - addSingletonFactory { DownloadProvider(app) } - addSingletonFactory { DownloadManager(app) } - addSingletonFactory { DownloadCache(app) } - - addSingletonFactory { TrackerManager() } - addSingletonFactory { DelayedTrackingStore(app) } - - addSingletonFactory { ImageSaver(app) } - - addSingletonFactory { AndroidStorageFolderProvider(app) } - addSingletonFactory { LocalSourceFileSystem(get()) } - addSingletonFactory { LocalCoverManager(app, get()) } - addSingletonFactory { StorageManager(app, get()) } - - // Asynchronously init expensive components for a faster cold start - ContextCompat.getMainExecutor(app).execute { - get() - - get() - - get() - - get() - } - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/di/PreferenceModule.kt b/app/src/main/java/eu/kanade/tachiyomi/di/PreferenceModule.kt deleted file mode 100644 index e8e3e9864..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/di/PreferenceModule.kt +++ /dev/null @@ -1,82 +0,0 @@ -package eu.kanade.tachiyomi.di - -import android.app.Application -import eu.kanade.domain.base.BasePreferences -import eu.kanade.domain.source.service.SourcePreferences -import eu.kanade.domain.track.service.TrackPreferences -import eu.kanade.domain.ui.UiPreferences -import eu.kanade.tachiyomi.core.security.PrivacyPreferences -import eu.kanade.tachiyomi.core.security.SecurityPreferences -import eu.kanade.tachiyomi.network.NetworkPreferences -import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences -import eu.kanade.tachiyomi.util.system.isDebugBuildType -import tachiyomi.core.common.preference.AndroidPreferenceStore -import tachiyomi.core.common.preference.PreferenceStore -import tachiyomi.core.common.storage.AndroidStorageFolderProvider -import tachiyomi.domain.backup.service.BackupPreferences -import tachiyomi.domain.download.service.DownloadPreferences -import tachiyomi.domain.library.service.LibraryPreferences -import tachiyomi.domain.storage.service.StoragePreferences -import tachiyomi.domain.upcoming.service.UpcomingPreferences -import tachiyomi.domain.updates.service.UpdatesPreferences -import uy.kohesive.injekt.api.InjektModule -import uy.kohesive.injekt.api.InjektRegistrar -import uy.kohesive.injekt.api.addSingletonFactory -import uy.kohesive.injekt.api.get - -class PreferenceModule(val app: Application) : InjektModule { - - override fun InjektRegistrar.registerInjectables() { - addSingletonFactory { - AndroidPreferenceStore(app) - } - addSingletonFactory { - NetworkPreferences( - preferenceStore = get(), - verboseLoggingDefault = isDebugBuildType, - ) - } - addSingletonFactory { - SourcePreferences(get()) - } - addSingletonFactory { - SecurityPreferences(get()) - } - addSingletonFactory { - PrivacyPreferences(get()) - } - addSingletonFactory { - LibraryPreferences(get()) - } - addSingletonFactory { - UpcomingPreferences(get()) - } - addSingletonFactory { - UpdatesPreferences(get()) - } - addSingletonFactory { - ReaderPreferences(get()) - } - addSingletonFactory { - TrackPreferences(get()) - } - addSingletonFactory { - DownloadPreferences(get()) - } - addSingletonFactory { - BackupPreferences(get()) - } - addSingletonFactory { - StoragePreferences( - folderProvider = get(), - preferenceStore = get(), - ) - } - addSingletonFactory { - UiPreferences(get()) - } - addSingletonFactory { - BasePreferences(app, get()) - } - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt index 1982555bb..6a8f49597 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt @@ -2,6 +2,9 @@ package eu.kanade.tachiyomi.extension import android.content.Context import android.graphics.drawable.Drawable +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import eu.kanade.domain.extension.interactor.TrustExtension import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.tachiyomi.extension.api.ExtensionApi @@ -30,8 +33,6 @@ import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.source.model.StubSource import tachiyomi.i18n.MR -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.util.Locale /** @@ -41,10 +42,15 @@ import java.util.Locale * signature is trusted, otherwise the user will be prompted with a warning to trust it before being * loaded. */ +@Inject +@SingleIn(AppScope::class) class ExtensionManager( private val context: Context, - private val preferences: SourcePreferences = Injekt.get(), - private val trustExtension: TrustExtension = Injekt.get(), + private val preferences: SourcePreferences, + private val trustExtension: TrustExtension, + private val api: ExtensionApi, + private val installer: ExtensionInstaller, + private val extensionUpdateNotifier: ExtensionUpdateNotifier, ) { val scope = CoroutineScope(SupervisorJob()) @@ -52,16 +58,6 @@ class ExtensionManager( private val _isInitialized = MutableStateFlow(false) val isInitialized: StateFlow = _isInitialized.asStateFlow() - /** - * API where all the available extensions can be found. - */ - private val api = ExtensionApi() - - /** - * The installer which installs, updates and uninstalls the extensions. - */ - private val installer by lazy { ExtensionInstaller(context) } - private val iconMap = mutableMapOf() private val installedExtensionMapFlow = MutableStateFlow(emptyMap()) @@ -373,7 +369,7 @@ class ExtensionManager( val pendingUpdateCount = installedExtensionMapFlow.value.values.count { it.hasUpdate } preferences.extensionUpdatesCount.set(pendingUpdateCount) if (pendingUpdateCount == 0) { - ExtensionUpdateNotifier(context).dismiss() + extensionUpdateNotifier.dismiss() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt index 864f95597..83e2b3f02 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt @@ -1,19 +1,23 @@ package eu.kanade.tachiyomi.extension.api import android.content.Context +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.model.LoadResult import eu.kanade.tachiyomi.extension.util.ExtensionLoader import mihon.domain.extension.interactor.UpdateExtensionStores import mihon.domain.extension.repository.ExtensionStoreRepository import tachiyomi.core.common.util.lang.withIOContext -import uy.kohesive.injekt.injectLazy -internal class ExtensionApi { - - private val repository: ExtensionStoreRepository by injectLazy() - - private val updateExtensionStores: UpdateExtensionStores by injectLazy() +@Inject +@SingleIn(AppScope::class) +class ExtensionApi( + private val repository: ExtensionStoreRepository, + private val updateExtensionStores: UpdateExtensionStores, + private val extensionUpdateNotifier: ExtensionUpdateNotifier, +) { suspend fun findExtensions(): List { return withIOContext { repository.fetchExtensions() } @@ -41,7 +45,7 @@ internal class ExtensionApi { } if (extensionsWithUpdate.isNotEmpty()) { - ExtensionUpdateNotifier(context).promptUpdates(extensionsWithUpdate.map { it.name }) + extensionUpdateNotifier.promptUpdates(extensionsWithUpdate.map { it.name }) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt index dcf322f4c..51f2eb5c7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt @@ -2,6 +2,9 @@ package eu.kanade.tachiyomi.extension.api import android.content.Context import androidx.core.app.NotificationCompat +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.core.security.SecurityPreferences import eu.kanade.tachiyomi.data.notification.NotificationReceiver @@ -10,12 +13,12 @@ import eu.kanade.tachiyomi.util.system.cancelNotification import eu.kanade.tachiyomi.util.system.notify import tachiyomi.core.common.i18n.pluralStringResource import tachiyomi.i18n.MR -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +@Inject +@SingleIn(AppScope::class) class ExtensionUpdateNotifier( private val context: Context, - private val securityPreferences: SecurityPreferences = Injekt.get(), + private val securityPreferences: SecurityPreferences, ) { fun promptUpdates(names: List) { context.notify( diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallActivity.kt index ef73e9de7..68c734155 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallActivity.kt @@ -3,12 +3,12 @@ package eu.kanade.tachiyomi.extension.util import android.app.Activity import android.content.Intent import android.os.Bundle +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.extension.ExtensionManager import eu.kanade.tachiyomi.extension.model.InstallStep import eu.kanade.tachiyomi.util.system.hasMiuiPackageInstaller import eu.kanade.tachiyomi.util.system.toast -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +import mihon.app.di.appGraph import kotlin.time.Duration.Companion.seconds /** @@ -17,6 +17,8 @@ import kotlin.time.Duration.Companion.seconds */ class ExtensionInstallActivity : Activity() { + @Inject private lateinit var extensionManager: ExtensionManager + // MIUI package installer bug workaround private var ignoreUntil = 0L private var ignoreResult = false @@ -24,6 +26,7 @@ class ExtensionInstallActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + appGraph.inject(this) @Suppress("DEPRECATION") val installIntent = Intent(Intent.ACTION_INSTALL_PACKAGE) @@ -71,7 +74,6 @@ class ExtensionInstallActivity : Activity() { private fun checkInstallationResult(resultCode: Int) { val downloadId = intent.extras!!.getLong(ExtensionInstaller.EXTRA_DOWNLOAD_ID) - val extensionManager = Injekt.get() val newStep = when (resultCode) { RESULT_OK -> InstallStep.Installed RESULT_CANCELED -> InstallStep.Idle diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstaller.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstaller.kt index 4d9493ff7..49a8f008a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstaller.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstaller.kt @@ -4,6 +4,7 @@ import android.content.Context import android.content.Intent import androidx.core.content.ContextCompat import androidx.core.net.toUri +import dev.zacsweers.metro.Inject import eu.kanade.domain.base.BasePreferences import eu.kanade.tachiyomi.extension.installer.Installer import eu.kanade.tachiyomi.extension.model.Extension @@ -23,8 +24,6 @@ import logcat.LogPriority import okhttp3.OkHttpClient import okhttp3.Request import tachiyomi.core.common.util.system.logcat -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.io.File /** @@ -32,16 +31,19 @@ import java.io.File * * @param context The application context. */ -internal class ExtensionInstaller( +@Inject +class ExtensionInstaller( private val context: Context, + basePreferences: BasePreferences, + networkHelper: NetworkHelper, ) { private val scope = CoroutineScope(Dispatchers.IO) private val activeJobs = mutableMapOf() private val activeSteps = mutableMapOf>() - private val extensionInstaller = Injekt.get().extensionInstaller + private val extensionInstaller = basePreferences.extensionInstaller - private val httpClient: OkHttpClient = Injekt.get().client + private val httpClient: OkHttpClient = networkHelper.client /** * Adds the given extension to the downloads queue and returns an observable containing its diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt index 105c1b8e8..8c931cb87 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt @@ -7,7 +7,6 @@ import android.content.pm.PackageManager import android.os.Build import androidx.core.content.pm.PackageInfoCompat import eu.kanade.domain.extension.interactor.TrustExtension -import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.model.LoadResult import eu.kanade.tachiyomi.source.Source @@ -20,8 +19,8 @@ import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.runBlocking import logcat.LogPriority +import mihon.app.di.appGraph import tachiyomi.core.common.util.system.logcat -import uy.kohesive.injekt.injectLazy import java.io.File /** @@ -40,12 +39,6 @@ import java.io.File */ internal object ExtensionLoader { - private val preferences: SourcePreferences by injectLazy() - private val trustExtension: TrustExtension by injectLazy() - private val loadNsfwSource by lazy { - preferences.showNsfwSource.get() - } - private const val EXTENSION_FEATURE = "tachiyomi.extension" private const val METADATA_SOURCE_CLASS = "tachiyomi.extension.class" private const val METADATA_SOURCE_FACTORY = "tachiyomi.extension.factory" @@ -228,6 +221,9 @@ internal object ExtensionLoader { * @param extensionInfo The extension to load. */ private suspend fun loadExtension(context: Context, extensionInfo: ExtensionInfo): LoadResult { + val trustExtension: TrustExtension = context.appGraph.trustExtension + val loadNsfwSource: Boolean = context.appGraph.sourcePreferences.showNsfwSource.get() + val pkgManager = context.packageManager val pkgInfo = extensionInfo.packageInfo val appInfo = pkgInfo.applicationInfo!! diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt b/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt index b89985f32..eb33ffde1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt @@ -1,6 +1,9 @@ package eu.kanade.tachiyomi.source -import android.content.Context +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.extension.ExtensionManager import eu.kanade.tachiyomi.source.online.HttpSource @@ -20,22 +23,21 @@ import tachiyomi.domain.source.model.StubSource import tachiyomi.domain.source.repository.StubSourceRepository import tachiyomi.domain.source.service.SourceManager import tachiyomi.source.local.LocalSource -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get -import uy.kohesive.injekt.injectLazy import java.util.concurrent.ConcurrentHashMap +@Inject +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) class AndroidSourceManager( - private val context: Context, private val extensionManager: ExtensionManager, private val sourceRepository: StubSourceRepository, + private val localSource: LocalSource, + private val downloadManager: Lazy, ) : SourceManager { private val _isInitialized = MutableStateFlow(false) override val isInitialized: StateFlow = _isInitialized.asStateFlow() - private val downloadManager: DownloadManager by injectLazy() - private val scope = CoroutineScope(Job() + Dispatchers.IO) private val sourcesMapFlow = MutableStateFlow(ConcurrentHashMap()) @@ -51,13 +53,7 @@ class AndroidSourceManager( extensionManager.installedExtensionsFlow .collectLatest { extensions -> val mutableMap = ConcurrentHashMap( - mapOf( - LocalSource.ID to LocalSource( - context, - Injekt.get(), - Injekt.get(), - ), - ), + mapOf(LocalSource.ID to localSource), ) extensions.forEach { extension -> extension.sources.forEach { @@ -106,7 +102,7 @@ class AndroidSourceManager( if (dbSource == source) return@launch sourceRepository.upsertStubSource(source.id, source.lang, source.name) if (dbSource != null) { - downloadManager.renameSource(dbSource, source) + downloadManager.value.renameSource(dbSource, source) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/SourceExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/source/SourceExtensions.kt index 56face00f..55c9ecf40 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/SourceExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/SourceExtensions.kt @@ -1,13 +1,14 @@ package eu.kanade.tachiyomi.source -import eu.kanade.domain.source.service.SourcePreferences +import android.content.Context +import mihon.app.di.appGraph import tachiyomi.domain.source.model.StubSource import tachiyomi.source.local.isLocal import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get fun Source.getNameForMangaInfo(): String { - val preferences = Injekt.get() + val preferences = Injekt.get().appGraph.sourcePreferences val enabledLanguages = preferences.enabledLanguages.get() .filterNot { it in listOf("all", "other") } val hasOneActiveLanguages = enabledLanguages.size == 1 diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/activity/BaseActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/activity/BaseActivity.kt index a99fdab4c..c97fd7bbb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/activity/BaseActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/activity/BaseActivity.kt @@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegateImpl import eu.kanade.tachiyomi.ui.base.delegate.ThemingDelegate import eu.kanade.tachiyomi.ui.base.delegate.ThemingDelegateImpl import eu.kanade.tachiyomi.util.system.prepareTabletUiContext +import mihon.app.di.appGraph open class BaseActivity : AppCompatActivity(), @@ -15,7 +16,8 @@ open class BaseActivity : ThemingDelegate by ThemingDelegateImpl() { override fun attachBaseContext(newBase: Context) { - super.attachBaseContext(newBase.prepareTabletUiContext()) + val uiPreferences = newBase.appGraph.uiPreferences + super.attachBaseContext(newBase.prepareTabletUiContext(uiPreferences)) } override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/SecureActivityDelegate.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/SecureActivityDelegate.kt index c7c8ffe0c..bf77417e5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/SecureActivityDelegate.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/SecureActivityDelegate.kt @@ -1,12 +1,14 @@ package eu.kanade.tachiyomi.ui.base.delegate import android.app.Activity +import android.content.Context import android.content.Intent import android.os.Build import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope +import dev.zacsweers.metro.Inject import eu.kanade.domain.base.BasePreferences import eu.kanade.tachiyomi.core.security.SecurityPreferences import eu.kanade.tachiyomi.ui.security.UnlockActivity @@ -16,9 +18,9 @@ import eu.kanade.tachiyomi.util.view.setSecureScreen import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import mihon.app.di.appGraph import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import uy.kohesive.injekt.injectLazy interface SecureActivityDelegate { fun registerSecureActivity(activity: AppCompatActivity) @@ -31,8 +33,8 @@ interface SecureActivityDelegate { */ var requireUnlock = true - fun onApplicationStopped() { - val preferences = Injekt.get() + fun onApplicationStopped(context: Context) { + val preferences = context.appGraph.securityPreferences if (!preferences.useAuthenticator.get()) return if (!AuthenticatorUtil.isAuthenticating) { @@ -48,8 +50,8 @@ interface SecureActivityDelegate { /** * Checks if unlock is needed when app comes foreground. */ - fun onApplicationStart() { - val preferences = Injekt.get() + fun onApplicationStart(context: Context) { + val preferences = context.appGraph.securityPreferences if (!preferences.useAuthenticator.get()) return val lastClosedPref = preferences.lastAppClosed @@ -76,11 +78,13 @@ class SecureActivityDelegateImpl : SecureActivityDelegate, DefaultLifecycleObser private lateinit var activity: AppCompatActivity - private val preferences: BasePreferences by injectLazy() - private val securityPreferences: SecurityPreferences by injectLazy() + @Inject private lateinit var preferences: BasePreferences + + @Inject private lateinit var securityPreferences: SecurityPreferences override fun registerSecureActivity(activity: AppCompatActivity) { this.activity = activity + Injekt.get().appGraph.inject(this) activity.lifecycle.addObserver(this) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/ThemingDelegate.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/ThemingDelegate.kt index bd412ca7f..6ee6056cf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/ThemingDelegate.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/ThemingDelegate.kt @@ -1,11 +1,9 @@ package eu.kanade.tachiyomi.ui.base.delegate import android.app.Activity -import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.model.AppTheme import eu.kanade.tachiyomi.R -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +import mihon.app.di.appGraph interface ThemingDelegate { fun applyAppTheme(activity: Activity) @@ -22,7 +20,7 @@ interface ThemingDelegate { class ThemingDelegateImpl : ThemingDelegate { override fun applyAppTheme(activity: Activity) { - val uiPreferences = Injekt.get() + val uiPreferences = activity.appGraph.uiPreferences ThemingDelegate.getThemeResIds(uiPreferences.appTheme.get(), uiPreferences.themeDarkAmoled.get()) .forEach(activity::setTheme) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt index 3c7c0c903..22f04f9ea 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt @@ -9,10 +9,10 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.tab.LocalTabNavigator import cafe.adriel.voyager.navigator.tab.TabOptions +import dev.zacsweers.metrox.viewmodel.metroViewModel import eu.kanade.presentation.components.TabbedScreen import eu.kanade.presentation.util.Tab import eu.kanade.tachiyomi.R @@ -58,7 +58,7 @@ data object BrowseTab : Tab { val context = LocalContext.current // Hoisted for extensions tab's search bar - val extensionsViewModel = viewModel() + val extensionsViewModel = metroViewModel() val extensionsSearchQuery by extensionsViewModel.searchQuery.collectAsStateWithLifecycle() val tabs = listOf( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterScreen.kt index 3a9105965..a1c83a9fd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterScreen.kt @@ -8,6 +8,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metrox.viewmodel.metroViewModel import eu.kanade.presentation.browse.ExtensionFilterScreen import eu.kanade.presentation.util.Screen import kotlinx.coroutines.flow.collectLatest @@ -21,7 +22,7 @@ class ExtensionFilterScreen : Screen() { override fun Content() { val context = LocalContext.current val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel() + val viewModel = metroViewModel() val state by viewModel.state.collectAsStateWithLifecycle() if (state is ExtensionFilterState.Loading) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterViewModel.kt index 22b0e7599..8e069035f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterViewModel.kt @@ -3,6 +3,11 @@ package eu.kanade.tachiyomi.ui.browse.extension import androidx.compose.runtime.Immutable import androidx.lifecycle.ViewModel 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.domain.extension.interactor.GetExtensionLanguages import eu.kanade.domain.source.interactor.ToggleLanguage import eu.kanade.domain.source.service.SourcePreferences @@ -17,14 +22,15 @@ import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.stateIn import logcat.LogPriority import tachiyomi.core.common.util.system.logcat -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.time.Duration.Companion.seconds +@Inject +@ViewModelKey +@ContributesIntoMap(AppScope::class, binding = binding()) class ExtensionFilterViewModel( - private val preferences: SourcePreferences = Injekt.get(), - private val getExtensionLanguages: GetExtensionLanguages = Injekt.get(), - private val toggleLanguage: ToggleLanguage = Injekt.get(), + private val preferences: SourcePreferences, + private val getExtensionLanguages: GetExtensionLanguages, + private val toggleLanguage: ToggleLanguage, ) : ViewModel() { private val _events: Channel = Channel() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsViewModel.kt index 3af566ccb..a7eb35c2f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsViewModel.kt @@ -1,10 +1,15 @@ package eu.kanade.tachiyomi.ui.browse.extension -import android.app.Application +import android.content.Context import androidx.compose.runtime.Immutable import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dev.icerock.moko.resources.StringResource +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.domain.base.BasePreferences import eu.kanade.domain.extension.interactor.GetExtensionsByType import eu.kanade.domain.source.service.SourcePreferences @@ -33,21 +38,21 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import tachiyomi.core.common.util.lang.launchIO import tachiyomi.i18n.MR -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.time.Duration.Companion.seconds +@Inject +@ViewModelKey +@ContributesIntoMap(AppScope::class, binding = binding()) class ExtensionsViewModel( - private val preferences: SourcePreferences = Injekt.get(), - basePreferences: BasePreferences = Injekt.get(), - private val extensionManager: ExtensionManager = Injekt.get(), - private val getExtensions: GetExtensionsByType = Injekt.get(), + private val preferences: SourcePreferences, + basePreferences: BasePreferences, + private val extensionManager: ExtensionManager, + private val getExtensions: GetExtensionsByType, + private val context: Context, ) : ViewModel() { private val currentDownloads = MutableStateFlow>(hashMapOf()) - private val context = Injekt.get() - // Public so BrowseTab's search bar can observe it without subscribing to the whole state. val searchQuery: StateFlow field = MutableStateFlow(null) @@ -117,6 +122,7 @@ class ExtensionsViewModel( .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5.seconds), State()) init { + viewModelScope.launchIO { findAvailableExtensions() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreen.kt index d7e5df0de..5bd8af8c2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreen.kt @@ -4,10 +4,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.viewmodel.CreationExtras import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metrox.viewmodel.assistedMetroViewModel import eu.kanade.presentation.browse.ExtensionDetailsScreen import eu.kanade.presentation.util.Screen import tachiyomi.i18n.MR @@ -20,12 +20,10 @@ data class ExtensionDetailsScreen( @Composable override fun Content() { - val viewModel = viewModel( - factory = ExtensionDetailsViewModel.Factory, - extras = CreationExtras { - set(ExtensionDetailsViewModel.PKG_NAME_KEY, pkgName) - }, - ) + val viewModel = + assistedMetroViewModel { + create(pkgName = pkgName) + } val state by viewModel.state.collectAsStateWithLifecycle() val navigator = LocalNavigator.currentOrThrow diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsViewModel.kt index 0a9f7fc35..89e59a857 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsViewModel.kt @@ -1,13 +1,16 @@ package eu.kanade.tachiyomi.ui.browse.extension.details -import android.app.Application import android.content.Context import androidx.compose.runtime.Immutable import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.initializer -import androidx.lifecycle.viewmodel.viewModelFactory +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 eu.kanade.domain.extension.interactor.ExtensionSourceItem import eu.kanade.domain.extension.interactor.GetExtensionSources import eu.kanade.domain.source.interactor.ToggleIncognito @@ -32,32 +35,25 @@ import kotlinx.coroutines.flow.stateIn import logcat.LogPriority import okhttp3.HttpUrl.Companion.toHttpUrl import tachiyomi.core.common.util.system.logcat -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.time.Duration.Companion.seconds +@AssistedInject class ExtensionDetailsViewModel( - pkgName: String, + @Assisted pkgName: String, private val context: Context, - private val network: NetworkHelper = Injekt.get(), - private val extensionManager: ExtensionManager = Injekt.get(), - private val getExtensionSources: GetExtensionSources = Injekt.get(), - private val toggleSource: ToggleSource = Injekt.get(), - private val toggleIncognito: ToggleIncognito = Injekt.get(), - private val preferences: SourcePreferences = Injekt.get(), + private val network: NetworkHelper, + private val extensionManager: ExtensionManager, + private val getExtensionSources: GetExtensionSources, + private val toggleSource: ToggleSource, + private val toggleIncognito: ToggleIncognito, + private val preferences: SourcePreferences, ) : ViewModel() { - companion object { - val PKG_NAME_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - ExtensionDetailsViewModel( - pkgName = get(PKG_NAME_KEY)!!, - context = Injekt.get(), - ) - } - } + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(pkgName: String): ExtensionDetailsViewModel } val state: StateFlow = extensionManager.installedExtensionsFlow diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt index 538e257a5..19b3103df 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt @@ -38,11 +38,9 @@ import eu.kanade.tachiyomi.data.preference.SharedPreferencesDataStore import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.sourcePreferences import eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText.Companion.setIncognito -import tachiyomi.domain.source.service.SourceManager +import mihon.app.di.appGraph import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.screens.LoadingScreen -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get class SourcePreferencesScreen(val sourceId: Long) : Screen() { @@ -59,7 +57,7 @@ class SourcePreferencesScreen(val sourceId: Long) : Screen() { Scaffold( topBar = { AppBar( - title = Injekt.get().getOrStub(sourceId).toString(), + title = context.appGraph.sourceManager.getOrStub(sourceId).toString(), navigateUp = navigator::pop, scrollBehavior = it, ) @@ -132,7 +130,9 @@ class SourcePreferencesFragment : PreferenceFragmentCompat() { private fun populateScreen(): PreferenceScreen { val sourceId = requireArguments().getLong(SOURCE_ID) - val source = Injekt.get().getOrStub(sourceId) + val appGraph = requireContext().appGraph + val source = appGraph.sourceManager.getOrStub(sourceId) + val basePreferences = appGraph.basePreferences val sourceScreen = preferenceManager.createPreferenceScreen(requireContext()) if (source is ConfigurableSource) { @@ -152,7 +152,7 @@ class SourcePreferencesFragment : PreferenceFragmentCompat() { val setListener = pref.getOnBindEditTextListener() pref.setOnBindEditTextListener { setListener?.onBindEditText(it) - it.setIncognito(lifecycleScope) + it.setIncognito(basePreferences, lifecycleScope) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreen.kt index f4879f7c6..83c5beb4c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreen.kt @@ -19,10 +19,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.viewmodel.CreationExtras import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metrox.viewmodel.assistedMetroViewModel import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.manga.components.BaseMangaListItem import eu.kanade.presentation.util.Screen @@ -48,12 +48,8 @@ data class MigrateMangaScreen( override fun Content() { val context = LocalContext.current val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel( - factory = MigrateMangaViewModel.Factory, - extras = CreationExtras { - set(MigrateMangaViewModel.SOURCE_ID_KEY, sourceId) - }, - ) + val viewModel = + assistedMetroViewModel { create(sourceId = sourceId) } val state by viewModel.state.collectAsStateWithLifecycle() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaViewModel.kt index 104440a7c..84f8c0674 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaViewModel.kt @@ -3,9 +3,13 @@ package eu.kanade.tachiyomi.ui.browse.migration.manga import androidx.compose.runtime.Immutable import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.initializer -import androidx.lifecycle.viewmodel.viewModelFactory +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 eu.kanade.tachiyomi.source.Source import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.Channel @@ -27,26 +31,20 @@ import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.manga.interactor.GetFavorites 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.seconds +@AssistedInject class MigrateMangaViewModel( - private val sourceId: Long, - private val sourceManager: SourceManager = Injekt.get(), - private val getFavorites: GetFavorites = Injekt.get(), + @Assisted private val sourceId: Long, + private val sourceManager: SourceManager, + private val getFavorites: GetFavorites, ) : ViewModel() { - companion object { - val SOURCE_ID_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - MigrateMangaViewModel( - sourceId = get(SOURCE_ID_KEY)!!, - ) - } - } + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(sourceId: Long): MigrateMangaViewModel } private val _events: Channel = Channel() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreen.kt index 0fc094bfa..3cb3236b2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreen.kt @@ -3,10 +3,9 @@ package eu.kanade.tachiyomi.ui.browse.migration.search import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metrox.viewmodel.assistedMetroViewModel import eu.kanade.presentation.browse.MigrateSearchScreen import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchViewModel @@ -20,12 +19,8 @@ class MigrateSearchScreen(private val mangaId: Long) : Screen() { override fun Content() { val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel( - factory = MigrateSearchViewModel.Factory, - extras = CreationExtras { - set(MigrateSearchViewModel.MANGA_ID_KEY, mangaId) - }, - ) + val viewModel = + assistedMetroViewModel { create(mangaId = mangaId) } val state by viewModel.state.collectAsState() MigrateSearchScreen( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchViewModel.kt index 33c3181d0..749ec2d9c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchViewModel.kt @@ -1,36 +1,45 @@ package eu.kanade.tachiyomi.ui.browse.migration.search import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.initializer -import androidx.lifecycle.viewmodel.viewModelFactory +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 eu.kanade.domain.source.service.SourcePreferences +import eu.kanade.tachiyomi.extension.ExtensionManager import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchViewModel import kotlinx.coroutines.launch import tachiyomi.domain.manga.interactor.GetManga +import tachiyomi.domain.manga.interactor.NetworkToLocalManga import tachiyomi.domain.source.service.SourceManager -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +@AssistedInject class MigrateSearchViewModel( - val mangaId: Long, - getManga: GetManga = Injekt.get(), - private val sourceManager: SourceManager = Injekt.get(), - private val sourcePreferences: SourcePreferences = Injekt.get(), -) : SearchViewModel() { - - companion object { - val MANGA_ID_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - MigrateSearchViewModel( - mangaId = get(MANGA_ID_KEY)!!, - ) - } - } + @Assisted val mangaId: Long, + sourcePreferences: SourcePreferences, + extensionManager: ExtensionManager, + networkToLocalManga: NetworkToLocalManga, + getManga: GetManga, + preferences: SourcePreferences, + private val sourceManager: SourceManager, +) : SearchViewModel( + sourcePreferences = sourcePreferences, + sourceManager = sourceManager, + extensionManager = extensionManager, + networkToLocalManga = networkToLocalManga, + getManga = getManga, + preferences = preferences, +) { + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(mangaId: Long): MigrateSearchViewModel } private val migrationSources by lazy { sourcePreferences.migrationSources.get() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSourceSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSourceSearchScreen.kt index 6dbc41d52..a451b8f8c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSourceSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSourceSearchScreen.kt @@ -17,10 +17,9 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalUriHandler -import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metrox.viewmodel.assistedMetroViewModel import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.presentation.browse.BrowseSourceContent import eu.kanade.presentation.components.SearchToolbar @@ -60,13 +59,10 @@ data class MigrateSourceSearchScreen( val navigator = LocalNavigator.currentOrThrow val scope = rememberCoroutineScope() - val viewModel = viewModel( - factory = BrowseSourceViewModel.Factory, - extras = CreationExtras { - set(BrowseSourceViewModel.SOURCE_ID_KEY, sourceId) - set(BrowseSourceViewModel.LISTING_QUERY_KEY, query) - }, - ) + val viewModel = + assistedMetroViewModel { + create(sourceId = sourceId, listingQuery = query) + } val state by viewModel.state.collectAsState() val snackbarHostState = remember { SnackbarHostState() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceTab.kt index cda1422ad..4081c9b26 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceTab.kt @@ -10,6 +10,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metrox.viewmodel.metroViewModel import eu.kanade.presentation.browse.MigrateSourceScreen import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.TabContent @@ -21,7 +22,7 @@ import tachiyomi.presentation.core.i18n.stringResource fun Screen.migrateSourceTab(): TabContent { val uriHandler = LocalUriHandler.current val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel() + val viewModel = metroViewModel() val state by viewModel.state.collectAsStateWithLifecycle() return TabContent( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceViewModel.kt index 4f99f5b69..326c1eabd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceViewModel.kt @@ -3,6 +3,11 @@ package eu.kanade.tachiyomi.ui.browse.migration.sources import androidx.compose.runtime.Immutable import androidx.lifecycle.ViewModel 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.domain.source.interactor.GetSourcesWithFavoriteCount import eu.kanade.domain.source.interactor.SetMigrateSorting import eu.kanade.domain.source.service.SourcePreferences @@ -20,13 +25,14 @@ import logcat.LogPriority import tachiyomi.core.common.preference.getAndSet import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.source.model.Source -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.time.Duration.Companion.seconds +@Inject +@ViewModelKey +@ContributesIntoMap(AppScope::class, binding = binding()) class MigrateSourceViewModel( - private val preferences: SourcePreferences = Injekt.get(), - private val getSourcesWithFavoriteCount: GetSourcesWithFavoriteCount = Injekt.get(), + private val preferences: SourcePreferences, + private val getSourcesWithFavoriteCount: GetSourcesWithFavoriteCount, ) : ViewModel() { private val _channel = Channel(Int.MAX_VALUE) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesFilterScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesFilterScreen.kt index f0ff2f703..e2cefb904 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesFilterScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesFilterScreen.kt @@ -8,6 +8,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metrox.viewmodel.metroViewModel import eu.kanade.presentation.browse.SourcesFilterScreen import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.util.system.toast @@ -19,7 +20,7 @@ class SourcesFilterScreen : Screen() { @Composable override fun Content() { val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel() + val viewModel = metroViewModel() val state by viewModel.state.collectAsStateWithLifecycle() if (state is SourcesFilterViewModel.State.Loading) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesFilterViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesFilterViewModel.kt index 46aa7ecb3..ac2de235b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesFilterViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesFilterViewModel.kt @@ -3,6 +3,11 @@ package eu.kanade.tachiyomi.ui.browse.source import androidx.compose.runtime.Immutable import androidx.lifecycle.ViewModel 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.domain.source.interactor.GetLanguagesWithSources import eu.kanade.domain.source.interactor.ToggleLanguage import eu.kanade.domain.source.interactor.ToggleSource @@ -14,16 +19,17 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn import tachiyomi.domain.source.model.Source -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.util.SortedMap import kotlin.time.Duration.Companion.seconds +@Inject +@ViewModelKey +@ContributesIntoMap(AppScope::class, binding = binding()) class SourcesFilterViewModel( - private val preferences: SourcePreferences = Injekt.get(), - private val getLanguagesWithSources: GetLanguagesWithSources = Injekt.get(), - private val toggleSource: ToggleSource = Injekt.get(), - private val toggleLanguage: ToggleLanguage = Injekt.get(), + private val preferences: SourcePreferences, + private val getLanguagesWithSources: GetLanguagesWithSources, + private val toggleSource: ToggleSource, + private val toggleLanguage: ToggleLanguage, ) : ViewModel() { val state: StateFlow = combine( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesTab.kt index 4326d3fbc..5d1469d54 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesTab.kt @@ -11,6 +11,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metrox.viewmodel.metroViewModel import eu.kanade.presentation.browse.SourceOptionsDialog import eu.kanade.presentation.browse.SourcesScreen import eu.kanade.presentation.components.AppBar @@ -25,7 +26,7 @@ import tachiyomi.presentation.core.i18n.stringResource @Composable fun Screen.sourcesTab(): TabContent { val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel() + val viewModel = metroViewModel() val state by viewModel.state.collectAsStateWithLifecycle() return TabContent( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesViewModel.kt index d13923904..39c9ba64f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesViewModel.kt @@ -3,6 +3,11 @@ package eu.kanade.tachiyomi.ui.browse.source import androidx.compose.runtime.Immutable import androidx.lifecycle.ViewModel 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.domain.source.interactor.GetEnabledSources import eu.kanade.domain.source.interactor.ToggleSource import eu.kanade.domain.source.interactor.ToggleSourcePin @@ -24,15 +29,16 @@ import logcat.LogPriority import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.source.model.Pin import tachiyomi.domain.source.model.Source -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.util.TreeMap import kotlin.time.Duration.Companion.seconds +@Inject +@ViewModelKey +@ContributesIntoMap(AppScope::class, binding = binding()) class SourcesViewModel( - private val getEnabledSources: GetEnabledSources = Injekt.get(), - private val toggleSource: ToggleSource = Injekt.get(), - private val toggleSourcePin: ToggleSourcePin = Injekt.get(), + private val getEnabledSources: GetEnabledSources, + private val toggleSource: ToggleSource, + private val toggleSourcePin: ToggleSourcePin, ) : ViewModel() { private val _events = Channel(Int.MAX_VALUE) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt index f8fd1c219..0770e60a3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt @@ -32,10 +32,9 @@ import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalUriHandler -import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metrox.viewmodel.assistedMetroViewModel import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.presentation.browse.BrowseSourceContent import eu.kanade.presentation.browse.MissingSourceScreen @@ -82,13 +81,10 @@ data class BrowseSourceScreen( return } - val viewModel = viewModel( - factory = BrowseSourceViewModel.Factory, - extras = CreationExtras { - set(BrowseSourceViewModel.SOURCE_ID_KEY, sourceId) - set(BrowseSourceViewModel.LISTING_QUERY_KEY, listingQuery) - }, - ) + val viewModel = + assistedMetroViewModel { + create(sourceId = sourceId, listingQuery = listingQuery) + } val state by viewModel.state.collectAsState() val navigator = LocalNavigator.currentOrThrow diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceViewModel.kt index bb0616bbd..7b46f1a94 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceViewModel.kt @@ -8,14 +8,18 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.unit.dp import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.initializer -import androidx.lifecycle.viewmodel.viewModelFactory import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn import androidx.paging.filter import androidx.paging.map +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 eu.kanade.core.preference.asState import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.source.interactor.GetIncognitoState @@ -49,44 +53,36 @@ import tachiyomi.domain.manga.model.MangaWithChapterCount import tachiyomi.domain.manga.model.toMangaUpdate import tachiyomi.domain.source.interactor.GetRemoteManga import tachiyomi.domain.source.service.SourceManager -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.time.Clock import eu.kanade.tachiyomi.source.model.Filter as SourceModelFilter +@AssistedInject class BrowseSourceViewModel( - private val sourceId: Long, - listingQuery: String?, - sourceManager: SourceManager = Injekt.get(), - sourcePreferences: SourcePreferences = Injekt.get(), - private val libraryPreferences: LibraryPreferences = Injekt.get(), - private val coverCache: CoverCache = Injekt.get(), - private val getRemoteManga: GetRemoteManga = Injekt.get(), - private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(), - private val getCategories: GetCategories = Injekt.get(), - private val setMangaCategories: SetMangaCategories = Injekt.get(), - private val setMangaDefaultChapterFlags: SetMangaDefaultChapterFlags = Injekt.get(), - private val getManga: GetManga = Injekt.get(), - private val updateManga: UpdateManga = Injekt.get(), - private val addTracks: AddTracks = Injekt.get(), - getIncognitoState: GetIncognitoState = Injekt.get(), + @Assisted private val sourceId: Long, + @Assisted listingQuery: String?, + sourceManager: SourceManager, + sourcePreferences: SourcePreferences, + private val libraryPreferences: LibraryPreferences, + private val coverCache: CoverCache, + private val getRemoteManga: GetRemoteManga, + private val getDuplicateLibraryManga: GetDuplicateLibraryManga, + private val getCategories: GetCategories, + private val setMangaCategories: SetMangaCategories, + private val setMangaDefaultChapterFlags: SetMangaDefaultChapterFlags, + private val getManga: GetManga, + private val updateManga: UpdateManga, + private val addTracks: AddTracks, + getIncognitoState: GetIncognitoState, ) : ViewModel() { val state: StateFlow field = MutableStateFlow(State(Listing.valueOf(listingQuery))) - companion object { - val SOURCE_ID_KEY = CreationExtras.Key() - val LISTING_QUERY_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - BrowseSourceViewModel( - sourceId = get(SOURCE_ID_KEY)!!, - listingQuery = get(LISTING_QUERY_KEY), - ) - } - } + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(sourceId: Long, listingQuery: String?): BrowseSourceViewModel } var displayMode by sourcePreferences.sourceDisplayMode.asState(viewModelScope) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt index a7c138d6c..250a985c1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt @@ -7,10 +7,9 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metrox.viewmodel.assistedMetroViewModel import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.presentation.browse.GlobalSearchScreen import eu.kanade.presentation.util.Screen @@ -32,13 +31,10 @@ class GlobalSearchScreen( val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel( - factory = GlobalSearchViewModel.Factory, - extras = CreationExtras { - set(GlobalSearchViewModel.INITIAL_QUERY_KEY, searchQuery) - set(GlobalSearchViewModel.INITIAL_EXTENSION_FILTER_KEY, extensionFilter) - }, - ) + val viewModel = + assistedMetroViewModel { + create(initialQuery = searchQuery, initialExtensionFilter = extensionFilter) + } val state by viewModel.state.collectAsState() var showSingleLoadingScreen by remember { mutableStateOf(searchQuery.isNotEmpty() && !extensionFilter.isNullOrEmpty() && state.total == 1) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchViewModel.kt index cfb48e51a..3b3480291 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchViewModel.kt @@ -1,27 +1,44 @@ package eu.kanade.tachiyomi.ui.browse.source.globalsearch -import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.initializer -import androidx.lifecycle.viewmodel.viewModelFactory +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 eu.kanade.domain.source.service.SourcePreferences +import eu.kanade.tachiyomi.extension.ExtensionManager import eu.kanade.tachiyomi.source.Source +import tachiyomi.domain.manga.interactor.GetManga +import tachiyomi.domain.manga.interactor.NetworkToLocalManga +import tachiyomi.domain.source.service.SourceManager +@AssistedInject class GlobalSearchViewModel( - initialQuery: String, - initialExtensionFilter: String?, -) : SearchViewModel(State(searchQuery = initialQuery)) { + @Assisted initialQuery: String, + @Assisted initialExtensionFilter: String?, + sourcePreferences: SourcePreferences, + sourceManager: SourceManager, + extensionManager: ExtensionManager, + networkToLocalManga: NetworkToLocalManga, + getManga: GetManga, + preferences: SourcePreferences, +) : SearchViewModel( + initialState = State(searchQuery = initialQuery), + sourcePreferences = sourcePreferences, + sourceManager = sourceManager, + extensionManager = extensionManager, + networkToLocalManga = networkToLocalManga, + getManga = getManga, + preferences = preferences, +) { - companion object { - val INITIAL_QUERY_KEY = CreationExtras.Key() - val INITIAL_EXTENSION_FILTER_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - GlobalSearchViewModel( - initialQuery = get(INITIAL_QUERY_KEY)!!, - initialExtensionFilter = get(INITIAL_EXTENSION_FILTER_KEY), - ) - } - } + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(initialQuery: String, initialExtensionFilter: String?): GlobalSearchViewModel } init { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchViewModel.kt index 06783ab5b..5d9049d14 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchViewModel.kt @@ -27,18 +27,16 @@ import tachiyomi.domain.manga.interactor.GetManga import tachiyomi.domain.manga.interactor.NetworkToLocalManga import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.service.SourceManager -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.util.concurrent.Executors abstract class SearchViewModel( initialState: State = State(), - sourcePreferences: SourcePreferences = Injekt.get(), - private val sourceManager: SourceManager = Injekt.get(), - private val extensionManager: ExtensionManager = Injekt.get(), - private val networkToLocalManga: NetworkToLocalManga = Injekt.get(), - private val getManga: GetManga = Injekt.get(), - private val preferences: SourcePreferences = Injekt.get(), + sourcePreferences: SourcePreferences, + private val sourceManager: SourceManager, + private val extensionManager: ExtensionManager, + private val networkToLocalManga: NetworkToLocalManga, + private val getManga: GetManga, + private val preferences: SourcePreferences, ) : ViewModel() { val state: StateFlow diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryScreen.kt index 3102e8d8c..069dd0d27 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryScreen.kt @@ -9,6 +9,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metrox.viewmodel.metroViewModel import eu.kanade.presentation.category.CategoryScreen import eu.kanade.presentation.category.components.CategoryCreateDialog import eu.kanade.presentation.category.components.CategoryDeleteDialog @@ -24,7 +25,7 @@ class CategoryScreen : Screen() { override fun Content() { val context = LocalContext.current val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel() + val viewModel = metroViewModel() val state by viewModel.state.collectAsStateWithLifecycle() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryViewModel.kt index 3d384c826..49e29f131 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryViewModel.kt @@ -4,6 +4,11 @@ import androidx.compose.runtime.Immutable import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dev.icerock.moko.resources.StringResource +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 kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -21,16 +26,17 @@ import tachiyomi.domain.category.interactor.RenameCategory import tachiyomi.domain.category.interactor.ReorderCategory import tachiyomi.domain.category.model.Category import tachiyomi.i18n.MR -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.time.Duration.Companion.seconds +@Inject +@ViewModelKey +@ContributesIntoMap(AppScope::class, binding = binding()) class CategoryViewModel( - private val getCategories: GetCategories = Injekt.get(), - private val createCategoryWithName: CreateCategoryWithName = Injekt.get(), - private val deleteCategory: DeleteCategory = Injekt.get(), - private val reorderCategory: ReorderCategory = Injekt.get(), - private val renameCategory: RenameCategory = Injekt.get(), + private val getCategories: GetCategories, + private val createCategoryWithName: CreateCategoryWithName, + private val deleteCategory: DeleteCategory, + private val reorderCategory: ReorderCategory, + private val renameCategory: RenameCategory, ) : ViewModel() { private val _events: Channel = Channel() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/deeplink/DeepLinkScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/deeplink/DeepLinkScreen.kt index 46a1b7539..5f94b4afc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/deeplink/DeepLinkScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/deeplink/DeepLinkScreen.kt @@ -6,10 +6,9 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metrox.viewmodel.assistedMetroViewModel import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchScreen @@ -29,12 +28,7 @@ class DeepLinkScreen( val context = LocalContext.current val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel( - factory = DeepLinkViewModel.Factory, - extras = CreationExtras { - set(DeepLinkViewModel.QUERY_KEY, query) - }, - ) + val viewModel = assistedMetroViewModel { create(query = query) } val state by viewModel.state.collectAsState() Scaffold( topBar = { scrollBehavior -> diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/deeplink/DeepLinkViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/deeplink/DeepLinkViewModel.kt index bb243ac47..fd597ca52 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/deeplink/DeepLinkViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/deeplink/DeepLinkViewModel.kt @@ -3,9 +3,13 @@ package eu.kanade.tachiyomi.ui.deeplink import androidx.compose.runtime.Immutable import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.initializer -import androidx.lifecycle.viewmodel.viewModelFactory +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 eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.online.ResolvableSource @@ -21,30 +25,24 @@ import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.manga.interactor.NetworkToLocalManga import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.service.SourceManager -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +@AssistedInject class DeepLinkViewModel( - query: String, - private val sourceManager: SourceManager = Injekt.get(), - private val networkToLocalManga: NetworkToLocalManga = Injekt.get(), - private val getChapterByUrlAndMangaId: GetChapterByUrlAndMangaId = Injekt.get(), - private val updateMangaFromRemote: UpdateMangaFromRemote = Injekt.get(), + @Assisted query: String, + private val sourceManager: SourceManager, + private val networkToLocalManga: NetworkToLocalManga, + private val getChapterByUrlAndMangaId: GetChapterByUrlAndMangaId, + private val updateMangaFromRemote: UpdateMangaFromRemote, ) : ViewModel() { val state: StateFlow field = MutableStateFlow(State.Loading) - companion object { - val QUERY_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - DeepLinkViewModel( - query = get(QUERY_KEY)!!, - ) - } - } + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(query: String): DeepLinkViewModel } init { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt index c78800677..9876d69b4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt @@ -45,6 +45,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel import androidx.recyclerview.widget.LinearLayoutManager import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metrox.viewmodel.metroViewModel import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.components.DropdownMenu @@ -65,7 +66,7 @@ object DownloadQueueScreen : Screen() { override fun Content() { val navigator = LocalNavigator.currentOrThrow val scope = rememberCoroutineScope() - val viewModel = viewModel() + val viewModel = metroViewModel() val downloadList by viewModel.state.collectAsStateWithLifecycle() val downloadCount by remember { derivedStateOf { downloadList.sumOf { it.subItems.size } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueViewModel.kt index 9b9cd3ef4..1143bbf66 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueViewModel.kt @@ -3,6 +3,10 @@ package eu.kanade.tachiyomi.ui.download import android.view.MenuItem import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesIntoMap +import dev.zacsweers.metro.Inject +import dev.zacsweers.metrox.viewmodel.ViewModelKey import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.model.Download @@ -20,13 +24,14 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds +@Inject +@ViewModelKey +@ContributesIntoMap(AppScope::class) class DownloadQueueViewModel( - private val downloadManager: DownloadManager = Injekt.get(), + private val downloadManager: DownloadManager, ) : ViewModel() { val state: StateFlow> = downloadManager.queueState diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryTab.kt index fd0b0d3ea..840b175b1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryTab.kt @@ -16,6 +16,7 @@ import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.tab.LocalTabNavigator import cafe.adriel.voyager.navigator.tab.TabOptions +import dev.zacsweers.metrox.viewmodel.metroViewModel import eu.kanade.presentation.category.components.ChangeCategoryDialog import eu.kanade.presentation.history.HistoryScreen import eu.kanade.presentation.history.components.HistoryDeleteAllDialog @@ -62,7 +63,7 @@ data object HistoryTab : Tab { override fun Content() { val navigator = LocalNavigator.currentOrThrow val context = LocalContext.current - val viewModel = viewModel() + val viewModel = metroViewModel() val state by viewModel.state.collectAsStateWithLifecycle() HistoryScreen( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryViewModel.kt index 761210730..8291df989 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryViewModel.kt @@ -4,6 +4,11 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Immutable import androidx.lifecycle.ViewModel 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.core.util.insertSeparators import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.track.interactor.AddTracks @@ -46,25 +51,27 @@ import tachiyomi.domain.manga.interactor.GetManga import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.MangaWithChapterCount import tachiyomi.domain.source.service.SourceManager -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.time.Duration.Companion.seconds +@Inject +@ViewModelKey +@ContributesIntoMap(AppScope::class, binding = binding()) class HistoryViewModel( - private val addTracks: AddTracks = Injekt.get(), - private val getCategories: GetCategories = Injekt.get(), - private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(), - private val getHistory: GetHistory = Injekt.get(), - private val getManga: GetManga = Injekt.get(), - private val getNextChapters: GetNextChapters = Injekt.get(), - private val libraryPreferences: LibraryPreferences = Injekt.get(), - private val removeHistory: RemoveHistory = Injekt.get(), - private val setMangaCategories: SetMangaCategories = Injekt.get(), - private val updateManga: UpdateManga = Injekt.get(), - val snackbarHostState: SnackbarHostState = SnackbarHostState(), - private val sourceManager: SourceManager = Injekt.get(), + private val addTracks: AddTracks, + private val getCategories: GetCategories, + private val getDuplicateLibraryManga: GetDuplicateLibraryManga, + private val getHistory: GetHistory, + private val getManga: GetManga, + private val getNextChapters: GetNextChapters, + private val libraryPreferences: LibraryPreferences, + private val removeHistory: RemoveHistory, + private val setMangaCategories: SetMangaCategories, + private val updateManga: UpdateManga, + private val sourceManager: SourceManager, ) : ViewModel() { + val snackbarHostState: SnackbarHostState = SnackbarHostState() + private val _events: Channel = Channel(Channel.UNLIMITED) val events: Flow = _events.receiveAsFlow() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt index 93df94f77..a915d2d03 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.TextOverflow @@ -33,7 +34,6 @@ import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.tab.LocalTabNavigator import cafe.adriel.voyager.navigator.tab.TabNavigator -import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.isTabletUi import eu.kanade.tachiyomi.ui.browse.BrowseTab @@ -48,16 +48,14 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch +import mihon.app.di.appGraph import soup.compose.material.motion.animation.materialFadeThroughIn import soup.compose.material.motion.animation.materialFadeThroughOut -import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.NavigationBar import tachiyomi.presentation.core.components.material.NavigationRail import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.i18n.pluralStringResource -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get object HomeScreen : Screen() { @@ -235,12 +233,13 @@ object HomeScreen : Screen() { @Composable private fun NavigationIconItem(tab: eu.kanade.presentation.util.Tab) { + val context = LocalContext.current BadgedBox( badge = { when { tab is UpdatesTab -> { val count by produceState(initialValue = 0) { - val pref = Injekt.get() + val pref = context.appGraph.libraryPreferences combine( pref.newShowUpdatesCount.changes(), pref.newUpdatesCount.changes(), @@ -263,7 +262,7 @@ object HomeScreen : Screen() { } BrowseTab::class.isInstance(tab) -> { val count by produceState(initialValue = 0) { - Injekt.get().extensionUpdatesCount.changes() + context.appGraph.sourcePreferences.extensionUpdatesCount.changes() .collectLatest { value = it } } if (count > 0) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsViewModel.kt index 647ef0b2a..dbea4718b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsViewModel.kt @@ -2,6 +2,10 @@ package eu.kanade.tachiyomi.ui.library import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesIntoMap +import dev.zacsweers.metro.Inject +import dev.zacsweers.metrox.viewmodel.ViewModelKey import eu.kanade.domain.base.BasePreferences import eu.kanade.tachiyomi.data.track.TrackerManager import kotlinx.coroutines.flow.SharingStarted @@ -16,16 +20,17 @@ import tachiyomi.domain.category.model.Category import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibrarySort import tachiyomi.domain.library.service.LibraryPreferences -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.time.Duration.Companion.seconds +@Inject +@ViewModelKey +@ContributesIntoMap(AppScope::class) class LibrarySettingsViewModel( - val preferences: BasePreferences = Injekt.get(), - val libraryPreferences: LibraryPreferences = Injekt.get(), - private val setDisplayMode: SetDisplayMode = Injekt.get(), - private val setSortModeForCategory: SetSortModeForCategory = Injekt.get(), - trackerManager: TrackerManager = Injekt.get(), + val preferences: BasePreferences, + val libraryPreferences: LibraryPreferences, + private val setDisplayMode: SetDisplayMode, + private val setSortModeForCategory: SetSortModeForCategory, + trackerManager: TrackerManager, ) : ViewModel() { val trackersFlow = trackerManager.loggedInTrackersFlow() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt index f01127f8d..09008bca2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt @@ -27,6 +27,7 @@ import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.tab.LocalTabNavigator import cafe.adriel.voyager.navigator.tab.TabOptions +import dev.zacsweers.metrox.viewmodel.metroViewModel import eu.kanade.presentation.category.components.ChangeCategoryDialog import eu.kanade.presentation.library.DeleteLibraryMangaDialog import eu.kanade.presentation.library.LibrarySettingsDialog @@ -43,6 +44,7 @@ import eu.kanade.tachiyomi.ui.home.HomeScreen import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.manga.MangaScreen import eu.kanade.tachiyomi.ui.reader.ReaderActivity +import eu.kanade.tachiyomi.util.system.workManager import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.receiveAsFlow @@ -86,14 +88,14 @@ data object LibraryTab : Tab { val scope = rememberCoroutineScope() val haptic = LocalHapticFeedback.current - val viewModel = viewModel() - val settingsViewModel = viewModel() + val viewModel = metroViewModel() + val settingsViewModel = metroViewModel() val state by viewModel.state.collectAsStateWithLifecycle() val snackbarHostState = remember { SnackbarHostState() } val onClickRefresh: (Category?) -> Boolean = { category -> - val started = LibraryUpdateJob.startNow(context, category) + val started = LibraryUpdateJob.startNow(context.workManager, category) scope.launch { val msgRes = when { !started -> MR.strings.update_already_running diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryViewModel.kt index 4a2957d11..36284623a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryViewModel.kt @@ -6,6 +6,11 @@ import androidx.compose.ui.util.fastFilter import androidx.compose.ui.util.fastMap import androidx.lifecycle.ViewModel 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.core.preference.PreferenceMutableState import eu.kanade.core.preference.asState import eu.kanade.core.util.fastFilterNot @@ -65,28 +70,29 @@ import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.track.interactor.GetTracksPerManga import tachiyomi.domain.track.model.Track import tachiyomi.source.local.isLocal -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.random.Random import kotlin.time.Duration.Companion.seconds +@Inject +@ViewModelKey +@ContributesIntoMap(AppScope::class, binding = binding()) class LibraryViewModel( - private val getLibraryManga: GetLibraryManga = Injekt.get(), - private val getCategories: GetCategories = Injekt.get(), - private val getTracksPerManga: GetTracksPerManga = Injekt.get(), - private val getNextChapters: GetNextChapters = Injekt.get(), - private val getChaptersByMangaId: GetChaptersByMangaId = Injekt.get(), - private val getBookmarkedChaptersByMangaId: GetBookmarkedChaptersByMangaId = Injekt.get(), - private val setReadStatus: SetReadStatus = Injekt.get(), - private val updateManga: UpdateManga = Injekt.get(), - private val setMangaCategories: SetMangaCategories = Injekt.get(), - private val preferences: BasePreferences = Injekt.get(), - private val libraryPreferences: LibraryPreferences = Injekt.get(), - private val coverCache: CoverCache = Injekt.get(), - private val sourceManager: SourceManager = Injekt.get(), - private val downloadManager: DownloadManager = Injekt.get(), - private val downloadCache: DownloadCache = Injekt.get(), - private val trackerManager: TrackerManager = Injekt.get(), + private val getLibraryManga: GetLibraryManga, + private val getCategories: GetCategories, + private val getTracksPerManga: GetTracksPerManga, + private val getNextChapters: GetNextChapters, + private val getChaptersByMangaId: GetChaptersByMangaId, + private val getBookmarkedChaptersByMangaId: GetBookmarkedChaptersByMangaId, + private val setReadStatus: SetReadStatus, + private val updateManga: UpdateManga, + private val setMangaCategories: SetMangaCategories, + private val preferences: BasePreferences, + private val libraryPreferences: LibraryPreferences, + private val coverCache: CoverCache, + private val sourceManager: SourceManager, + private val downloadManager: DownloadManager, + private val downloadCache: DownloadCache, + private val trackerManager: TrackerManager, ) : ViewModel() { private val searchQuery = MutableStateFlow(null) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index c5e320e5e..2a9bea744 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -67,6 +67,7 @@ import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.NavigatorDisposeBehavior import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metro.Inject import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.source.interactor.GetIncognitoState import eu.kanade.presentation.components.AdaptiveSheet @@ -81,7 +82,6 @@ import eu.kanade.presentation.util.DefaultNavigatorScreenTransition import eu.kanade.tachiyomi.data.cache.ChapterCache import eu.kanade.tachiyomi.data.download.DownloadCache import eu.kanade.tachiyomi.data.notification.NotificationReceiver -import eu.kanade.tachiyomi.data.updater.AppUpdateChecker import eu.kanade.tachiyomi.extension.api.ExtensionApi import eu.kanade.tachiyomi.ui.base.activity.BaseActivity import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen @@ -106,6 +106,9 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import logcat.LogPriority +import mihon.app.di.AppGraph +import mihon.app.di.appGraph +import mihon.core.metro.metroGraph import mihon.core.migration.Migrator import mihon.feature.support.SupportUsScreen import tachiyomi.core.common.Constants @@ -118,7 +121,6 @@ import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.collectAsState -import uy.kohesive.injekt.injectLazy import kotlin.time.Clock import kotlin.time.Duration.Companion.days import kotlin.time.Instant @@ -126,13 +128,19 @@ import kotlin.time.times class MainActivity : BaseActivity() { - private val libraryPreferences: LibraryPreferences by injectLazy() - private val preferences: BasePreferences by injectLazy() + private val graph: AppGraph by lazy { metroGraph() } - private val downloadCache: DownloadCache by injectLazy() - private val chapterCache: ChapterCache by injectLazy() + @Inject private lateinit var libraryPreferences: LibraryPreferences - private val getIncognitoState: GetIncognitoState by injectLazy() + @Inject private lateinit var preferences: BasePreferences + + @Inject private lateinit var downloadCache: DownloadCache + + @Inject private lateinit var chapterCache: ChapterCache + + @Inject private lateinit var getIncognitoState: GetIncognitoState + + @Inject private lateinit var extensionApi: ExtensionApi // To be checked by splash screen. If true then splash screen will be removed. var ready = false @@ -144,6 +152,7 @@ class MainActivity : BaseActivity() { } override fun onCreate(savedInstanceState: Bundle?) { + graph.inject(this) val isLaunch = savedInstanceState == null // Prevent splash screen showing up on configuration changes @@ -311,7 +320,7 @@ class MainActivity : BaseActivity() { LaunchedEffect(Unit) { if (updaterEnabled) { try { - val result = AppUpdateChecker().checkForUpdate() + val result = context.appGraph.updateChecker.checkForUpdate() if (result is GetApplicationRelease.Result.NewUpdate) { val updateScreen = NewUpdateScreen( versionName = result.release.version, @@ -330,7 +339,7 @@ class MainActivity : BaseActivity() { // Extensions updates LaunchedEffect(Unit) { try { - ExtensionApi().checkForUpdates(context) + extensionApi.checkForUpdates(context) } catch (e: Exception) { logcat(LogPriority.ERROR, e) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverViewModel.kt index e996e7634..dae1829a6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverViewModel.kt @@ -5,13 +5,17 @@ import android.net.Uri import androidx.compose.material3.SnackbarHostState import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.initializer -import androidx.lifecycle.viewmodel.viewModelFactory import coil3.asDrawable import coil3.imageLoader import coil3.request.ImageRequest import coil3.size.Size +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 eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.saver.Image @@ -36,30 +40,25 @@ import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.manga.interactor.GetManga import tachiyomi.domain.manga.model.Manga import tachiyomi.i18n.MR -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +import tachiyomi.source.local.image.LocalCoverManager import kotlin.time.Duration.Companion.seconds +@AssistedInject class MangaCoverViewModel( - private val mangaId: Long, - private val getManga: GetManga = Injekt.get(), - private val imageSaver: ImageSaver = Injekt.get(), - private val coverCache: CoverCache = Injekt.get(), - private val updateManga: UpdateManga = Injekt.get(), - + @Assisted private val mangaId: Long, + private val getManga: GetManga, + private val imageSaver: ImageSaver, + private val coverCache: CoverCache, + private val updateManga: UpdateManga, + private val coverManager: LocalCoverManager, val snackbarHostState: SnackbarHostState = SnackbarHostState(), ) : ViewModel() { - companion object { - val MANGA_ID_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - MangaCoverViewModel( - mangaId = get(MANGA_ID_KEY)!!, - ) - } - } + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(mangaId: Long): MangaCoverViewModel } val state: StateFlow = getManga.subscribe(mangaId) @@ -140,7 +139,7 @@ class MangaCoverViewModel( viewModelScope.launchIO { context.contentResolver.openInputStream(data)?.use { try { - manga.editCover(Injekt.get(), it, updateManga, coverCache) + manga.editCover(coverManager, it, updateManga, coverCache) notifyCoverUpdated(context) } catch (e: Exception) { notifyFailedCoverUpdate(context, e) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt index d577fd377..d1efaf058 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt @@ -17,11 +17,10 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalHapticFeedback import androidx.core.net.toUri import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metrox.viewmodel.assistedMetroViewModel import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.domain.manga.model.hasCustomCover import eu.kanade.domain.manga.model.toSManga @@ -83,13 +82,10 @@ class MangaScreen( val context = LocalContext.current val haptic = LocalHapticFeedback.current val scope = rememberCoroutineScope() - val viewModel = viewModel( - factory = MangaViewModel.Factory, - extras = CreationExtras { - set(MangaViewModel.MANGA_ID_KEY, mangaId) - set(MangaViewModel.IS_FROM_SOURCE_KEY, fromSource) - }, - ) + val viewModel = + assistedMetroViewModel { + create(mangaId = mangaId, isFromSource = fromSource) + } val state by viewModel.state.collectAsStateWithLifecycle() @@ -243,28 +239,26 @@ class MangaScreen( ) } MangaViewModel.Dialog.FullCover -> { - val sm = viewModel( - factory = MangaCoverViewModel.Factory, - extras = CreationExtras { - set(MangaCoverViewModel.MANGA_ID_KEY, successState.manga.id) - }, - ) - val manga by sm.state.collectAsStateWithLifecycle() + val vm = + assistedMetroViewModel { + create(mangaId = mangaId) + } + val manga by vm.state.collectAsStateWithLifecycle() if (manga != null) { val getContent = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { if (it == null) return@rememberLauncherForActivityResult - sm.editCover(context, it) + vm.editCover(context, it) } MangaCoverDialog( manga = manga!!, - snackbarHostState = sm.snackbarHostState, + snackbarHostState = vm.snackbarHostState, isCustomCover = remember(manga) { manga!!.hasCustomCover() }, - onShareClick = { sm.shareCover(context) }, - onSaveClick = { sm.saveCover(context) }, + onShareClick = { vm.shareCover(context) }, + onSaveClick = { vm.saveCover(context) }, onEditClick = { when (it) { EditCoverAction.EDIT -> getContent.launch("image/*") - EditCoverAction.DELETE -> sm.deleteCustomCover(context) + EditCoverAction.DELETE -> vm.deleteCustomCover(context) } }, onDismissRequest = onDismissRequest, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaViewModel.kt index f61daa2ed..08fa62c12 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaViewModel.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.manga -import android.app.Application import android.content.Context import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHostState @@ -10,9 +9,13 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.util.fastAny import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.initializer -import androidx.lifecycle.viewmodel.viewModelFactory +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 eu.kanade.core.preference.asState import eu.kanade.core.util.addOrRemove import eu.kanade.core.util.insertSeparators @@ -31,6 +34,7 @@ import eu.kanade.domain.track.service.TrackPreferences import eu.kanade.presentation.manga.DownloadAction import eu.kanade.presentation.manga.components.ChapterDownloadAction import eu.kanade.presentation.util.formattedMessage +import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.download.DownloadCache import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.model.Download @@ -85,60 +89,54 @@ import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.i18n.MR import tachiyomi.source.local.isLocal -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.math.floor +@AssistedInject class MangaViewModel( + @Assisted private val mangaId: Long, + @Assisted private val isFromSource: Boolean, private val context: Context, - private val mangaId: Long, - private val isFromSource: Boolean, - private val libraryPreferences: LibraryPreferences = Injekt.get(), - trackPreferences: TrackPreferences = Injekt.get(), - readerPreferences: ReaderPreferences = Injekt.get(), - private val trackerManager: TrackerManager = Injekt.get(), - private val trackChapter: TrackChapter = Injekt.get(), - private val downloadManager: DownloadManager = Injekt.get(), - private val downloadCache: DownloadCache = Injekt.get(), - private val getMangaAndChapters: GetMangaWithChapters = Injekt.get(), - private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(), - private val getAvailableScanlators: GetAvailableScanlators = Injekt.get(), - private val getExcludedScanlators: GetExcludedScanlators = Injekt.get(), - private val setExcludedScanlators: SetExcludedScanlators = Injekt.get(), - private val setMangaChapterFlags: SetMangaChapterFlags = Injekt.get(), - private val setMangaDefaultChapterFlags: SetMangaDefaultChapterFlags = Injekt.get(), - private val setReadStatus: SetReadStatus = Injekt.get(), - private val updateChapter: UpdateChapter = Injekt.get(), - private val updateManga: UpdateManga = Injekt.get(), - private val getCategories: GetCategories = Injekt.get(), - private val getTracks: GetTracks = Injekt.get(), - private val addTracks: AddTracks = Injekt.get(), - private val setMangaCategories: SetMangaCategories = Injekt.get(), - private val mangaRepository: MangaRepository = Injekt.get(), - private val filterChaptersForDownload: FilterChaptersForDownload = Injekt.get(), - private val updateMangaFromRemote: UpdateMangaFromRemote = Injekt.get(), - val snackbarHostState: SnackbarHostState = SnackbarHostState(), + private val libraryPreferences: LibraryPreferences, + trackPreferences: TrackPreferences, + readerPreferences: ReaderPreferences, + private val trackerManager: TrackerManager, + private val trackChapter: TrackChapter, + private val downloadManager: DownloadManager, + private val downloadCache: DownloadCache, + private val getMangaAndChapters: GetMangaWithChapters, + private val getDuplicateLibraryManga: GetDuplicateLibraryManga, + private val getAvailableScanlators: GetAvailableScanlators, + private val getExcludedScanlators: GetExcludedScanlators, + private val setExcludedScanlators: SetExcludedScanlators, + private val setMangaChapterFlags: SetMangaChapterFlags, + private val setMangaDefaultChapterFlags: SetMangaDefaultChapterFlags, + private val setReadStatus: SetReadStatus, + private val updateChapter: UpdateChapter, + private val updateManga: UpdateManga, + private val getCategories: GetCategories, + private val getTracks: GetTracks, + private val addTracks: AddTracks, + private val setMangaCategories: SetMangaCategories, + private val mangaRepository: MangaRepository, + private val filterChaptersForDownload: FilterChaptersForDownload, + private val updateMangaFromRemote: UpdateMangaFromRemote, + private val sourceManager: SourceManager, + private val refreshTracks: RefreshTracks, + private val coverCache: CoverCache, ) : ViewModel() { - val state: StateFlow - field = MutableStateFlow(State.Loading) + val state: StateFlow + field = MutableStateFlow(State.Loading) - companion object { - val MANGA_ID_KEY = CreationExtras.Key() - - val IS_FROM_SOURCE_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - MangaViewModel( - context = Injekt.get(), - mangaId = get(MANGA_ID_KEY)!!, - isFromSource = get(IS_FROM_SOURCE_KEY)!!, - ) - } - } + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(mangaId: Long, isFromSource: Boolean): MangaViewModel } + val snackbarHostState: SnackbarHostState = SnackbarHostState() + private val successState: State.Success? get() = state.value as? State.Success @@ -236,7 +234,7 @@ class MangaViewModel( state.update { State.Success( manga = manga, - source = Injekt.get().getOrStub(manga.source), + source = sourceManager.getOrStub(manga.source), isFromSource = isFromSource, chapters = chapters, availableScanlators = getAvailableScanlators.await(mangaId), @@ -348,7 +346,7 @@ class MangaViewModel( // Remove from library if (updateManga.awaitUpdateFavorite(manga.id, false)) { // Remove covers and update last modified in db - if (manga.removeCovers() != manga) { + if (manga.removeCovers(coverCache) != manga) { updateManga.awaitUpdateCoverLastModified(manga.id) } withUIContext { onRemoved() } @@ -777,9 +775,7 @@ class MangaViewModel( } } - private suspend fun refreshTrackers( - refreshTracks: RefreshTracks = Injekt.get(), - ) { + private suspend fun refreshTrackers() { refreshTracks.await(mangaId) .filter { it.first != null } .forEach { (track, e) -> diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/notes/MangaNotesScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/notes/MangaNotesScreen.kt index b113eeda2..4601002a6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/notes/MangaNotesScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/notes/MangaNotesScreen.kt @@ -6,12 +6,16 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -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.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.manga.MangaNotesScreen import eu.kanade.presentation.util.Screen import kotlinx.coroutines.flow.MutableStateFlow @@ -20,8 +24,6 @@ import kotlinx.coroutines.flow.update import tachiyomi.core.common.util.lang.launchNonCancellable import tachiyomi.domain.manga.interactor.UpdateMangaNotes import tachiyomi.domain.manga.model.Manga -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get class MangaNotesScreen( private val manga: Manga, @@ -30,12 +32,7 @@ class MangaNotesScreen( override fun Content() { val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel( - factory = Model.Factory, - extras = CreationExtras { - set(Model.MANGA_KEY, manga) - }, - ) + val viewModel = assistedMetroViewModel { create(manga = manga) } val state by viewModel.state.collectAsState() MangaNotesScreen( @@ -45,24 +42,20 @@ class MangaNotesScreen( ) } + @AssistedInject class Model( - private val manga: Manga, - private val updateMangaNotes: UpdateMangaNotes = Injekt.get(), + @Assisted private val manga: Manga, + private val updateMangaNotes: UpdateMangaNotes, ) : ViewModel() { val state: StateFlow field = MutableStateFlow(State(manga, manga.notes)) - companion object { - val MANGA_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - Model( - manga = get(MANGA_KEY)!!, - ) - } - } + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(manga: Manga): Model } fun updateNotes(content: String) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt index 9b1ed955f..80dd19103 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.manga.track -import android.app.Application import android.content.Context import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -32,14 +31,18 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextAlign import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -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.Navigator import cafe.adriel.voyager.navigator.currentOrThrow import dev.icerock.moko.resources.StringResource +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.domain.track.interactor.RefreshTracks import eu.kanade.domain.track.model.toDbTrack import eu.kanade.domain.ui.UiPreferences @@ -52,7 +55,6 @@ import eu.kanade.presentation.track.TrackerSearch import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.data.track.DeletableTracker import eu.kanade.tachiyomi.data.track.EnhancedTracker -import eu.kanade.tachiyomi.data.track.Tracker import eu.kanade.tachiyomi.data.track.TrackerManager import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.util.lang.convertEpochMillisZone @@ -70,6 +72,7 @@ import kotlinx.coroutines.launch import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime import logcat.LogPriority +import mihon.app.di.appGraph import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.util.lang.launchNonCancellable import tachiyomi.core.common.util.lang.withIOContext @@ -85,8 +88,6 @@ import tachiyomi.presentation.core.components.LabeledCheckbox import tachiyomi.presentation.core.components.material.AlertDialogContent import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.time.Clock import kotlin.time.Instant @@ -100,15 +101,9 @@ data class TrackInfoDialogHomeScreen( override fun Content() { val navigator = LocalNavigator.currentOrThrow val context = LocalContext.current - val viewModel = viewModel( - factory = Model.Factory, - extras = CreationExtras { - set(Model.MANGA_ID_KEY, mangaId) - set(Model.SOURCE_ID_KEY, sourceId) - }, - ) + val viewModel = assistedMetroViewModel { create(mangaId = mangaId, sourceId = sourceId) } - val dateFormat = remember { UiPreferences.dateFormat(Injekt.get().dateFormat.get()) } + val dateFormat = remember { UiPreferences.dateFormat(context.appGraph.uiPreferences.dateFormat.get()) } val state by viewModel.state.collectAsState() TrackInfoDialogHome( @@ -202,27 +197,26 @@ data class TrackInfoDialogHomeScreen( } } + @AssistedInject class Model( - private val mangaId: Long, - private val sourceId: Long, - private val getTracks: GetTracks = Injekt.get(), + @Assisted private val mangaId: Long, + @Assisted private val sourceId: Long, + private val context: Context, + private val getTracks: GetTracks, + private val getManga: GetManga, + private val trackerManager: TrackerManager, + private val sourceManager: SourceManager, + private val refreshTracks: RefreshTracks, ) : ViewModel() { val state: StateFlow field = MutableStateFlow(State()) - companion object { - val MANGA_ID_KEY = CreationExtras.Key() - val SOURCE_ID_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - Model( - mangaId = get(MANGA_ID_KEY)!!, - sourceId = get(SOURCE_ID_KEY)!!, - ) - } - } + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(mangaId: Long, sourceId: Long): Model } init { @@ -242,20 +236,19 @@ data class TrackInfoDialogHomeScreen( fun registerEnhancedTracking(item: TrackItem) { item.tracker as EnhancedTracker viewModelScope.launchNonCancellable { - val manga = Injekt.get().await(mangaId) ?: return@launchNonCancellable + val manga = getManga.await(mangaId) ?: return@launchNonCancellable try { val matchResult = item.tracker.match(manga) ?: throw Exception() item.tracker.register(matchResult, mangaId) } catch (_: Exception) { - withUIContext { Injekt.get().toast(MR.strings.error_no_match) } + withUIContext { + context.toast(MR.strings.error_no_match) + } } } } private suspend fun refreshTrackers() { - val refreshTracks = Injekt.get() - val context = Injekt.get() - refreshTracks.await(mangaId) .filter { it.first != null } .forEach { (track, e) -> @@ -281,8 +274,8 @@ data class TrackInfoDialogHomeScreen( } private fun List.mapToTrackItem(): List { - val loggedInTrackers = Injekt.get().loggedInTrackers() - val source = Injekt.get().getOrStub(sourceId) + val loggedInTrackers = trackerManager.loggedInTrackers() + val source = sourceManager.getOrStub(sourceId) return loggedInTrackers // Map to TrackItem .map { service -> TrackItem(find { it.trackerId == service.id }, service) } @@ -297,7 +290,7 @@ data class TrackInfoDialogHomeScreen( } } -private data class TrackStatusSelectorScreen( +data class TrackStatusSelectorScreen( private val track: Track, private val serviceId: Long, ) : Screen() { @@ -305,13 +298,7 @@ private data class TrackStatusSelectorScreen( @Composable override fun Content() { val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel( - factory = Model.Factory, - extras = CreationExtras { - set(Model.TRACK_KEY, track) - set(Model.TRACKER_KEY, Injekt.get().get(serviceId)!!) - }, - ) + val viewModel = assistedMetroViewModel { create(track = track, trackerId = serviceId) } val state by viewModel.state.collectAsState() TrackStatusSelector( selection = state.selection, @@ -325,28 +312,25 @@ private data class TrackStatusSelectorScreen( ) } + @AssistedInject class Model( - private val track: Track, - private val tracker: Tracker, + @Assisted private val track: Track, + @Assisted private val trackerId: Long, + trackerManager: TrackerManager, ) : ViewModel() { val state: StateFlow field = MutableStateFlow(State(track.status)) - companion object { - val TRACK_KEY = CreationExtras.Key() - val TRACKER_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - Model( - track = get(TRACK_KEY)!!, - tracker = get(TRACKER_KEY)!!, - ) - } - } + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(track: Track, trackerId: Long): Model } + val tracker = trackerManager.get(trackerId)!! + fun getSelections(): Map { return tracker.getStatusList().associateWith { tracker.getStatus(it) } } @@ -368,7 +352,7 @@ private data class TrackStatusSelectorScreen( } } -private data class TrackChapterSelectorScreen( +data class TrackChapterSelectorScreen( private val track: Track, private val serviceId: Long, ) : Screen() { @@ -376,13 +360,7 @@ private data class TrackChapterSelectorScreen( @Composable override fun Content() { val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel( - factory = Model.Factory, - extras = CreationExtras { - set(Model.TRACK_KEY, track) - set(Model.TRACKER_KEY, Injekt.get().get(serviceId)!!) - }, - ) + val viewModel = assistedMetroViewModel { create(track = track, trackerId = serviceId) } val state by viewModel.state.collectAsState() TrackChapterSelector( @@ -397,28 +375,25 @@ private data class TrackChapterSelectorScreen( ) } + @AssistedInject class Model( - private val track: Track, - private val tracker: Tracker, + @Assisted private val track: Track, + @Assisted private val trackerId: Long, + trackerManager: TrackerManager, ) : ViewModel() { val state: StateFlow field = MutableStateFlow(State(track.lastChapterRead.toInt())) - companion object { - val TRACK_KEY = CreationExtras.Key() - val TRACKER_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - Model( - track = get(TRACK_KEY)!!, - tracker = get(TRACKER_KEY)!!, - ) - } - } + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(track: Track, trackerId: Long): Model } + val tracker = trackerManager.get(trackerId)!! + fun getRange(): Iterable { val endRange = if (track.totalChapters > 0) { track.totalChapters @@ -445,7 +420,7 @@ private data class TrackChapterSelectorScreen( } } -private data class TrackScoreSelectorScreen( +data class TrackScoreSelectorScreen( private val track: Track, private val serviceId: Long, ) : Screen() { @@ -453,13 +428,7 @@ private data class TrackScoreSelectorScreen( @Composable override fun Content() { val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel( - factory = Model.Factory, - extras = CreationExtras { - set(Model.TRACK_KEY, track) - set(Model.TRACKER_KEY, Injekt.get().get(serviceId)!!) - }, - ) + val viewModel = assistedMetroViewModel { create(track = track, trackerId = serviceId) } val state by viewModel.state.collectAsState() TrackScoreSelector( @@ -474,26 +443,27 @@ private data class TrackScoreSelectorScreen( ) } + @AssistedInject class Model( - private val track: Track, - private val tracker: Tracker, + @Assisted private val track: Track, + @Assisted private val trackerId: Long, + trackerManager: TrackerManager, ) : ViewModel() { val state: StateFlow - field = MutableStateFlow(State(tracker.displayScore(track))) + field = MutableStateFlow(State("")) - companion object { - val TRACK_KEY = CreationExtras.Key() - val TRACKER_KEY = CreationExtras.Key() + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(track: Track, trackerId: Long): Model + } - val Factory = viewModelFactory { - initializer { - Model( - track = get(TRACK_KEY)!!, - tracker = get(TRACKER_KEY)!!, - ) - } - } + val tracker = trackerManager.get(trackerId)!! + + init { + tracker.displayScore(track).let(::setSelection) } fun getSelections(): List { @@ -517,7 +487,7 @@ private data class TrackScoreSelectorScreen( } } -private data class TrackDateSelectorScreen( +data class TrackDateSelectorScreen( private val track: Track, private val serviceId: Long, private val start: Boolean, @@ -573,14 +543,9 @@ private data class TrackDateSelectorScreen( @Composable override fun Content() { val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel( - factory = Model.Factory, - extras = CreationExtras { - set(Model.TRACK_KEY, track) - set(Model.TRACKER_KEY, Injekt.get().get(serviceId)!!) - set(Model.START_KEY, start) - }, - ) + val viewModel = assistedMetroViewModel { + create(track = track, trackerId = serviceId, start = start) + } val canRemove = if (start) { track.startDate > 0 @@ -604,28 +569,23 @@ private data class TrackDateSelectorScreen( ) } + @AssistedInject class Model( - private val track: Track, - private val tracker: Tracker, - private val start: Boolean, + @Assisted private val track: Track, + @Assisted private val trackerId: Long, + @Assisted private val start: Boolean, + trackerManager: TrackerManager, ) : ViewModel() { - companion object { - val TRACK_KEY = CreationExtras.Key() - val TRACKER_KEY = CreationExtras.Key() - val START_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - Model( - track = get(TRACK_KEY) as Track, - tracker = get(TRACKER_KEY) as Tracker, - start = get(START_KEY) as Boolean, - ) - } - } + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(track: Track, trackerId: Long, start: Boolean): Model } + private val tracker = trackerManager.get(trackerId)!! + // In UTC val initialSelection: Long get() { @@ -654,7 +614,7 @@ private data class TrackDateSelectorScreen( } } -private data class TrackDateRemoverScreen( +data class TrackDateRemoverScreen( private val track: Track, private val serviceId: Long, private val start: Boolean, @@ -663,14 +623,9 @@ private data class TrackDateRemoverScreen( @Composable override fun Content() { val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel( - factory = Model.Factory, - extras = CreationExtras { - set(Model.TRACK_KEY, track) - set(Model.TRACKER_KEY, Injekt.get().get(serviceId)!!) - set(Model.START_KEY, start) - }, - ) + val viewModel = assistedMetroViewModel { + create(track = track, trackerId = serviceId, start = start) + } AlertDialogContent( modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars), icon = { @@ -720,28 +675,23 @@ private data class TrackDateRemoverScreen( ) } + @AssistedInject class Model( - private val track: Track, - private val tracker: Tracker, - private val start: Boolean, + @Assisted private val track: Track, + @Assisted private val trackerId: Long, + @Assisted private val start: Boolean, + trackerManager: TrackerManager, ) : ViewModel() { - companion object { - val TRACK_KEY = CreationExtras.Key() - val TRACKER_KEY = CreationExtras.Key() - val START_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - Model( - track = get(TRACK_KEY) as Track, - tracker = get(TRACKER_KEY) as Tracker, - start = get(START_KEY) as Boolean, - ) - } - } + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(track: Track, trackerId: Long, start: Boolean): Model } + private val tracker = trackerManager.get(trackerId)!! + fun getServiceName() = tracker.name fun removeDate() { @@ -766,15 +716,14 @@ data class TrackerSearchScreen( @Composable override fun Content() { val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel( - factory = Model.Factory, - extras = CreationExtras { - set(Model.MANGA_ID_KEY, mangaId) - set(Model.CURRENT_URL_KEY, currentUrl) - set(Model.INITIAL_QUERY_KEY, initialQuery) - set(Model.TRACKER_KEY, Injekt.get().get(serviceId)!!) - }, - ) + val viewModel = assistedMetroViewModel { + create( + mangaId = mangaId, + currentUrl = currentUrl, + initialQuery = initialQuery, + trackerId = serviceId, + ) + } val state by viewModel.state.collectAsState() @@ -796,34 +745,32 @@ data class TrackerSearchScreen( ) } + @AssistedInject class Model( - private val mangaId: Long, - private val currentUrl: String?, - initialQuery: String, - private val tracker: Tracker, + @Assisted private val mangaId: Long, + @Assisted private val currentUrl: String?, + @Assisted initialQuery: String, + @Assisted private val trackerId: Long, + trackerManager: TrackerManager, ) : ViewModel() { val state: StateFlow field = MutableStateFlow(State()) - companion object { - val MANGA_ID_KEY = CreationExtras.Key() - val CURRENT_URL_KEY = CreationExtras.Key() - val INITIAL_QUERY_KEY = CreationExtras.Key() - val TRACKER_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - Model( - mangaId = get(MANGA_ID_KEY)!!, - currentUrl = get(CURRENT_URL_KEY), - initialQuery = get(INITIAL_QUERY_KEY)!!, - tracker = get(TRACKER_KEY)!!, - ) - } - } + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create( + mangaId: Long, + currentUrl: String?, + initialQuery: String, + trackerId: Long, + ): Model } + private val tracker = trackerManager.get(trackerId)!! + val supportsPrivateTracking = tracker.supportsPrivateTracking init { @@ -871,7 +818,7 @@ data class TrackerSearchScreen( } } -private data class TrackerRemoveScreen( +data class TrackerRemoveScreen( private val mangaId: Long, private val track: Track, private val serviceId: Long, @@ -880,14 +827,9 @@ private data class TrackerRemoveScreen( @Composable override fun Content() { val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel( - factory = Model.Factory, - extras = CreationExtras { - set(Model.MANGA_ID_KEY, mangaId) - set(Model.TRACK_KEY, track) - set(Model.TRACKER_KEY, Injekt.get().get(serviceId)!!) - }, - ) + val viewModel = assistedMetroViewModel { + create(mangaId = mangaId, track = track, trackerId = serviceId) + } val serviceName = viewModel.getName() var removeRemoteTrack by remember { mutableStateOf(false) } AlertDialogContent( @@ -950,29 +892,24 @@ private data class TrackerRemoveScreen( ) } + @AssistedInject class Model( - private val mangaId: Long, - private val track: Track, - private val tracker: Tracker, - private val deleteTrack: DeleteTrack = Injekt.get(), + @Assisted private val mangaId: Long, + @Assisted private val track: Track, + @Assisted private val trackerId: Long, + private val deleteTrack: DeleteTrack, + trackerManager: TrackerManager, ) : ViewModel() { - companion object { - val MANGA_ID_KEY = CreationExtras.Key() - val TRACK_KEY = CreationExtras.Key() - val TRACKER_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - Model( - mangaId = get(MANGA_ID_KEY) as Long, - track = get(TRACK_KEY) as Track, - tracker = get(TRACKER_KEY) as Tracker, - ) - } - } + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(mangaId: Long, track: Track, trackerId: Long): Model } + private val tracker = trackerManager.get(trackerId)!! + fun getName() = tracker.name fun isDeletable() = tracker is DeletableTracker diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt index ecf3f0b1a..f474296e6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt @@ -9,12 +9,16 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.tab.LocalTabNavigator import cafe.adriel.voyager.navigator.tab.TabOptions +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.core.preference.asState import eu.kanade.domain.base.BasePreferences import eu.kanade.presentation.more.MoreScreen @@ -34,8 +38,6 @@ import mihon.feature.support.SupportUsScreen import tachiyomi.core.common.util.lang.launchIO import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get data object MoreTab : Tab { @@ -58,7 +60,7 @@ data object MoreTab : Tab { @Composable override fun Content() { val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel() + val viewModel = metroViewModel() val downloadQueueState by viewModel.downloadQueueState.collectAsState() MoreScreen( downloadQueueStateProvider = { downloadQueueState }, @@ -77,9 +79,12 @@ data object MoreTab : Tab { } } +@Inject +@ViewModelKey +@ContributesIntoMap(AppScope::class) class MoreViewModel( - private val downloadManager: DownloadManager = Injekt.get(), - preferences: BasePreferences = Injekt.get(), + private val downloadManager: DownloadManager, + preferences: BasePreferences, ) : ViewModel() { var downloadedOnly by preferences.downloadedOnly.asState(viewModelScope) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/more/NewUpdateScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/more/NewUpdateScreen.kt index 5fa273f24..d2c08898b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/more/NewUpdateScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/more/NewUpdateScreen.kt @@ -4,10 +4,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.platform.LocalContext -import androidx.lifecycle.viewmodel.CreationExtras import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metrox.viewmodel.assistedMetroViewModel import eu.kanade.presentation.more.NewUpdateScreen import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.util.system.openInBrowser @@ -23,13 +23,10 @@ class NewUpdateScreen( override fun Content() { val navigator = LocalNavigator.currentOrThrow val context = LocalContext.current - val viewModel = viewModel( - factory = NewUpdateScreenModel.Factory, - extras = CreationExtras { - set(NewUpdateScreenModel.CHANGELOG_INFO_KEY, changelogInfo) - set(NewUpdateScreenModel.DOWNLOAD_LINK_KEY, downloadLink) - }, - ) + val viewModel = + assistedMetroViewModel { + create(changelogInfo = changelogInfo, downloadLink = downloadLink) + } val state by viewModel.state.collectAsState() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/more/NewUpdateScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/more/NewUpdateScreenModel.kt index 281f3b5fa..32b66c05b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/more/NewUpdateScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/more/NewUpdateScreenModel.kt @@ -5,9 +5,13 @@ import android.content.Intent import androidx.compose.runtime.Immutable import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.initializer -import androidx.lifecycle.viewmodel.viewModelFactory +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 eu.kanade.tachiyomi.extension.util.ExtensionInstaller import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.NetworkHelper @@ -25,20 +29,26 @@ import kotlinx.coroutines.launch import logcat.LogPriority import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.system.logcat -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.io.File +@AssistedInject class NewUpdateScreenModel( - changelogInfo: String, - private val downloadLink: String, - private val context: Context = Injekt.get(), - private val network: NetworkHelper = Injekt.get(), + @Assisted changelogInfo: String, + @Assisted private val downloadLink: String, + private val context: Context, + private val network: NetworkHelper, ) : ViewModel() { val state: StateFlow field = MutableStateFlow(State(changelogInfo = changelogInfo)) + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(changelogInfo: String, downloadLink: String): NewUpdateScreenModel + } + private val apkFile: File get() = File(context.externalCacheDir, "update.apk") @@ -106,18 +116,4 @@ class NewUpdateScreenModel( Downloaded, Failed, } - - companion object { - val CHANGELOG_INFO_KEY = CreationExtras.Key() - val DOWNLOAD_LINK_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - NewUpdateScreenModel( - changelogInfo = get(CHANGELOG_INFO_KEY)!!, - downloadLink = get(DOWNLOAD_LINK_KEY)!!, - ) - } - } - } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/more/OnboardingScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/more/OnboardingScreen.kt index 3e8a36bf3..fede598f9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/more/OnboardingScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/more/OnboardingScreen.kt @@ -4,26 +4,26 @@ import androidx.activity.compose.BackHandler import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.domain.base.BasePreferences import eu.kanade.presentation.more.onboarding.OnboardingScreen import eu.kanade.presentation.more.settings.screen.SearchableSettings import eu.kanade.presentation.more.settings.screen.SettingsDataScreen import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.ui.setting.SettingsScreen +import mihon.app.di.appGraph import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.collectAsState -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get class OnboardingScreen : Screen() { @Composable override fun Content() { + val context = LocalContext.current val navigator = LocalNavigator.currentOrThrow - val basePreferences = remember { Injekt.get() } + val basePreferences = remember { context.appGraph.basePreferences } val shownOnboardingFlow by basePreferences.shownOnboardingFlow.collectAsState() val finishOnboarding: () -> Unit = { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index 0955fec46..4bdb503ee 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -47,6 +47,7 @@ import androidx.lifecycle.lifecycleScope import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.google.android.material.transition.platform.MaterialContainerTransform import com.hippo.unifile.UniFile +import dev.zacsweers.metro.Inject import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.domain.base.BasePreferences import eu.kanade.presentation.reader.DisplayRefreshHost @@ -95,6 +96,8 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.sample import kotlinx.coroutines.launch import logcat.LogPriority +import mihon.app.di.AppGraph +import mihon.core.metro.metroGraph import tachiyomi.core.common.Constants import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.util.lang.launchIO @@ -102,13 +105,13 @@ import tachiyomi.core.common.util.lang.launchNonCancellable import tachiyomi.core.common.util.system.logcat import tachiyomi.i18n.MR import tachiyomi.presentation.core.util.collectAsState -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.io.ByteArrayOutputStream import kotlin.time.Duration.Companion.seconds class ReaderActivity : BaseActivity() { + private val graph: AppGraph by lazy { metroGraph() } + companion object { fun newIntent(context: Context, mangaId: Long?, chapterId: Long?): Intent { return Intent(context, ReaderActivity::class.java).apply { @@ -119,12 +122,13 @@ class ReaderActivity : BaseActivity() { } } - private val readerPreferences = Injekt.get() - private val preferences = Injekt.get() + @Inject private lateinit var readerPreferences: ReaderPreferences + + @Inject private lateinit var preferences: BasePreferences lateinit var binding: ReaderActivityBinding - val viewModel by viewModels() + val viewModel by viewModels { graph.viewModelFactory } private var assistUrl: String? = null /** @@ -147,6 +151,7 @@ class ReaderActivity : BaseActivity() { * Called when the activity is created. Initializes the presenter and configuration. */ override fun onCreate(savedInstanceState: Bundle?) { + graph.inject(this) registerSecureActivity(this) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { overrideActivityTransition( @@ -255,6 +260,7 @@ class ReaderActivity : BaseActivity() { readerState = viewModel.state, onChangeReadingMode = viewModel::setMangaReadingMode, onChangeOrientation = viewModel::setMangaOrientationType, + preferences = readerPreferences, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt index 47ce42651..39a235f6e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt @@ -1,21 +1,34 @@ package eu.kanade.tachiyomi.ui.reader -import android.app.Application +import android.content.Context import android.net.Uri import androidx.annotation.IntRange import androidx.compose.runtime.Immutable import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel +import androidx.lifecycle.createSavedStateHandle import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.CreationExtras +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.ViewModelAssistedFactory +import dev.zacsweers.metrox.viewmodel.ViewModelAssistedFactoryKey import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.domain.manga.interactor.SetMangaViewerFlags +import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.model.readerOrientation import eu.kanade.domain.manga.model.readingMode import eu.kanade.domain.source.interactor.GetIncognitoState import eu.kanade.domain.track.interactor.TrackChapter import eu.kanade.domain.track.service.TrackPreferences +import eu.kanade.tachiyomi.data.cache.ChapterCache +import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.database.models.toDomainChapter +import eu.kanade.tachiyomi.data.download.DownloadCache import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadProvider import eu.kanade.tachiyomi.data.download.model.Download @@ -74,9 +87,8 @@ import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.manga.interactor.GetManga import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.service.SourceManager +import tachiyomi.source.local.image.LocalCoverManager import tachiyomi.source.local.isLocal -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.util.Date import kotlin.getValue import kotlin.time.Clock @@ -84,27 +96,45 @@ import kotlin.time.Clock /** * Presenter used by the activity to perform background operations. */ -class ReaderViewModel @JvmOverloads constructor( - private val savedState: SavedStateHandle, - private val sourceManager: SourceManager = Injekt.get(), - private val downloadManager: DownloadManager = Injekt.get(), - private val downloadProvider: DownloadProvider = Injekt.get(), - private val imageSaver: ImageSaver = Injekt.get(), - val readerPreferences: ReaderPreferences = Injekt.get(), - private val basePreferences: BasePreferences = Injekt.get(), - private val downloadPreferences: DownloadPreferences = Injekt.get(), - private val trackPreferences: TrackPreferences = Injekt.get(), - private val trackChapter: TrackChapter = Injekt.get(), - private val getManga: GetManga = Injekt.get(), - private val getChaptersByMangaId: GetChaptersByMangaId = Injekt.get(), - private val getNextChapters: GetNextChapters = Injekt.get(), - private val upsertHistory: UpsertHistory = Injekt.get(), - private val updateChapter: UpdateChapter = Injekt.get(), - private val setMangaViewerFlags: SetMangaViewerFlags = Injekt.get(), - private val getIncognitoState: GetIncognitoState = Injekt.get(), - private val libraryPreferences: LibraryPreferences = Injekt.get(), +@AssistedInject +class ReaderViewModel( + @Assisted private val savedState: SavedStateHandle, + private val context: Context, + private val sourceManager: SourceManager, + private val downloadManager: DownloadManager, + private val downloadProvider: DownloadProvider, + private val imageSaver: ImageSaver, + val readerPreferences: ReaderPreferences, + private val basePreferences: BasePreferences, + private val downloadPreferences: DownloadPreferences, + private val trackPreferences: TrackPreferences, + private val trackChapter: TrackChapter, + private val getManga: GetManga, + private val getChaptersByMangaId: GetChaptersByMangaId, + private val getNextChapters: GetNextChapters, + private val upsertHistory: UpsertHistory, + private val updateChapter: UpdateChapter, + private val setMangaViewerFlags: SetMangaViewerFlags, + private val getIncognitoState: GetIncognitoState, + private val libraryPreferences: LibraryPreferences, + private val coverManager: LocalCoverManager, + private val updateManga: UpdateManga, + private val coverCache: CoverCache, + private val chapterCache: ChapterCache, + private val downloadCache: DownloadCache, ) : ViewModel() { + @AssistedFactory + @ViewModelAssistedFactoryKey(ReaderViewModel::class) + @ContributesIntoMap(AppScope::class) + fun interface Factory : ViewModelAssistedFactory { + override fun create(extras: CreationExtras): ReaderViewModel { + return create(extras.createSavedStateHandle()) + } + + fun create(@Assisted savedState: SavedStateHandle): ReaderViewModel + } + private val mutableState = MutableStateFlow(State()) val state = mutableState.asStateFlow() @@ -226,7 +256,7 @@ class ReaderViewModel @JvmOverloads constructor( } .run { if (basePreferences.downloadedOnly.get()) { - filterDownloaded(manga) + filterDownloaded(manga, downloadCache) } else { this } @@ -290,9 +320,8 @@ class ReaderViewModel @JvmOverloads constructor( mutableState.update { it.copy(manga = manga) } if (chapterId == -1L) chapterId = initialChapterId - val context = Injekt.get() val source = sourceManager.getOrStub(manga.source) - loader = ChapterLoader(context, downloadManager, downloadProvider, manga, source) + loader = ChapterLoader(context, downloadManager, downloadProvider, chapterCache, manga, source) loadChapter(loader!!, chapterList.first { chapterId == it.chapter.id }) } catch (e: Throwable) { @@ -801,7 +830,6 @@ class ReaderViewModel @JvmOverloads constructor( if (page?.status != Page.State.Ready) return val manga = manga ?: return - val context = Injekt.get() val notifier = SaveImageNotifier(context) notifier.onClear() @@ -849,7 +877,6 @@ class ReaderViewModel @JvmOverloads constructor( if (page?.status != Page.State.Ready) return val manga = manga ?: return - val context = Injekt.get() val destDir = context.cacheImageDir val filename = generateFilename(manga, page) @@ -882,7 +909,7 @@ class ReaderViewModel @JvmOverloads constructor( viewModelScope.launchNonCancellable { val result = try { - manga.editCover(Injekt.get(), stream()) + manga.editCover(coverManager, stream(), updateManga, coverCache) if (manga.isLocal() || manga.favorite) { SetAsCoverResult.Success } else { @@ -915,7 +942,6 @@ class ReaderViewModel @JvmOverloads constructor( if (!trackPreferences.autoUpdateTrack.get()) return val manga = manga ?: return - val context = Injekt.get() viewModelScope.launchNonCancellable { trackChapter.await(context, manga.id, readerChapter.chapter.chapter_number.toDouble()) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt index 761c212a1..3cbc9416d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt @@ -1,6 +1,7 @@ package eu.kanade.tachiyomi.ui.reader.loader import android.content.Context +import eu.kanade.tachiyomi.data.cache.ChapterCache import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadProvider import eu.kanade.tachiyomi.source.Source @@ -24,6 +25,7 @@ class ChapterLoader( private val context: Context, private val downloadManager: DownloadManager, private val downloadProvider: DownloadProvider, + private val chapterCache: ChapterCache, private val manga: Manga, private val source: Source, ) { @@ -100,7 +102,7 @@ class ChapterLoader( is Format.Epub -> EpubPageLoader(format.file.epubReader(context)) } } - source is HttpSource -> HttpPageLoader(chapter, source) + source is HttpSource -> HttpPageLoader(chapter, source, chapterCache) source is StubSource -> error(context.stringResource(MR.strings.source_not_installed, source.toString())) else -> error(context.stringResource(MR.strings.loader_not_implemented_error)) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt index 026faac3e..e948c86af 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt @@ -1,6 +1,6 @@ package eu.kanade.tachiyomi.ui.reader.loader -import android.app.Application +import android.content.Context import android.net.Uri import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.data.database.models.toDomainChapter @@ -25,7 +25,7 @@ internal class DownloadPageLoader( private val downloadProvider: DownloadProvider, ) : PageLoader() { - private val context: Application by injectLazy() + private val context: Context by injectLazy() private var archivePageLoader: ArchivePageLoader? = null diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt index 81530afa3..227a986c4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt @@ -17,8 +17,6 @@ import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.suspendCancellableCoroutine import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.lang.withIOContext -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.util.concurrent.PriorityBlockingQueue import kotlin.concurrent.atomics.AtomicInt import kotlin.concurrent.atomics.ExperimentalAtomicApi @@ -31,7 +29,7 @@ import kotlin.math.min internal class HttpPageLoader( private val chapter: ReaderChapter, private val source: HttpSource, - private val chapterCache: ChapterCache = Injekt.get(), + private val chapterCache: ChapterCache, ) : PageLoader() { private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt index e229d2cd2..79a1eaf26 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt @@ -3,12 +3,17 @@ package eu.kanade.tachiyomi.ui.reader.setting import android.os.Build import androidx.compose.ui.graphics.BlendMode 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.PreferenceStore import tachiyomi.core.common.preference.getEnum import tachiyomi.core.common.preference.getEnumSet import tachiyomi.i18n.MR +@Inject +@SingleIn(AppScope::class) class ReaderPreferences( preferenceStore: PreferenceStore, ) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsViewModel.kt index 885188ecb..2ceca6db4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsViewModel.kt @@ -8,14 +8,12 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get class ReaderSettingsViewModel( readerState: StateFlow, val onChangeReadingMode: (ReadingMode) -> Unit, val onChangeOrientation: (ReaderOrientation) -> Unit, - val preferences: ReaderPreferences = Injekt.get(), + val preferences: ReaderPreferences, ) : ViewModel() { val viewerFlow = readerState diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingMode.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingMode.kt index ec13f9d0a..22efe05c5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingMode.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingMode.kt @@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.reader.setting import androidx.annotation.DrawableRes import dev.icerock.moko.resources.StringResource -import eu.kanade.domain.base.BasePreferences import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.ui.reader.viewer.Viewer @@ -12,9 +11,8 @@ import eu.kanade.tachiyomi.ui.reader.viewer.pager.VerticalPagerViewer import eu.kanade.tachiyomi.ui.reader.viewer.webgpu.WebGpuViewer import eu.kanade.tachiyomi.ui.reader.viewer.webgpu.WebGpuViewerContinuous import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer +import mihon.app.di.appGraph import tachiyomi.i18n.MR -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get enum class ReadingMode( val stringRes: StringResource, @@ -72,7 +70,7 @@ enum class ReadingMode( } fun toViewer(preference: Int?, activity: ReaderActivity): Viewer { - if (Injekt.get().highQualityRenderer.get()) { + if (activity.appGraph.basePreferences.highQualityRenderer.get()) { return when (fromPreference(preference)) { LEFT_TO_RIGHT -> WebGpuViewer(activity, isReversed = false, isVertical = false) RIGHT_TO_LEFT -> WebGpuViewer(activity, isReversed = true, isVertical = false) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt index 3dc33deb1..b0aa9bb77 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt @@ -33,7 +33,6 @@ import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_IN_OUT import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_OUT_QUAD import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE import com.github.chrisbanes.photoview.PhotoView -import eu.kanade.domain.base.BasePreferences import eu.kanade.tachiyomi.data.coil.cropBorders import eu.kanade.tachiyomi.data.coil.customDecoder import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonSubsamplingImageView @@ -41,8 +40,6 @@ import eu.kanade.tachiyomi.util.system.animatorDurationScale import eu.kanade.tachiyomi.util.view.isVisibleOnScreen import okio.BufferedSource import tachiyomi.core.common.util.system.ImageUtil -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get /** * A wrapper view for showing page image. diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt index d73f677ac..6b5e0af3e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt @@ -13,8 +13,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get /** * Configuration used by pager viewers. @@ -22,7 +20,7 @@ import uy.kohesive.injekt.api.get class PagerConfig( private val viewer: PagerViewer, scope: CoroutineScope, - readerPreferences: ReaderPreferences = Injekt.get(), + readerPreferences: ReaderPreferences, ) : ViewerConfig(readerPreferences, scope) { var theme = readerPreferences.readerTheme.get() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt index 1d39507b3..3bd7feaef 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt @@ -11,7 +11,6 @@ import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.viewpager.widget.ViewPager import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition import eu.kanade.tachiyomi.ui.reader.model.InsertPage @@ -21,8 +20,8 @@ import eu.kanade.tachiyomi.ui.reader.viewer.Viewer import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation.NavigationRegion import kotlinx.coroutines.MainScope import kotlinx.coroutines.cancel +import mihon.app.di.appGraph import tachiyomi.core.common.util.system.logcat -import uy.kohesive.injekt.injectLazy import kotlin.math.min /** @@ -31,7 +30,9 @@ import kotlin.math.min @Suppress("LeakingThis") abstract class PagerViewer(val activity: ReaderActivity) : Viewer { - val downloadManager: DownloadManager by injectLazy() + val graph by lazy { activity.appGraph } + val downloadManager by lazy { graph.downloadManager } + val readerPreferences by lazy { graph.readerPreferences } private val scope = MainScope() @@ -44,7 +45,7 @@ abstract class PagerViewer(val activity: ReaderActivity) : Viewer { /** * Configuration used by the pager, like allow taps, scale mode on images, page transitions... */ - val config = PagerConfig(this, scope) + val config = PagerConfig(this, scope, readerPreferences) /** * Adapter of the pager. diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webgpu/WebGpuConfig.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webgpu/WebGpuConfig.kt index 99757cd05..79db9ed82 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webgpu/WebGpuConfig.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webgpu/WebGpuConfig.kt @@ -13,8 +13,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get /** * Configuration used by pager viewers. @@ -22,7 +20,7 @@ import uy.kohesive.injekt.api.get class WebGpuConfig( private val viewer: WebGpuViewer, scope: CoroutineScope, - readerPreferences: ReaderPreferences = Injekt.get(), + readerPreferences: ReaderPreferences, ) : ViewerConfig(readerPreferences, scope) { var theme = readerPreferences.readerTheme.get() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webgpu/WebGpuViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webgpu/WebGpuViewer.kt index c392c8e63..627c4d481 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webgpu/WebGpuViewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webgpu/WebGpuViewer.kt @@ -52,6 +52,7 @@ import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.takeWhile import kotlinx.coroutines.launch import logcat.LogPriority +import mihon.app.di.appGraph import tachiyomi.core.common.util.system.logcat import java.util.concurrent.Executors import kotlin.math.abs @@ -66,6 +67,8 @@ open class WebGpuViewer( open val isContinuous: Boolean = false + val readerPreferences by lazy { activity.appGraph.readerPreferences } + private fun readerBackgroundColor(): Int = activity.baseContext.readerBackgroundColor(config.theme) private fun readerOnBackgroundColor(): Int = MaterialColors.getColor( @@ -186,7 +189,7 @@ open class WebGpuViewer( /** * Configuration used by the pager, like allow taps, scale mode on images, page transitions... */ - val config = WebGpuConfig(this, scope) + val config = WebGpuConfig(this, scope, readerPreferences) var viewerChapters: ViewerChapters? = null diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonConfig.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonConfig.kt index 278a3d938..01b6d7de2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonConfig.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonConfig.kt @@ -13,15 +13,13 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get /** * Configuration used by webtoon viewers. */ class WebtoonConfig( scope: CoroutineScope, - readerPreferences: ReaderPreferences = Injekt.get(), + readerPreferences: ReaderPreferences, ) : ViewerConfig(readerPreferences, scope) { var themeChangedListener: (() -> Unit)? = null diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt index 33d21d1eb..228145a66 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt @@ -11,20 +11,16 @@ import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.WebtoonLayoutManager -import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters -import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.ui.reader.viewer.Viewer import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation.NavigationRegion import kotlinx.coroutines.MainScope import kotlinx.coroutines.cancel +import mihon.app.di.appGraph import tachiyomi.core.common.util.system.logcat -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get -import uy.kohesive.injekt.injectLazy import kotlin.math.max import kotlin.math.min @@ -33,7 +29,9 @@ import kotlin.math.min */ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = true) : Viewer { - val downloadManager: DownloadManager by injectLazy() + val graph by lazy { activity.appGraph } + val downloadManager by lazy { graph.downloadManager } + val readerPreferences by lazy { graph.readerPreferences } private val scope = MainScope() @@ -60,7 +58,7 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr /** * Configuration used by this viewer, like allow taps, or crop image borders. */ - val config = WebtoonConfig(scope) + val config = WebtoonConfig(scope, readerPreferences) /** * Adapter of the recycler view. @@ -72,11 +70,7 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr */ private var currentPage: Any? = null - private val threshold: Int = - Injekt.get() - .readerHideThreshold - .get() - .threshold + private val threshold: Int by lazy { readerPreferences.readerHideThreshold.get().threshold } init { recycler.setItemViewCacheSize(RECYCLER_VIEW_CACHE_SIZE) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/BaseOAuthLoginActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/BaseOAuthLoginActivity.kt index 08dcee264..23e93d93f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/BaseOAuthLoginActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/BaseOAuthLoginActivity.kt @@ -3,21 +3,25 @@ package eu.kanade.tachiyomi.ui.setting.track import android.content.Intent import android.net.Uri import android.os.Bundle +import dev.zacsweers.metro.HasMemberInjections +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.data.track.TrackerManager import eu.kanade.tachiyomi.ui.base.activity.BaseActivity import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.util.view.setComposeContent +import mihon.app.di.appGraph import tachiyomi.presentation.core.screens.LoadingScreen -import uy.kohesive.injekt.injectLazy +@HasMemberInjections abstract class BaseOAuthLoginActivity : BaseActivity() { - internal val trackerManager: TrackerManager by injectLazy() + @Inject protected lateinit var trackerManager: TrackerManager abstract fun handleResult(uri: Uri) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + appGraph.inject(this) setComposeContent { LoadingScreen() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreen.kt index 27fcf3a66..d5a512495 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreen.kt @@ -3,9 +3,9 @@ package eu.kanade.tachiyomi.ui.stats import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metrox.viewmodel.metroViewModel import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.more.stats.StatsScreenContent import eu.kanade.presentation.more.stats.StatsScreenState @@ -21,7 +21,7 @@ class StatsScreen : Screen() { override fun Content() { val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel() + val viewModel = metroViewModel() val state by viewModel.state.collectAsState() Scaffold( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsViewModel.kt index 94df2ea5d..c5902132d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsViewModel.kt @@ -4,6 +4,11 @@ import androidx.compose.ui.util.fastDistinctBy import androidx.compose.ui.util.fastFilter import androidx.lifecycle.ViewModel 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.core.util.fastCountNot import eu.kanade.presentation.more.stats.StatsScreenState import eu.kanade.presentation.more.stats.data.StatsData @@ -24,16 +29,17 @@ import tachiyomi.domain.manga.interactor.GetLibraryManga import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.domain.track.model.Track import tachiyomi.source.local.isLocal -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +@Inject +@ViewModelKey +@ContributesIntoMap(AppScope::class, binding = binding()) class StatsViewModel( - private val downloadManager: DownloadManager = Injekt.get(), - private val getLibraryManga: GetLibraryManga = Injekt.get(), - private val getTotalReadDuration: GetTotalReadDuration = Injekt.get(), - private val getTracks: GetTracks = Injekt.get(), - private val preferences: LibraryPreferences = Injekt.get(), - private val trackerManager: TrackerManager = Injekt.get(), + private val downloadManager: DownloadManager, + private val getLibraryManga: GetLibraryManga, + private val getTotalReadDuration: GetTotalReadDuration, + private val getTracks: GetTracks, + private val preferences: LibraryPreferences, + private val trackerManager: TrackerManager, ) : ViewModel() { val state: StateFlow diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesSettingsViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesSettingsViewModel.kt index 4ca15163d..a2b350dcb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesSettingsViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesSettingsViewModel.kt @@ -1,18 +1,23 @@ package eu.kanade.tachiyomi.ui.updates import androidx.lifecycle.ViewModel +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesIntoMap +import dev.zacsweers.metro.Inject +import dev.zacsweers.metrox.viewmodel.ViewModelKey import tachiyomi.core.common.preference.Preference import tachiyomi.core.common.preference.TriState import tachiyomi.core.common.preference.getAndSet import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.category.model.Category import tachiyomi.domain.updates.service.UpdatesPreferences -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +@Inject +@ViewModelKey +@ContributesIntoMap(AppScope::class) class UpdatesSettingsViewModel( - val updatesPreferences: UpdatesPreferences = Injekt.get(), - val getCategories: GetCategories = Injekt.get(), + val updatesPreferences: UpdatesPreferences, + val getCategories: GetCategories, ) : ViewModel() { val includedCategories = updatesPreferences.filterIncludedCategories diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt index e49aa7934..45d0d325c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt @@ -15,6 +15,7 @@ import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.tab.LocalTabNavigator import cafe.adriel.voyager.navigator.tab.TabOptions +import dev.zacsweers.metrox.viewmodel.metroViewModel import eu.kanade.presentation.updates.UpdateScreen import eu.kanade.presentation.updates.UpdatesDeleteConfirmationDialog import eu.kanade.presentation.updates.UpdatesFilterDialog @@ -54,8 +55,8 @@ data object UpdatesTab : Tab { override fun Content() { val context = LocalContext.current val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel() - val settingsViewModel = viewModel() + val viewModel = metroViewModel() + val settingsViewModel = metroViewModel() val state by viewModel.state.collectAsStateWithLifecycle() UpdateScreen( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesViewModel.kt index 939b3f93e..48b7dde46 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesViewModel.kt @@ -1,12 +1,17 @@ package eu.kanade.tachiyomi.ui.updates -import android.app.Application +import android.content.Context import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Immutable import androidx.compose.runtime.getValue import androidx.compose.ui.util.fastFilter import androidx.lifecycle.ViewModel 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.core.preference.asState import eu.kanade.core.util.addOrRemove import eu.kanade.core.util.insertSeparators @@ -18,6 +23,7 @@ import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.library.LibraryUpdateJob import eu.kanade.tachiyomi.util.lang.toLocalDate +import eu.kanade.tachiyomi.util.system.workManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow @@ -54,25 +60,28 @@ import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.updates.interactor.GetUpdates import tachiyomi.domain.updates.model.UpdatesWithRelations import tachiyomi.domain.updates.service.UpdatesPreferences -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.time.Clock import kotlin.time.Duration.Companion.seconds +@Inject +@ViewModelKey +@ContributesIntoMap(AppScope::class, binding = binding()) class UpdatesViewModel( - private val sourceManager: SourceManager = Injekt.get(), - private val downloadManager: DownloadManager = Injekt.get(), - private val downloadCache: DownloadCache = Injekt.get(), - private val updateChapter: UpdateChapter = Injekt.get(), - private val setReadStatus: SetReadStatus = Injekt.get(), - private val getUpdates: GetUpdates = Injekt.get(), - private val getManga: GetManga = Injekt.get(), - private val getChapter: GetChapter = Injekt.get(), - private val libraryPreferences: LibraryPreferences = Injekt.get(), - private val updatesPreferences: UpdatesPreferences = Injekt.get(), - val snackbarHostState: SnackbarHostState = SnackbarHostState(), + private val context: Context, + private val sourceManager: SourceManager, + private val downloadManager: DownloadManager, + private val downloadCache: DownloadCache, + private val updateChapter: UpdateChapter, + private val setReadStatus: SetReadStatus, + private val getUpdates: GetUpdates, + private val getManga: GetManga, + private val getChapter: GetChapter, + private val libraryPreferences: LibraryPreferences, + private val updatesPreferences: UpdatesPreferences, ) : ViewModel() { + val snackbarHostState: SnackbarHostState = SnackbarHostState() + private val _events: Channel = Channel(Int.MAX_VALUE) val events: Flow = _events.receiveAsFlow() @@ -226,7 +235,7 @@ class UpdatesViewModel( } fun updateLibrary(): Boolean { - val started = LibraryUpdateJob.startNow(Injekt.get()) + val started = LibraryUpdateJob.startNow(context.workManager) viewModelScope.launch { _events.send(Event.LibraryUpdateTriggered(started)) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt index 8506fa8fc..b15d1adff 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt @@ -7,6 +7,7 @@ import android.os.Build import android.os.Bundle import android.widget.Toast import androidx.core.net.toUri +import dev.zacsweers.metro.Inject import eu.kanade.presentation.webview.WebViewScreenContent import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.network.NetworkHelper @@ -18,16 +19,17 @@ import eu.kanade.tachiyomi.util.system.toShareIntent import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.view.setComposeContent import logcat.LogPriority +import mihon.app.di.appGraph import okhttp3.HttpUrl.Companion.toHttpUrl import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.source.service.SourceManager import tachiyomi.i18n.MR -import uy.kohesive.injekt.injectLazy class WebViewActivity : BaseActivity() { - private val sourceManager: SourceManager by injectLazy() - private val network: NetworkHelper by injectLazy() + @Inject private lateinit var sourceManager: SourceManager + + @Inject private lateinit var network: NetworkHelper private var assistUrl: String? = null @@ -47,6 +49,7 @@ class WebViewActivity : BaseActivity() { overridePendingTransition(R.anim.shared_axis_x_push_enter, R.anim.shared_axis_x_push_exit) } super.onCreate(savedInstanceState) + appGraph.inject(this) if (!WebViewUtil.supportsWebView(this)) { toast(MR.strings.information_webview_required, Toast.LENGTH_LONG) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewScreen.kt index 385ecd95b..ec40661f9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewScreen.kt @@ -2,10 +2,9 @@ package eu.kanade.tachiyomi.ui.webview import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext -import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metrox.viewmodel.assistedMetroViewModel import eu.kanade.presentation.util.AssistContentScreen import eu.kanade.presentation.util.Screen import eu.kanade.presentation.webview.WebViewScreenContent @@ -24,12 +23,8 @@ class WebViewScreen( override fun Content() { val navigator = LocalNavigator.currentOrThrow val context = LocalContext.current - val viewModel = viewModel( - factory = WebViewViewModel.Factory, - extras = CreationExtras { - set(WebViewViewModel.SOURCE_ID_KEY, sourceId) - }, - ) + val viewModel = + assistedMetroViewModel { create(sourceId = sourceId) } WebViewScreenContent( onNavigateUp = { navigator.pop() }, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewViewModel.kt index 47e71940d..9ba4b21a4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewViewModel.kt @@ -3,9 +3,13 @@ package eu.kanade.tachiyomi.ui.webview import android.content.Context import androidx.core.net.toUri import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.initializer -import androidx.lifecycle.viewmodel.viewModelFactory +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 eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.util.system.openInBrowser @@ -15,25 +19,19 @@ import logcat.LogPriority import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.source.service.SourceManager -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +@AssistedInject class WebViewViewModel( - val sourceId: Long?, - private val sourceManager: SourceManager = Injekt.get(), - private val network: NetworkHelper = Injekt.get(), + @Assisted val sourceId: Long?, + private val sourceManager: SourceManager, + private val network: NetworkHelper, ) : ViewModel() { - companion object { - val SOURCE_ID_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - WebViewViewModel( - sourceId = get(SOURCE_ID_KEY), - ) - } - } + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(sourceId: Long?): WebViewViewModel } var headers = emptyMap() diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt b/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt index 038444c56..038983c7c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.util import android.content.Context import android.os.Build +import dev.zacsweers.metro.Inject import eu.kanade.domain.base.BasePreferences import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.extension.ExtensionManager @@ -16,15 +17,14 @@ import kotlinx.datetime.offsetAt import kotlinx.datetime.toLocalDateTime import tachiyomi.core.common.util.lang.withNonCancellableContext import tachiyomi.core.common.util.lang.withUIContext -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.time.Clock +@Inject class CrashLogUtil( private val context: Context, - private val extensionManager: ExtensionManager = Injekt.get(), - private val preferences: BasePreferences = Injekt.get(), - private val networkPreferences: NetworkPreferences = Injekt.get(), + private val extensionManager: ExtensionManager, + private val preferences: BasePreferences, + private val networkPreferences: NetworkPreferences, ) { suspend fun dumpLogs(exception: Throwable? = null) = withNonCancellableContext { diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt index c39357b25..e13bf9602 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt @@ -6,12 +6,10 @@ import eu.kanade.tachiyomi.data.cache.CoverCache import tachiyomi.domain.manga.model.Manga import tachiyomi.source.local.image.LocalCoverManager import tachiyomi.source.local.isLocal -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.io.InputStream import kotlin.time.Clock -fun Manga.removeCovers(coverCache: CoverCache = Injekt.get()): Manga { +fun Manga.removeCovers(coverCache: CoverCache): Manga { if (isLocal()) return this return if (coverCache.deleteFromCache(this, true) > 0) { copy(coverLastModified = Clock.System.now().toEpochMilliseconds()) @@ -23,8 +21,8 @@ fun Manga.removeCovers(coverCache: CoverCache = Injekt.get()): Manga { suspend fun Manga.editCover( coverManager: LocalCoverManager, stream: InputStream, - updateManga: UpdateManga = Injekt.get(), - coverCache: CoverCache = Injekt.get(), + updateManga: UpdateManga, + coverCache: CoverCache, ) { if (isLocal()) { coverManager.update(toSManga(), stream) diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterFilterDownloaded.kt b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterFilterDownloaded.kt index f33313c68..2f7cba6b4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterFilterDownloaded.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterFilterDownloaded.kt @@ -4,16 +4,12 @@ import eu.kanade.tachiyomi.data.download.DownloadCache import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.manga.model.Manga import tachiyomi.source.local.isLocal -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get /** * Returns a copy of the list with not downloaded chapters removed. */ -fun List.filterDownloaded(manga: Manga): List { +fun List.filterDownloaded(manga: Manga, downloadCache: DownloadCache): List { if (manga.isLocal()) return this - val downloadCache: DownloadCache = Injekt.get() - return filter { downloadCache.isChapterDownloaded(it.name, it.scanlator, it.url, manga.title, manga.source, false) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt index 3965fcdbb..29f4a81f8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt @@ -15,19 +15,16 @@ import androidx.appcompat.view.ContextThemeWrapper import androidx.core.content.getSystemService import androidx.core.net.toUri import com.hippo.unifile.UniFile -import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.model.ThemeMode import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.base.delegate.ThemingDelegate -import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.util.lang.truncateCenter import logcat.LogPriority +import mihon.app.di.appGraph import rikka.shizuku.ShizukuProvider import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.util.system.logcat import tachiyomi.i18n.MR -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.io.File /** @@ -120,8 +117,9 @@ fun Context.readerBackgroundColor(readerTheme: Int): Int = when (readerTheme) { * https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java;l=348;drc=e28752c96fc3fb4d3354781469a1af3dbded4898 */ fun Context.createReaderThemeContext(): Context { - val preferences = Injekt.get() - val readerPreferences = Injekt.get() + val graph = appGraph + val preferences = graph.uiPreferences + val readerPreferences = graph.readerPreferences val themeMode = preferences.themeMode.get() val isDarkBackground = when (readerPreferences.readerTheme.get()) { 1, 2 -> true // Black, Gray diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/DisplayExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/DisplayExtensions.kt index 1a0fd0faa..ad8845780 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/DisplayExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/DisplayExtensions.kt @@ -7,8 +7,6 @@ import android.os.Build import android.view.View import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.model.TabletUiMode -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get private const val TABLET_UI_REQUIRED_SCREEN_WIDTH_DP = 720 @@ -23,9 +21,9 @@ fun Configuration.isTabletUi(): Boolean { } // TODO: move the logic to `isTabletUi()` when main activity is rewritten in Compose -fun Context.prepareTabletUiContext(): Context { +fun Context.prepareTabletUiContext(uiPreferences: UiPreferences): Context { val configuration = resources.configuration - val expected = when (Injekt.get().tabletUiMode.get()) { + val expected = when (uiPreferences.tabletUiMode.get()) { TabletUiMode.AUTOMATIC -> configuration.smallestScreenWidthDp >= when (configuration.orientation) { Configuration.ORIENTATION_PORTRAIT -> TABLET_UI_MIN_SCREEN_WIDTH_PORTRAIT_DP diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt index 60d357a53..8c7789661 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt @@ -20,8 +20,10 @@ import androidx.compose.runtime.CompositionContext import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy +import dev.zacsweers.metrox.viewmodel.LocalMetroViewModelFactory import eu.kanade.presentation.theme.TachiyomiTheme import eu.kanade.tachiyomi.R +import mihon.app.di.appGraph inline fun ComponentActivity.setComposeContent( parent: CompositionContext? = null, @@ -32,6 +34,7 @@ inline fun ComponentActivity.setComposeContent( CompositionLocalProvider( LocalTextStyle provides MaterialTheme.typography.bodySmall, LocalContentColor provides MaterialTheme.colorScheme.onBackground, + LocalMetroViewModelFactory provides appGraph.viewModelFactory, ) { content() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiTextInputEditText.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiTextInputEditText.kt index f6b58026a..62071ab0c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiTextInputEditText.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiTextInputEditText.kt @@ -14,8 +14,7 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +import mihon.app.di.appGraph /** * A custom [TextInputEditText] that sets [EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING] to imeOptions @@ -34,7 +33,8 @@ class TachiyomiTextInputEditText @JvmOverloads constructor( override fun onAttachedToWindow() { super.onAttachedToWindow() scope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - setIncognito(scope!!) + val basePreferences = context.appGraph.basePreferences + setIncognito(basePreferences, scope!!) } override fun onDetachedFromWindow() { @@ -48,8 +48,8 @@ class TachiyomiTextInputEditText @JvmOverloads constructor( * Sets Flow to this [EditText] that sets [EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING] to imeOptions * if [BasePreferences.incognitoMode] is true. Some IMEs may not respect this flag. */ - fun EditText.setIncognito(viewScope: CoroutineScope) { - Injekt.get().incognitoMode.changes() + fun EditText.setIncognito(basePreferences: BasePreferences, viewScope: CoroutineScope) { + basePreferences.incognitoMode.changes() .onEach { imeOptions = if (it) { imeOptions or EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING diff --git a/app/src/main/java/mihon/app/di/AppBindings.kt b/app/src/main/java/mihon/app/di/AppBindings.kt new file mode 100644 index 000000000..219bec558 --- /dev/null +++ b/app/src/main/java/mihon/app/di/AppBindings.kt @@ -0,0 +1,85 @@ +package mihon.app.di + +import android.content.Context +import androidx.sqlite.driver.bundled.BundledSQLiteDriver +import app.cash.sqldelight.db.SqlDriver +import com.eygraber.sqldelight.androidx.driver.AndroidxSqliteConfiguration +import com.eygraber.sqldelight.androidx.driver.AndroidxSqliteDatabaseType +import com.eygraber.sqldelight.androidx.driver.AndroidxSqliteDriver +import com.eygraber.sqldelight.androidx.driver.FileProvider +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.BindingContainer +import dev.zacsweers.metro.Provides +import dev.zacsweers.metro.SingleIn +import kotlinx.serialization.json.Json +import kotlinx.serialization.protobuf.ProtoBuf +import nl.adaptivity.xmlutil.XmlDeclMode +import nl.adaptivity.xmlutil.core.XmlVersion +import nl.adaptivity.xmlutil.serialization.XML +import tachiyomi.data.Chapters +import tachiyomi.data.Database +import tachiyomi.data.DateColumnAdapter +import tachiyomi.data.History +import tachiyomi.data.Mangas +import tachiyomi.data.MemoColumnAdapter +import tachiyomi.data.StringListColumnAdapter +import tachiyomi.data.UpdateStrategyColumnAdapter + +@BindingContainer +object AppBindings { + + @Provides + @SingleIn(AppScope::class) + fun providesSqlDriver(context: Context): SqlDriver { + return AndroidxSqliteDriver( + driver = BundledSQLiteDriver(), + databaseType = AndroidxSqliteDatabaseType.FileProvider(context, "tachiyomi.db"), + schema = Database.Schema, + configuration = AndroidxSqliteConfiguration( + isForeignKeyConstraintsEnabled = true, + ), + ) + } + + @Provides + @SingleIn(AppScope::class) + fun providesDatabase(driver: SqlDriver): Database { + return Database( + driver = driver, + historyAdapter = History.Adapter( + last_readAdapter = DateColumnAdapter, + ), + mangasAdapter = Mangas.Adapter( + genreAdapter = StringListColumnAdapter, + update_strategyAdapter = UpdateStrategyColumnAdapter, + memoAdapter = MemoColumnAdapter, + ), + chaptersAdapter = Chapters.Adapter( + memoAdapter = MemoColumnAdapter, + ), + ) + } + + @Provides + @SingleIn(AppScope::class) + fun providesJson(): Json = Json { + ignoreUnknownKeys = true + explicitNulls = false + } + + @Provides + @SingleIn(AppScope::class) + fun providesXML(): XML = XML.v1 { + policy { + ignoreUnknownChildren() + autoPolymorphic = true + } + xmlDeclMode = XmlDeclMode.Charset + xmlVersion = XmlVersion.XML10 + setIndent(2) + } + + @Provides + @SingleIn(AppScope::class) + fun providesProtoBuf(): ProtoBuf = ProtoBuf +} diff --git a/app/src/main/java/mihon/app/di/AppGraph.kt b/app/src/main/java/mihon/app/di/AppGraph.kt new file mode 100644 index 000000000..e31f57af1 --- /dev/null +++ b/app/src/main/java/mihon/app/di/AppGraph.kt @@ -0,0 +1,122 @@ +package mihon.app.di + +import android.content.Context +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.DependencyGraph +import dev.zacsweers.metro.Provides +import dev.zacsweers.metrox.viewmodel.MetroViewModelFactory +import dev.zacsweers.metrox.viewmodel.ViewModelGraph +import eu.kanade.domain.base.BasePreferences +import eu.kanade.domain.extension.interactor.TrustExtension +import eu.kanade.domain.source.service.SourcePreferences +import eu.kanade.domain.track.interactor.AddTracks +import eu.kanade.domain.track.service.DelayedTrackingUpdateJob +import eu.kanade.domain.track.service.TrackPreferences +import eu.kanade.domain.ui.UiPreferences +import eu.kanade.tachiyomi.App +import eu.kanade.tachiyomi.core.security.PrivacyPreferences +import eu.kanade.tachiyomi.core.security.SecurityPreferences +import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob +import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob +import eu.kanade.tachiyomi.data.cache.ChapterCache +import eu.kanade.tachiyomi.data.download.DownloadCache +import eu.kanade.tachiyomi.data.download.DownloadJob +import eu.kanade.tachiyomi.data.download.DownloadManager +import eu.kanade.tachiyomi.data.library.LibraryUpdateJob +import eu.kanade.tachiyomi.data.library.MetadataUpdateJob +import eu.kanade.tachiyomi.data.notification.NotificationReceiver +import eu.kanade.tachiyomi.data.track.TrackerManager +import eu.kanade.tachiyomi.data.updater.AppUpdateChecker +import eu.kanade.tachiyomi.extension.ExtensionManager +import eu.kanade.tachiyomi.extension.util.ExtensionInstallActivity +import eu.kanade.tachiyomi.network.NetworkHelper +import eu.kanade.tachiyomi.network.NetworkPreferences +import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegateImpl +import eu.kanade.tachiyomi.ui.main.MainActivity +import eu.kanade.tachiyomi.ui.reader.ReaderActivity +import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences +import eu.kanade.tachiyomi.ui.setting.track.BaseOAuthLoginActivity +import eu.kanade.tachiyomi.ui.webview.WebViewActivity +import eu.kanade.tachiyomi.util.CrashLogUtil +import kotlinx.serialization.json.Json +import mihon.core.metro.IsDebugBuild +import mihon.domain.extension.interactor.GetExtensionStoreCountAsFlow +import tachiyomi.domain.backup.service.BackupPreferences +import tachiyomi.domain.category.interactor.GetCategories +import tachiyomi.domain.category.interactor.ResetCategoryFlags +import tachiyomi.domain.download.service.DownloadPreferences +import tachiyomi.domain.library.service.LibraryPreferences +import tachiyomi.domain.manga.interactor.GetFavorites +import tachiyomi.domain.manga.interactor.ResetViewerFlags +import tachiyomi.domain.source.service.SourceManager +import tachiyomi.domain.storage.service.StoragePreferences +import tachiyomi.domain.track.interactor.InsertTrack + +@DependencyGraph( + scope = AppScope::class, + bindingContainers = [AppBindings::class], +) +interface AppGraph : ViewModelGraph { + fun inject(app: App) + fun inject(mainActivity: MainActivity) + fun inject(readerActivity: ReaderActivity) + fun inject(webViewActivity: WebViewActivity) + fun inject(baseOAuthLoginActivity: BaseOAuthLoginActivity) + fun inject(libraryUpdateJob: LibraryUpdateJob) + fun inject(metadataUpdateJob: MetadataUpdateJob) + fun inject(backupRestoreJob: BackupRestoreJob) + fun inject(backupCreateJob: BackupCreateJob) + fun inject(delayedTrackingUpdateJob: DelayedTrackingUpdateJob) + fun inject(downloadJob: DownloadJob) + fun inject(notificationReceiver: NotificationReceiver) + fun inject(notificationReceiver: SecureActivityDelegateImpl) + fun inject(extensionInstallActivity: ExtensionInstallActivity) + + val context: Context + + val viewModelFactory: MetroViewModelFactory + + val basePreferences: BasePreferences + val uiPreferences: UiPreferences + val readerPreferences: ReaderPreferences + val networkPreferences: NetworkPreferences + val libraryPreferences: LibraryPreferences + val sourcePreferences: SourcePreferences + val trackPreferences: TrackPreferences + val backupPreferences: BackupPreferences + val storagePreferences: StoragePreferences + val privacyPreferences: PrivacyPreferences + val securityPreferences: SecurityPreferences + val downloadPreferences: DownloadPreferences + + val crashLogUtil: CrashLogUtil + + val downloadManager: DownloadManager + + val updateChecker: AppUpdateChecker + + val trustExtension: TrustExtension + + val sourceManager: SourceManager + val trackerManager: TrackerManager + val extensionManager: ExtensionManager + val chapterCache: ChapterCache + val downloadCache: DownloadCache + + val json: Json + val networkHelper: NetworkHelper + + val getFavorites: GetFavorites + val getCategories: GetCategories + val resetViewerFlags: ResetViewerFlags + val resetCategoryFlags: ResetCategoryFlags + val addTracks: AddTracks + val insertTrack: InsertTrack + + val getExtensionStoreCountAsFlow: GetExtensionStoreCountAsFlow + + @DependencyGraph.Factory + fun interface Factory { + fun create(@Provides context: Context, @Provides @IsDebugBuild isDebugBuild: Boolean): AppGraph + } +} diff --git a/app/src/main/java/mihon/app/di/AppGraphUtils.kt b/app/src/main/java/mihon/app/di/AppGraphUtils.kt new file mode 100644 index 000000000..96ce58e1f --- /dev/null +++ b/app/src/main/java/mihon/app/di/AppGraphUtils.kt @@ -0,0 +1,6 @@ +package mihon.app.di + +import android.content.Context +import mihon.core.metro.metroGraph + +val Context.appGraph get() = metroGraph() diff --git a/app/src/main/java/mihon/app/di/MihonViewModelFactory.kt b/app/src/main/java/mihon/app/di/MihonViewModelFactory.kt new file mode 100644 index 000000000..579e2adb3 --- /dev/null +++ b/app/src/main/java/mihon/app/di/MihonViewModelFactory.kt @@ -0,0 +1,21 @@ +package mihon.app.di + +import androidx.lifecycle.ViewModel +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn +import dev.zacsweers.metrox.viewmodel.ManualViewModelAssistedFactory +import dev.zacsweers.metrox.viewmodel.MetroViewModelFactory +import dev.zacsweers.metrox.viewmodel.ViewModelAssistedFactory +import kotlin.reflect.KClass + +@Inject +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) +class MihonViewModelFactory( + override val viewModelProviders: Map, () -> ViewModel>, + override val assistedFactoryProviders: Map, () -> ViewModelAssistedFactory>, + override val manualAssistedFactoryProviders: + Map, () -> ManualViewModelAssistedFactory>, +) : MetroViewModelFactory() diff --git a/app/src/main/java/mihon/app/di/injekt/MetroInteropModule.kt b/app/src/main/java/mihon/app/di/injekt/MetroInteropModule.kt new file mode 100644 index 000000000..7758f8f6f --- /dev/null +++ b/app/src/main/java/mihon/app/di/injekt/MetroInteropModule.kt @@ -0,0 +1,49 @@ +package mihon.app.di.injekt + +import dev.zacsweers.metro.Inject +import eu.kanade.domain.track.service.TrackPreferences +import eu.kanade.tachiyomi.data.cache.CoverCache +import eu.kanade.tachiyomi.extension.ExtensionManager +import eu.kanade.tachiyomi.network.JavaScriptEngine +import eu.kanade.tachiyomi.network.NetworkHelper +import kotlinx.serialization.json.Json +import kotlinx.serialization.protobuf.ProtoBuf +import nl.adaptivity.xmlutil.serialization.XML +import tachiyomi.core.common.preference.PreferenceStore +import uy.kohesive.injekt.api.InjektModule +import uy.kohesive.injekt.api.InjektRegistrar +import uy.kohesive.injekt.api.addSingleton + +@Inject +class MetroInteropModule( + private val json: Json, + private val protoBuf: ProtoBuf, + private val xml: XML, + + private val networkHelper: NetworkHelper, + private val javaScriptEngine: JavaScriptEngine, + + private val preferenceStore: PreferenceStore, + private val trackPreferences: TrackPreferences, + + private val extensionManager: ExtensionManager, + + private val coverCache: CoverCache, +) : InjektModule { + + override fun InjektRegistrar.registerInjectables() { + addSingleton(json) + addSingleton(protoBuf) + addSingleton(xml) + + addSingleton(networkHelper) + addSingleton(javaScriptEngine) + + addSingleton(preferenceStore) + addSingleton(trackPreferences) + + addSingleton(extensionManager) + + addSingleton(coverCache) + } +} diff --git a/app/src/main/java/mihon/core/migration/MigrationContext.kt b/app/src/main/java/mihon/core/migration/MigrationContext.kt index f37785f05..39d165cf3 100644 --- a/app/src/main/java/mihon/core/migration/MigrationContext.kt +++ b/app/src/main/java/mihon/core/migration/MigrationContext.kt @@ -1,13 +1,6 @@ package mihon.core.migration -import uy.kohesive.injekt.Injekt - class MigrationContext( val dryrun: Boolean, val previousVersion: Int, -) { - - inline fun get(): T? { - return Injekt.getInstanceOrNull(T::class.java) - } -} +) diff --git a/app/src/main/java/mihon/core/migration/migrations/CategoryPreferencesCleanupMigration.kt b/app/src/main/java/mihon/core/migration/migrations/CategoryPreferencesCleanupMigration.kt index 0bcbd2e6c..f1aa735f9 100644 --- a/app/src/main/java/mihon/core/migration/migrations/CategoryPreferencesCleanupMigration.kt +++ b/app/src/main/java/mihon/core/migration/migrations/CategoryPreferencesCleanupMigration.kt @@ -1,5 +1,8 @@ package mihon.core.migration.migrations +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesIntoSet +import dev.zacsweers.metro.Inject import mihon.core.migration.Migration import mihon.core.migration.MigrationContext import tachiyomi.core.common.util.lang.withIOContext @@ -7,14 +10,16 @@ import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.download.service.DownloadPreferences import tachiyomi.domain.library.service.LibraryPreferences -class CategoryPreferencesCleanupMigration : Migration { +@Inject +@ContributesIntoSet(AppScope::class) +class CategoryPreferencesCleanupMigration( + private val libraryPreferences: LibraryPreferences, + private val downloadPreferences: DownloadPreferences, + private val getCategories: GetCategories, +) : Migration { override val version: Float = 10f override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext { - val libraryPreferences = migrationContext.get() ?: return@withIOContext false - val downloadPreferences = migrationContext.get() ?: return@withIOContext false - - val getCategories = migrationContext.get() ?: return@withIOContext false val allCategories = getCategories.await().map { it.id.toString() }.toSet() val defaultCategory = libraryPreferences.defaultCategory.get() diff --git a/app/src/main/java/mihon/core/migration/migrations/InstallationIdMigration.kt b/app/src/main/java/mihon/core/migration/migrations/InstallationIdMigration.kt index a19d5ff46..40485e67a 100644 --- a/app/src/main/java/mihon/core/migration/migrations/InstallationIdMigration.kt +++ b/app/src/main/java/mihon/core/migration/migrations/InstallationIdMigration.kt @@ -1,17 +1,24 @@ package mihon.core.migration.migrations +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesIntoSet +import dev.zacsweers.metro.Inject import eu.kanade.domain.base.BasePreferences import mihon.core.common.FeatureFlags import mihon.core.migration.Migration import mihon.core.migration.MigrationContext import kotlin.uuid.ExperimentalUuidApi -class InstallationIdMigration : Migration { +@Inject +@ContributesIntoSet(AppScope::class) +class InstallationIdMigration( + private val basePreferences: BasePreferences, +) : Migration { override val version: Float = Migration.ALWAYS @OptIn(ExperimentalUuidApi::class) override suspend fun invoke(migrationContext: MigrationContext): Boolean { - val installationId = migrationContext.get()?.installationId ?: return false + val installationId = basePreferences.installationId if (!installationId.isSet()) installationId.set(FeatureFlags.newInstallationId()) return true } diff --git a/app/src/main/java/mihon/core/migration/migrations/Migrations.kt b/app/src/main/java/mihon/core/migration/migrations/Migrations.kt deleted file mode 100644 index 523b8e846..000000000 --- a/app/src/main/java/mihon/core/migration/migrations/Migrations.kt +++ /dev/null @@ -1,13 +0,0 @@ -package mihon.core.migration.migrations - -import mihon.core.migration.Migration - -val migrations: List - get() = listOf( - SetupBackupCreateMigration(), - SetupLibraryUpdateMigration(), - TrustExtensionRepositoryMigration(), - CategoryPreferencesCleanupMigration(), - InstallationIdMigration(), - VerticalNavigatorMigration(), - ) diff --git a/app/src/main/java/mihon/core/migration/migrations/SetupBackupCreateMigration.kt b/app/src/main/java/mihon/core/migration/migrations/SetupBackupCreateMigration.kt index aabd5935f..3278da3ee 100644 --- a/app/src/main/java/mihon/core/migration/migrations/SetupBackupCreateMigration.kt +++ b/app/src/main/java/mihon/core/migration/migrations/SetupBackupCreateMigration.kt @@ -1,15 +1,21 @@ package mihon.core.migration.migrations -import android.app.Application +import android.content.Context +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesIntoSet +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob import mihon.core.migration.Migration import mihon.core.migration.MigrationContext -class SetupBackupCreateMigration : Migration { +@Inject +@ContributesIntoSet(AppScope::class) +class SetupBackupCreateMigration( + private val context: Context, +) : Migration { override val version: Float = Migration.ALWAYS override suspend fun invoke(migrationContext: MigrationContext): Boolean { - val context = migrationContext.get() ?: return false BackupCreateJob.setupTask(context) return true } diff --git a/app/src/main/java/mihon/core/migration/migrations/SetupLibraryUpdateMigration.kt b/app/src/main/java/mihon/core/migration/migrations/SetupLibraryUpdateMigration.kt index 08b84c9a0..039a78d99 100644 --- a/app/src/main/java/mihon/core/migration/migrations/SetupLibraryUpdateMigration.kt +++ b/app/src/main/java/mihon/core/migration/migrations/SetupLibraryUpdateMigration.kt @@ -1,15 +1,21 @@ package mihon.core.migration.migrations -import android.app.Application +import android.content.Context +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesIntoSet +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.data.library.LibraryUpdateJob import mihon.core.migration.Migration import mihon.core.migration.MigrationContext -class SetupLibraryUpdateMigration : Migration { +@Inject +@ContributesIntoSet(AppScope::class) +class SetupLibraryUpdateMigration( + private val context: Context, +) : Migration { override val version: Float = Migration.ALWAYS override suspend fun invoke(migrationContext: MigrationContext): Boolean { - val context = migrationContext.get() ?: return false LibraryUpdateJob.setupTask(context) return true } diff --git a/app/src/main/java/mihon/core/migration/migrations/TrustExtensionRepositoryMigration.kt b/app/src/main/java/mihon/core/migration/migrations/TrustExtensionRepositoryMigration.kt index e4335d595..882710cb9 100644 --- a/app/src/main/java/mihon/core/migration/migrations/TrustExtensionRepositoryMigration.kt +++ b/app/src/main/java/mihon/core/migration/migrations/TrustExtensionRepositoryMigration.kt @@ -1,5 +1,8 @@ package mihon.core.migration.migrations +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesIntoSet +import dev.zacsweers.metro.Inject import eu.kanade.domain.source.service.SourcePreferences import logcat.LogPriority import mihon.core.migration.Migration @@ -8,12 +11,15 @@ import mihon.domain.extension.repository.ExtensionStoreRepository import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.system.logcat -class TrustExtensionRepositoryMigration : Migration { +@Inject +@ContributesIntoSet(AppScope::class) +class TrustExtensionRepositoryMigration( + private val sourcePreferences: SourcePreferences, + private val repository: ExtensionStoreRepository, +) : Migration { override val version: Float = 7f override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext { - val sourcePreferences = migrationContext.get() ?: return@withIOContext false - val repository = migrationContext.get() ?: return@withIOContext false for ((index, source) in sourcePreferences.extensionRepos.get().withIndex()) { try { repository.insertFromPreference( diff --git a/app/src/main/java/mihon/core/migration/migrations/VerticalNavigatorMigration.kt b/app/src/main/java/mihon/core/migration/migrations/VerticalNavigatorMigration.kt index b85c48670..f97d00559 100644 --- a/app/src/main/java/mihon/core/migration/migrations/VerticalNavigatorMigration.kt +++ b/app/src/main/java/mihon/core/migration/migrations/VerticalNavigatorMigration.kt @@ -1,5 +1,8 @@ package mihon.core.migration.migrations +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesIntoSet +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode import mihon.core.migration.Migration @@ -7,13 +10,15 @@ import mihon.core.migration.MigrationContext import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.core.common.util.lang.withIOContext -class VerticalNavigatorMigration : Migration { +@Inject +@ContributesIntoSet(AppScope::class) +class VerticalNavigatorMigration( + private val preferenceStore: PreferenceStore, + private val readerPreferences: ReaderPreferences, +) : Migration { override val version: Float = 25f override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext { - val preferenceStore = migrationContext.get() ?: return@withIOContext false - val readerPreferences = migrationContext.get() ?: return@withIOContext false - if (migrationContext.previousVersion == 24) { val oldVerticalNavigator = preferenceStore.getBoolean("pref_webtoon_vertical_navigator", true) if (oldVerticalNavigator.get()) { diff --git a/app/src/main/java/mihon/domain/migration/usecases/MigrateMangaUseCase.kt b/app/src/main/java/mihon/domain/migration/usecases/MigrateMangaUseCase.kt index cf639e45d..fe7f71d96 100644 --- a/app/src/main/java/mihon/domain/migration/usecases/MigrateMangaUseCase.kt +++ b/app/src/main/java/mihon/domain/migration/usecases/MigrateMangaUseCase.kt @@ -1,5 +1,6 @@ package mihon.domain.migration.usecases +import dev.zacsweers.metro.Inject import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.model.hasCustomCover import eu.kanade.domain.source.service.SourcePreferences @@ -22,6 +23,7 @@ import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.domain.track.interactor.InsertTrack import kotlin.time.Clock +@Inject class MigrateMangaUseCase( private val sourcePreferences: SourcePreferences, private val trackerManager: TrackerManager, diff --git a/app/src/main/java/mihon/domain/source/interactor/UpdateMangaFromRemote.kt b/app/src/main/java/mihon/domain/source/interactor/UpdateMangaFromRemote.kt index d016eaa11..cb047d69f 100644 --- a/app/src/main/java/mihon/domain/source/interactor/UpdateMangaFromRemote.kt +++ b/app/src/main/java/mihon/domain/source/interactor/UpdateMangaFromRemote.kt @@ -1,5 +1,6 @@ package mihon.domain.source.interactor +import dev.zacsweers.metro.Inject import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource import eu.kanade.domain.chapter.model.toSChapter import eu.kanade.domain.manga.model.hasCustomCover @@ -22,6 +23,7 @@ import tachiyomi.domain.source.service.SourceManager import tachiyomi.source.local.isLocal import kotlin.time.Clock +@Inject class UpdateMangaFromRemote( private val sourceManager: SourceManager, private val chapterRepository: ChapterRepository, diff --git a/app/src/main/java/mihon/feature/migration/config/MigrationConfigScreen.kt b/app/src/main/java/mihon/feature/migration/config/MigrationConfigScreen.kt index abca9a40d..9d3562ff5 100644 --- a/app/src/main/java/mihon/feature/migration/config/MigrationConfigScreen.kt +++ b/app/src/main/java/mihon/feature/migration/config/MigrationConfigScreen.kt @@ -38,9 +38,14 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.util.fastForEachIndexed import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator 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.domain.source.service.SourcePreferences import eu.kanade.presentation.browse.components.SourceIcon import eu.kanade.presentation.components.AppBar @@ -68,8 +73,6 @@ import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.util.shouldExpandFAB -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get class MigrationConfigScreen(private val mangaIds: Collection) : Screen() { @@ -79,7 +82,7 @@ class MigrationConfigScreen(private val mangaIds: Collection) : Screen() { override fun Content() { val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel() + val viewModel = metroViewModel() val state by viewModel.state.collectAsState() var migrationSheetOpen by rememberSaveable { mutableStateOf(false) } @@ -308,9 +311,12 @@ class MigrationConfigScreen(private val mangaIds: Collection) : Screen() { } } + @Inject + @ViewModelKey + @ContributesIntoMap(AppScope::class, binding = binding()) class Model( - val sourcePreferences: SourcePreferences = Injekt.get(), - private val sourceManager: SourceManager = Injekt.get(), + val sourcePreferences: SourcePreferences, + private val sourceManager: SourceManager, ) : ViewModel() { val state: StateFlow diff --git a/app/src/main/java/mihon/feature/migration/dialog/MigrateMangaDialog.kt b/app/src/main/java/mihon/feature/migration/dialog/MigrateMangaDialog.kt index f943f3ebd..699e05741 100644 --- a/app/src/main/java/mihon/feature/migration/dialog/MigrateMangaDialog.kt +++ b/app/src/main/java/mihon/feature/migration/dialog/MigrateMangaDialog.kt @@ -19,8 +19,13 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.util.fastForEach import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.core.screen.Screen +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.domain.manga.model.hasCustomCover import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.tachiyomi.data.cache.CoverCache @@ -39,8 +44,6 @@ import tachiyomi.presentation.core.components.LabeledCheckbox import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.screens.LoadingScreen -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get @Composable internal fun Screen.MigrateMangaDialog( @@ -52,7 +55,7 @@ internal fun Screen.MigrateMangaDialog( ) { val scope = rememberCoroutineScope() - val viewModel = viewModel() + val viewModel = metroViewModel() LaunchedEffect(current, target) { viewModel.init(current, target) } @@ -125,11 +128,14 @@ internal fun Screen.MigrateMangaDialog( ) } +@Inject +@ViewModelKey +@ContributesIntoMap(AppScope::class, binding = binding()) class MigrateDialogViewModel( - private val sourcePreference: SourcePreferences = Injekt.get(), - private val coverCache: CoverCache = Injekt.get(), - private val downloadManager: DownloadManager = Injekt.get(), - private val migrateManga: MigrateMangaUseCase = Injekt.get(), + private val sourcePreference: SourcePreferences, + private val coverCache: CoverCache, + private val downloadManager: DownloadManager, + private val migrateManga: MigrateMangaUseCase, ) : ViewModel() { val state: StateFlow diff --git a/app/src/main/java/mihon/feature/migration/list/MigrationListScreen.kt b/app/src/main/java/mihon/feature/migration/list/MigrationListScreen.kt index 3aca6b8b3..82d5cc8fb 100644 --- a/app/src/main/java/mihon/feature/migration/list/MigrationListScreen.kt +++ b/app/src/main/java/mihon/feature/migration/list/MigrationListScreen.kt @@ -7,10 +7,9 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.platform.LocalContext -import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metrox.viewmodel.assistedMetroViewModel import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateSearchScreen import eu.kanade.tachiyomi.ui.manga.MangaScreen @@ -31,13 +30,10 @@ class MigrationListScreen(private val mangaIds: Collection, private val ex @Composable override fun Content() { val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel( - factory = MigrationListViewModel.Factory, - extras = CreationExtras { - set(MigrationListViewModel.MANGA_IDS_KEY, mangaIds) - set(MigrationListViewModel.EXTRA_SEARCH_QUERY_KEY, extraSearchQuery) - }, - ) + val viewModel = + assistedMetroViewModel { + create(mangaIds = mangaIds, extraSearchQuery = extraSearchQuery) + } val state by viewModel.state.collectAsState() val context = LocalContext.current diff --git a/app/src/main/java/mihon/feature/migration/list/MigrationListViewModel.kt b/app/src/main/java/mihon/feature/migration/list/MigrationListViewModel.kt index 25db7e733..3c8b0a99c 100644 --- a/app/src/main/java/mihon/feature/migration/list/MigrationListViewModel.kt +++ b/app/src/main/java/mihon/feature/migration/list/MigrationListViewModel.kt @@ -3,9 +3,13 @@ package mihon.feature.migration.list import androidx.annotation.FloatRange import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.initializer -import androidx.lifecycle.viewmodel.viewModelFactory +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 eu.kanade.domain.source.service.SourcePreferences import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.getNameForMangaInfo @@ -38,37 +42,28 @@ import tachiyomi.domain.manga.interactor.GetManga import tachiyomi.domain.manga.interactor.NetworkToLocalManga import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.service.SourceManager -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +@AssistedInject class MigrationListViewModel( - mangaIds: Collection, - extraSearchQuery: String?, - private val preferences: SourcePreferences = Injekt.get(), - private val sourceManager: SourceManager = Injekt.get(), - private val getManga: GetManga = Injekt.get(), - private val networkToLocalManga: NetworkToLocalManga = Injekt.get(), - private val getChaptersByMangaId: GetChaptersByMangaId = Injekt.get(), - private val migrateManga: MigrateMangaUseCase = Injekt.get(), - private val updateMangaFromRemote: UpdateMangaFromRemote = Injekt.get(), + @Assisted mangaIds: Collection, + @Assisted extraSearchQuery: String?, + private val preferences: SourcePreferences, + private val sourceManager: SourceManager, + private val getManga: GetManga, + private val networkToLocalManga: NetworkToLocalManga, + private val getChaptersByMangaId: GetChaptersByMangaId, + private val migrateManga: MigrateMangaUseCase, + private val updateMangaFromRemote: UpdateMangaFromRemote, ) : ViewModel() { val state: StateFlow field = MutableStateFlow(State()) - companion object { - val MANGA_IDS_KEY = CreationExtras.Key>() - - val EXTRA_SEARCH_QUERY_KEY = CreationExtras.Key() - - val Factory = viewModelFactory { - initializer { - MigrationListViewModel( - mangaIds = get(MANGA_IDS_KEY)!!, - extraSearchQuery = get(EXTRA_SEARCH_QUERY_KEY), - ) - } - } + @AssistedFactory + @ManualViewModelAssistedFactoryKey + @ContributesIntoMap(AppScope::class) + interface Factory : ManualViewModelAssistedFactory { + fun create(mangaIds: Collection, extraSearchQuery: String?): MigrationListViewModel } private val smartSearchEngine = SmartSourceSearchEngine(extraSearchQuery) diff --git a/app/src/main/java/mihon/feature/upcoming/UpcomingScreen.kt b/app/src/main/java/mihon/feature/upcoming/UpcomingScreen.kt index 2c105b2f8..b346f13a8 100644 --- a/app/src/main/java/mihon/feature/upcoming/UpcomingScreen.kt +++ b/app/src/main/java/mihon/feature/upcoming/UpcomingScreen.kt @@ -21,6 +21,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import dev.zacsweers.metrox.viewmodel.metroViewModel import eu.kanade.presentation.category.visualName import eu.kanade.presentation.components.TabbedDialog import eu.kanade.presentation.components.TabbedDialogPaddings @@ -41,7 +42,7 @@ class UpcomingScreen : Screen() { override fun Content() { val navigator = LocalNavigator.currentOrThrow - val viewModel = viewModel() + val viewModel = metroViewModel() val state by viewModel.state.collectAsStateWithLifecycle() when (state.dialog) { diff --git a/app/src/main/java/mihon/feature/upcoming/UpcomingViewModel.kt b/app/src/main/java/mihon/feature/upcoming/UpcomingViewModel.kt index ca0d53f3a..703d83f3c 100644 --- a/app/src/main/java/mihon/feature/upcoming/UpcomingViewModel.kt +++ b/app/src/main/java/mihon/feature/upcoming/UpcomingViewModel.kt @@ -5,6 +5,11 @@ import androidx.compose.ui.util.fastMap import androidx.compose.ui.util.fastMapIndexedNotNull import androidx.lifecycle.ViewModel 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.core.util.insertSeparatorsReversed import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow @@ -30,15 +35,16 @@ import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.category.model.Category import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.upcoming.service.UpcomingPreferences -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.time.Clock import kotlin.time.Duration.Companion.seconds +@Inject +@ViewModelKey +@ContributesIntoMap(AppScope::class, binding = binding()) class UpcomingViewModel( - private val getUpcomingManga: GetUpcomingManga = Injekt.get(), - val getCategories: GetCategories = Injekt.get(), - val upcomingPreferences: UpcomingPreferences = Injekt.get(), + private val getUpcomingManga: GetUpcomingManga, + val getCategories: GetCategories, + val upcomingPreferences: UpcomingPreferences, ) : ViewModel() { val excludedCategories = upcomingPreferences.filterExcludedCategories diff --git a/build.gradle.kts b/build.gradle.kts index d8977a34d..a4993e2ad 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,6 +15,7 @@ plugins { alias(libs.plugins.google.services) apply false alias(libs.plugins.kotlin.compose.compiler) apply false alias(libs.plugins.kotlin.serialization) apply false + alias(libs.plugins.metro) apply false alias(libs.plugins.moko.resources) apply false alias(libs.plugins.sqldelight) apply false diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index e5b8eb7e0..b695b88e2 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -2,6 +2,7 @@ plugins { alias(mihonx.plugins.android.library) alias(mihonx.plugins.spotless) + alias(libs.plugins.metro) alias(libs.plugins.kotlin.serialization) } @@ -19,6 +20,7 @@ kotlin { } dependencies { + implementation(projects.core.metro) implementation(projects.i18n) api(libs.logcat) @@ -53,4 +55,6 @@ dependencies { testImplementation(libs.bundles.test) testRuntimeOnly(libs.junit.platform.launcher) + + implementation(libs.metro.runtime) } diff --git a/core/common/src/main/kotlin/eu/kanade/tachiyomi/core/security/PrivacyPreferences.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/core/security/PrivacyPreferences.kt index cdb0098fb..a6bafac1a 100644 --- a/core/common/src/main/kotlin/eu/kanade/tachiyomi/core/security/PrivacyPreferences.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/core/security/PrivacyPreferences.kt @@ -1,8 +1,13 @@ package eu.kanade.tachiyomi.core.security +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.PreferenceStore +@Inject +@SingleIn(AppScope::class) class PrivacyPreferences( preferenceStore: PreferenceStore, ) { diff --git a/core/common/src/main/kotlin/eu/kanade/tachiyomi/core/security/SecurityPreferences.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/core/security/SecurityPreferences.kt index a2c2d2d76..07fe262ce 100644 --- a/core/common/src/main/kotlin/eu/kanade/tachiyomi/core/security/SecurityPreferences.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/core/security/SecurityPreferences.kt @@ -1,11 +1,16 @@ package eu.kanade.tachiyomi.core.security 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.PreferenceStore import tachiyomi.core.common.preference.getEnum import tachiyomi.i18n.MR +@Inject +@SingleIn(AppScope::class) class SecurityPreferences( preferenceStore: PreferenceStore, ) { diff --git a/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/JavaScriptEngine.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/JavaScriptEngine.kt index 5c7894606..db7d37446 100644 --- a/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/JavaScriptEngine.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/JavaScriptEngine.kt @@ -2,12 +2,17 @@ package eu.kanade.tachiyomi.network import android.content.Context import app.cash.quickjs.QuickJs +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import tachiyomi.core.common.util.lang.withIOContext /** * Util for evaluating JavaScript in sources. */ @Suppress("UNUSED", "UNCHECKED_CAST") +@Inject +@SingleIn(AppScope::class) class JavaScriptEngine(context: Context) { /** diff --git a/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkHelper.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkHelper.kt index 9580db6c9..21357b1cc 100644 --- a/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkHelper.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkHelper.kt @@ -1,6 +1,9 @@ package eu.kanade.tachiyomi.network import android.content.Context +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import eu.kanade.tachiyomi.network.interceptor.CloudflareInterceptor import eu.kanade.tachiyomi.network.interceptor.UncaughtExceptionInterceptor import eu.kanade.tachiyomi.network.interceptor.UserAgentInterceptor @@ -11,6 +14,8 @@ import java.io.File import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds +@Inject +@SingleIn(AppScope::class) class NetworkHelper( private val context: Context, private val preferences: NetworkPreferences, diff --git a/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkPreferences.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkPreferences.kt index 5df70e46d..43a195a99 100644 --- a/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkPreferences.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkPreferences.kt @@ -1,14 +1,23 @@ package eu.kanade.tachiyomi.network +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn +import mihon.core.metro.IsDebugBuild import tachiyomi.core.common.preference.Preference import tachiyomi.core.common.preference.PreferenceStore +@Inject +@SingleIn(AppScope::class) class NetworkPreferences( preferenceStore: PreferenceStore, - verboseLoggingDefault: Boolean = false, + @IsDebugBuild isDebugBuild: Boolean, ) { - val verboseLogging: Preference = preferenceStore.getBoolean("verbose_logging", verboseLoggingDefault) + val verboseLogging: Preference = preferenceStore.getBoolean( + "verbose_logging", + isDebugBuild, + ) val dohProvider: Preference = preferenceStore.getInt("doh_provider", -1) diff --git a/core/common/src/main/kotlin/tachiyomi/core/common/preference/AndroidPreferenceStore.kt b/core/common/src/main/kotlin/tachiyomi/core/common/preference/AndroidPreferenceStore.kt index cc3d2d067..c108b6006 100644 --- a/core/common/src/main/kotlin/tachiyomi/core/common/preference/AndroidPreferenceStore.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/preference/AndroidPreferenceStore.kt @@ -3,6 +3,10 @@ package tachiyomi.core.common.preference import android.content.Context import android.content.SharedPreferences import androidx.preference.PreferenceManager +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow import tachiyomi.core.common.preference.AndroidPreference.BooleanPrimitive @@ -15,10 +19,11 @@ import tachiyomi.core.common.preference.AndroidPreference.ObjectSetAsStringSet import tachiyomi.core.common.preference.AndroidPreference.StringPrimitive import tachiyomi.core.common.preference.AndroidPreference.StringSetPrimitive -class AndroidPreferenceStore( - context: Context, - private val sharedPreferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context), -) : PreferenceStore { +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) +class AndroidPreferenceStore(private val sharedPreferences: SharedPreferences) : PreferenceStore { + + @Inject constructor(context: Context) : this(PreferenceManager.getDefaultSharedPreferences(context)) private val keyFlow = sharedPreferences.keyFlow diff --git a/core/common/src/main/kotlin/tachiyomi/core/common/storage/AndroidStorageFolderProvider.kt b/core/common/src/main/kotlin/tachiyomi/core/common/storage/AndroidStorageFolderProvider.kt index 33335f345..a15fcd502 100644 --- a/core/common/src/main/kotlin/tachiyomi/core/common/storage/AndroidStorageFolderProvider.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/storage/AndroidStorageFolderProvider.kt @@ -3,10 +3,17 @@ package tachiyomi.core.common.storage import android.content.Context import android.os.Environment import androidx.core.net.toUri +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import java.io.File +@Inject +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) class AndroidStorageFolderProvider( private val context: Context, ) : FolderProvider { diff --git a/core/metro/build.gradle.kts b/core/metro/build.gradle.kts new file mode 100644 index 000000000..99ba240d6 --- /dev/null +++ b/core/metro/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + alias(mihonx.plugins.android.library) + alias(mihonx.plugins.spotless) + + alias(libs.plugins.metro) +} + +android { + namespace = "mihon.core.metro" +} + +dependencies { + implementation(libs.metro.runtime) +} diff --git a/core/metro/src/main/AndroidManifest.xml b/core/metro/src/main/AndroidManifest.xml new file mode 100644 index 000000000..8072ee00d --- /dev/null +++ b/core/metro/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/core/metro/src/main/kotlin/mihon/core/metro/GraphProvider.kt b/core/metro/src/main/kotlin/mihon/core/metro/GraphProvider.kt new file mode 100644 index 000000000..0f584f087 --- /dev/null +++ b/core/metro/src/main/kotlin/mihon/core/metro/GraphProvider.kt @@ -0,0 +1,5 @@ +package mihon.core.metro + +interface GraphProvider { + val graph: T +} diff --git a/core/metro/src/main/kotlin/mihon/core/metro/IsDebugBuild.kt b/core/metro/src/main/kotlin/mihon/core/metro/IsDebugBuild.kt new file mode 100644 index 000000000..4516846a2 --- /dev/null +++ b/core/metro/src/main/kotlin/mihon/core/metro/IsDebugBuild.kt @@ -0,0 +1,8 @@ +package mihon.core.metro + +import dev.zacsweers.metro.Qualifier + +@Qualifier +@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER) +@Retention(AnnotationRetention.RUNTIME) +annotation class IsDebugBuild diff --git a/core/metro/src/main/kotlin/mihon/core/metro/Utils.kt b/core/metro/src/main/kotlin/mihon/core/metro/Utils.kt new file mode 100644 index 000000000..2701cc6c4 --- /dev/null +++ b/core/metro/src/main/kotlin/mihon/core/metro/Utils.kt @@ -0,0 +1,6 @@ +package mihon.core.metro + +import android.content.Context + +@Suppress("UNCHECKED_CAST") +fun Context.metroGraph(): T = (applicationContext as GraphProvider).graph diff --git a/data/build.gradle.kts b/data/build.gradle.kts index 566345ad0..632bfbf73 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -4,6 +4,7 @@ plugins { alias(libs.plugins.kotlin.serialization) alias(libs.plugins.sqldelight) + alias(libs.plugins.metro) } android { @@ -32,6 +33,7 @@ dependencies { implementation(projects.domain) implementation(projects.core.common) + implementation(libs.metro.runtime) implementation(libs.kotlinx.serialization.json) implementation(libs.kotlinx.serialization.jsonOkio) implementation(libs.kotlinx.serialization.protobuf) diff --git a/data/src/main/java/mihon/data/extension/repository/ExtensionStoreRepositoryImpl.kt b/data/src/main/java/mihon/data/extension/repository/ExtensionStoreRepositoryImpl.kt index fbd431743..25e50bbd5 100644 --- a/data/src/main/java/mihon/data/extension/repository/ExtensionStoreRepositoryImpl.kt +++ b/data/src/main/java/mihon/data/extension/repository/ExtensionStoreRepositoryImpl.kt @@ -1,6 +1,10 @@ package mihon.data.extension.repository import app.cash.sqldelight.async.coroutines.awaitAsList +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import eu.kanade.tachiyomi.extension.model.Extension import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll @@ -15,6 +19,9 @@ import tachiyomi.data.Database import tachiyomi.data.subscribeToList import tachiyomi.data.subscribeToOne +@Inject +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) class ExtensionStoreRepositoryImpl( private val service: ExtensionStoreService, private val database: Database, diff --git a/data/src/main/java/mihon/data/extension/service/ExtensionStoreService.kt b/data/src/main/java/mihon/data/extension/service/ExtensionStoreService.kt index 155764518..f8ff5443e 100644 --- a/data/src/main/java/mihon/data/extension/service/ExtensionStoreService.kt +++ b/data/src/main/java/mihon/data/extension/service/ExtensionStoreService.kt @@ -1,5 +1,8 @@ package mihon.data.extension.service +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.NetworkHelper @@ -20,6 +23,8 @@ import okio.gzip import tachiyomi.core.common.util.system.logcat import kotlin.coroutines.cancellation.CancellationException +@Inject +@SingleIn(AppScope::class) class ExtensionStoreService( private val network: NetworkHelper, private val json: Json, diff --git a/data/src/main/java/tachiyomi/data/category/CategoryRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/category/CategoryRepositoryImpl.kt index a3fcddfd8..90f2f75f0 100644 --- a/data/src/main/java/tachiyomi/data/category/CategoryRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/category/CategoryRepositoryImpl.kt @@ -2,12 +2,19 @@ package tachiyomi.data.category import app.cash.sqldelight.async.coroutines.awaitAsList import app.cash.sqldelight.async.coroutines.awaitAsOneOrNull +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import kotlinx.coroutines.flow.Flow import tachiyomi.data.Database import tachiyomi.data.subscribeToList import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.repository.CategoryRepository +@Inject +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) class CategoryRepositoryImpl( private val database: Database, ) : CategoryRepository { diff --git a/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt index 9f3397a67..9ee0baec8 100644 --- a/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt @@ -3,6 +3,10 @@ package tachiyomi.data.chapter import app.cash.sqldelight.async.coroutines.awaitAsList import app.cash.sqldelight.async.coroutines.awaitAsOne import app.cash.sqldelight.async.coroutines.awaitAsOneOrNull +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import kotlinx.coroutines.flow.Flow import kotlinx.serialization.json.JsonObject import logcat.LogPriority @@ -15,6 +19,9 @@ import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.ChapterUpdate import tachiyomi.domain.chapter.repository.ChapterRepository +@Inject +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) class ChapterRepositoryImpl( private val database: Database, ) : ChapterRepository { diff --git a/data/src/main/java/tachiyomi/data/history/HistoryRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/history/HistoryRepositoryImpl.kt index 1dda3da41..fa2471884 100644 --- a/data/src/main/java/tachiyomi/data/history/HistoryRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/history/HistoryRepositoryImpl.kt @@ -3,6 +3,10 @@ package tachiyomi.data.history import app.cash.sqldelight.async.coroutines.awaitAsList import app.cash.sqldelight.async.coroutines.awaitAsOne import app.cash.sqldelight.async.coroutines.awaitAsOneOrNull +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import kotlinx.coroutines.flow.Flow import logcat.LogPriority import tachiyomi.core.common.util.system.logcat @@ -13,6 +17,9 @@ import tachiyomi.domain.history.model.HistoryUpdate import tachiyomi.domain.history.model.HistoryWithRelations import tachiyomi.domain.history.repository.HistoryRepository +@Inject +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) class HistoryRepositoryImpl( private val database: Database, ) : HistoryRepository { diff --git a/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt index 833a26763..f39bbf83c 100644 --- a/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt @@ -3,6 +3,10 @@ package tachiyomi.data.manga import app.cash.sqldelight.async.coroutines.awaitAsList import app.cash.sqldelight.async.coroutines.awaitAsOne import app.cash.sqldelight.async.coroutines.awaitAsOneOrNull +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import kotlinx.coroutines.flow.Flow import kotlinx.datetime.TimeZone import kotlinx.datetime.atStartOfDayIn @@ -23,6 +27,9 @@ import tachiyomi.domain.manga.model.MangaWithChapterCount import tachiyomi.domain.manga.repository.MangaRepository import kotlin.time.Clock +@Inject +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) class MangaRepositoryImpl( private val database: Database, ) : MangaRepository { diff --git a/data/src/main/java/tachiyomi/data/release/ReleaseServiceImpl.kt b/data/src/main/java/tachiyomi/data/release/ReleaseServiceImpl.kt index 247dc2244..225de5d10 100644 --- a/data/src/main/java/tachiyomi/data/release/ReleaseServiceImpl.kt +++ b/data/src/main/java/tachiyomi/data/release/ReleaseServiceImpl.kt @@ -1,6 +1,10 @@ package tachiyomi.data.release import android.os.Build +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.awaitSuccess @@ -10,6 +14,9 @@ import tachiyomi.domain.release.interactor.GetApplicationRelease import tachiyomi.domain.release.model.Release import tachiyomi.domain.release.service.ReleaseService +@Inject +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) class ReleaseServiceImpl( private val networkService: NetworkHelper, private val json: Json, diff --git a/data/src/main/java/tachiyomi/data/source/SourcePagingSource.kt b/data/src/main/java/tachiyomi/data/source/SourcePagingSource.kt index 425706741..bc9ea3fd4 100644 --- a/data/src/main/java/tachiyomi/data/source/SourcePagingSource.kt +++ b/data/src/main/java/tachiyomi/data/source/SourcePagingSource.kt @@ -9,26 +9,31 @@ import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.domain.manga.interactor.NetworkToLocalManga import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.repository.SourcePagingSource -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get class SourceSearchPagingSource( source: Source, private val query: String, private val filters: FilterList, -) : BaseSourcePagingSource(source) { + networkToLocalManga: NetworkToLocalManga, +) : BaseSourcePagingSource(source, networkToLocalManga) { override suspend fun requestNextPage(currentPage: Int): MangasPage { return source.getSearchManga(currentPage, query, filters) } } -class SourcePopularPagingSource(source: Source) : BaseSourcePagingSource(source) { +class SourcePopularPagingSource( + source: Source, + networkToLocalManga: NetworkToLocalManga, +) : BaseSourcePagingSource(source, networkToLocalManga) { override suspend fun requestNextPage(currentPage: Int): MangasPage { return source.getPopularManga(currentPage) } } -class SourceLatestPagingSource(source: Source) : BaseSourcePagingSource(source) { +class SourceLatestPagingSource( + source: Source, + networkToLocalManga: NetworkToLocalManga, +) : BaseSourcePagingSource(source, networkToLocalManga) { override suspend fun requestNextPage(currentPage: Int): MangasPage { return source.getLatestUpdates(currentPage) } @@ -36,7 +41,7 @@ class SourceLatestPagingSource(source: Source) : BaseSourcePagingSource(source) abstract class BaseSourcePagingSource( protected val source: Source, - private val networkToLocalManga: NetworkToLocalManga = Injekt.get(), + private val networkToLocalManga: NetworkToLocalManga, ) : SourcePagingSource() { private val seenManga = hashSetOf() diff --git a/data/src/main/java/tachiyomi/data/source/SourceRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/source/SourceRepositoryImpl.kt index 7decf8f7c..f6c88b2f0 100644 --- a/data/src/main/java/tachiyomi/data/source/SourceRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/source/SourceRepositoryImpl.kt @@ -1,5 +1,9 @@ package tachiyomi.data.source +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.online.HttpSource @@ -8,6 +12,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import tachiyomi.data.Database import tachiyomi.data.subscribeToList +import tachiyomi.domain.manga.interactor.NetworkToLocalManga import tachiyomi.domain.source.model.SourceWithCount import tachiyomi.domain.source.model.StubSource import tachiyomi.domain.source.repository.SourcePagingSource @@ -15,9 +20,13 @@ import tachiyomi.domain.source.repository.SourceRepository import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.model.Source as DomainSource +@Inject +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) class SourceRepositoryImpl( private val sourceManager: SourceManager, private val database: Database, + private val networkToLocalManga: NetworkToLocalManga, ) : SourceRepository { override fun getSources(): Flow> { @@ -76,15 +85,15 @@ class SourceRepositoryImpl( query: String, filterList: FilterList, ): SourcePagingSource { - return SourceSearchPagingSource(sourceManager.getOrStub(sourceId), query, filterList) + return SourceSearchPagingSource(sourceManager.getOrStub(sourceId), query, filterList, networkToLocalManga) } override fun getPopular(sourceId: Long): SourcePagingSource { - return SourcePopularPagingSource(sourceManager.getOrStub(sourceId)) + return SourcePopularPagingSource(sourceManager.getOrStub(sourceId), networkToLocalManga) } override fun getLatest(sourceId: Long): SourcePagingSource { - return SourceLatestPagingSource(sourceManager.getOrStub(sourceId)) + return SourceLatestPagingSource(sourceManager.getOrStub(sourceId), networkToLocalManga) } private fun mapSourceToDomainSource(source: Source): DomainSource = DomainSource( diff --git a/data/src/main/java/tachiyomi/data/source/StubSourceRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/source/StubSourceRepositoryImpl.kt index 4801bfcdd..8aee2e36f 100644 --- a/data/src/main/java/tachiyomi/data/source/StubSourceRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/source/StubSourceRepositoryImpl.kt @@ -1,12 +1,19 @@ package tachiyomi.data.source import app.cash.sqldelight.async.coroutines.awaitAsOneOrNull +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import kotlinx.coroutines.flow.Flow import tachiyomi.data.Database import tachiyomi.data.subscribeToList import tachiyomi.domain.source.model.StubSource import tachiyomi.domain.source.repository.StubSourceRepository +@Inject +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) class StubSourceRepositoryImpl( private val database: Database, ) : StubSourceRepository { diff --git a/data/src/main/java/tachiyomi/data/track/TrackRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/track/TrackRepositoryImpl.kt index bc4ef79b6..befd96324 100644 --- a/data/src/main/java/tachiyomi/data/track/TrackRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/track/TrackRepositoryImpl.kt @@ -2,12 +2,19 @@ package tachiyomi.data.track import app.cash.sqldelight.async.coroutines.awaitAsList import app.cash.sqldelight.async.coroutines.awaitAsOneOrNull +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import kotlinx.coroutines.flow.Flow import tachiyomi.data.Database import tachiyomi.data.subscribeToList import tachiyomi.domain.track.model.Track import tachiyomi.domain.track.repository.TrackRepository +@Inject +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) class TrackRepositoryImpl( private val database: Database, ) : TrackRepository { diff --git a/data/src/main/java/tachiyomi/data/updates/UpdatesRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/updates/UpdatesRepositoryImpl.kt index 7c1102233..651143628 100644 --- a/data/src/main/java/tachiyomi/data/updates/UpdatesRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/updates/UpdatesRepositoryImpl.kt @@ -1,6 +1,10 @@ package tachiyomi.data.updates import app.cash.sqldelight.async.coroutines.awaitAsList +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import kotlinx.coroutines.flow.Flow import tachiyomi.core.common.util.lang.toLong import tachiyomi.data.Database @@ -9,6 +13,9 @@ import tachiyomi.domain.manga.model.MangaCover import tachiyomi.domain.updates.model.UpdatesWithRelations import tachiyomi.domain.updates.repository.UpdatesRepository +@Inject +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) class UpdatesRepositoryImpl( private val database: Database, ) : UpdatesRepository { diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts index cd42533e4..6325e8144 100644 --- a/domain/build.gradle.kts +++ b/domain/build.gradle.kts @@ -3,6 +3,7 @@ plugins { alias(mihonx.plugins.spotless) alias(libs.plugins.kotlin.serialization) + alias(libs.plugins.metro) } android { @@ -19,6 +20,7 @@ dependencies { implementation(projects.sourceApi) implementation(projects.core.common) + implementation(libs.metro.runtime) implementation(libs.bundles.kotlinx.coroutines) implementation(libs.bundles.serialization) diff --git a/domain/src/main/java/mihon/domain/chapter/interactor/FilterChaptersForDownload.kt b/domain/src/main/java/mihon/domain/chapter/interactor/FilterChaptersForDownload.kt index 0250312f0..fce6aa923 100644 --- a/domain/src/main/java/mihon/domain/chapter/interactor/FilterChaptersForDownload.kt +++ b/domain/src/main/java/mihon/domain/chapter/interactor/FilterChaptersForDownload.kt @@ -1,5 +1,6 @@ package mihon.domain.chapter.interactor +import dev.zacsweers.metro.Inject import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId import tachiyomi.domain.chapter.model.Chapter @@ -13,6 +14,7 @@ import tachiyomi.domain.manga.model.Manga * @property downloadPreferences User preferences related to chapter downloads. * @property getCategories Interactor for retrieving categories associated with a manga. */ +@Inject class FilterChaptersForDownload( private val getChaptersByMangaId: GetChaptersByMangaId, private val downloadPreferences: DownloadPreferences, diff --git a/domain/src/main/java/mihon/domain/extension/interactor/AddExtensionStore.kt b/domain/src/main/java/mihon/domain/extension/interactor/AddExtensionStore.kt index 7c10703e5..4429107b5 100644 --- a/domain/src/main/java/mihon/domain/extension/interactor/AddExtensionStore.kt +++ b/domain/src/main/java/mihon/domain/extension/interactor/AddExtensionStore.kt @@ -1,7 +1,9 @@ package mihon.domain.extension.interactor +import dev.zacsweers.metro.Inject import mihon.domain.extension.repository.ExtensionStoreRepository +@Inject class AddExtensionStore( private val repository: ExtensionStoreRepository, ) { diff --git a/domain/src/main/java/mihon/domain/extension/interactor/GetExtensionStoreCountAsFlow.kt b/domain/src/main/java/mihon/domain/extension/interactor/GetExtensionStoreCountAsFlow.kt index 2871e7f67..eebf08bd6 100644 --- a/domain/src/main/java/mihon/domain/extension/interactor/GetExtensionStoreCountAsFlow.kt +++ b/domain/src/main/java/mihon/domain/extension/interactor/GetExtensionStoreCountAsFlow.kt @@ -1,7 +1,9 @@ package mihon.domain.extension.interactor +import dev.zacsweers.metro.Inject import mihon.domain.extension.repository.ExtensionStoreRepository +@Inject class GetExtensionStoreCountAsFlow( private val repository: ExtensionStoreRepository, ) { diff --git a/domain/src/main/java/mihon/domain/extension/interactor/GetExtensionStores.kt b/domain/src/main/java/mihon/domain/extension/interactor/GetExtensionStores.kt index e463af8d6..24a0b6c20 100644 --- a/domain/src/main/java/mihon/domain/extension/interactor/GetExtensionStores.kt +++ b/domain/src/main/java/mihon/domain/extension/interactor/GetExtensionStores.kt @@ -1,9 +1,11 @@ package mihon.domain.extension.interactor +import dev.zacsweers.metro.Inject import kotlinx.coroutines.flow.Flow import mihon.domain.extension.model.ExtensionStore import mihon.domain.extension.repository.ExtensionStoreRepository +@Inject class GetExtensionStores( private val repository: ExtensionStoreRepository, ) { diff --git a/domain/src/main/java/mihon/domain/extension/interactor/RemoveExtensionStore.kt b/domain/src/main/java/mihon/domain/extension/interactor/RemoveExtensionStore.kt index 42a8c07e2..5bafda22a 100644 --- a/domain/src/main/java/mihon/domain/extension/interactor/RemoveExtensionStore.kt +++ b/domain/src/main/java/mihon/domain/extension/interactor/RemoveExtensionStore.kt @@ -1,7 +1,9 @@ package mihon.domain.extension.interactor +import dev.zacsweers.metro.Inject import mihon.domain.extension.repository.ExtensionStoreRepository +@Inject class RemoveExtensionStore( private val repository: ExtensionStoreRepository, ) { diff --git a/domain/src/main/java/mihon/domain/extension/interactor/UpdateExtensionStores.kt b/domain/src/main/java/mihon/domain/extension/interactor/UpdateExtensionStores.kt index 7bbd2812e..b0ee9915a 100644 --- a/domain/src/main/java/mihon/domain/extension/interactor/UpdateExtensionStores.kt +++ b/domain/src/main/java/mihon/domain/extension/interactor/UpdateExtensionStores.kt @@ -1,7 +1,9 @@ package mihon.domain.extension.interactor +import dev.zacsweers.metro.Inject import mihon.domain.extension.repository.ExtensionStoreRepository +@Inject class UpdateExtensionStores( private val repository: ExtensionStoreRepository, ) { diff --git a/domain/src/main/java/mihon/domain/upcoming/interactor/GetUpcomingManga.kt b/domain/src/main/java/mihon/domain/upcoming/interactor/GetUpcomingManga.kt index cf63415a1..b183c44cf 100644 --- a/domain/src/main/java/mihon/domain/upcoming/interactor/GetUpcomingManga.kt +++ b/domain/src/main/java/mihon/domain/upcoming/interactor/GetUpcomingManga.kt @@ -1,10 +1,12 @@ package mihon.domain.upcoming.interactor +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.source.model.SManga import kotlinx.coroutines.flow.Flow import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.repository.MangaRepository +@Inject class GetUpcomingManga( private val mangaRepository: MangaRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/backup/service/BackupPreferences.kt b/domain/src/main/java/tachiyomi/domain/backup/service/BackupPreferences.kt index 954808c2e..5c699d292 100644 --- a/domain/src/main/java/tachiyomi/domain/backup/service/BackupPreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/backup/service/BackupPreferences.kt @@ -1,8 +1,13 @@ package tachiyomi.domain.backup.service +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.PreferenceStore +@Inject +@SingleIn(AppScope::class) class BackupPreferences( preferenceStore: PreferenceStore, ) { diff --git a/domain/src/main/java/tachiyomi/domain/category/interactor/CreateCategoryWithName.kt b/domain/src/main/java/tachiyomi/domain/category/interactor/CreateCategoryWithName.kt index 8dba6e4bd..244d63545 100644 --- a/domain/src/main/java/tachiyomi/domain/category/interactor/CreateCategoryWithName.kt +++ b/domain/src/main/java/tachiyomi/domain/category/interactor/CreateCategoryWithName.kt @@ -1,5 +1,6 @@ package tachiyomi.domain.category.interactor +import dev.zacsweers.metro.Inject import logcat.LogPriority import tachiyomi.core.common.util.lang.withNonCancellableContext import tachiyomi.core.common.util.system.logcat @@ -7,6 +8,7 @@ import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.repository.CategoryRepository import tachiyomi.domain.library.service.LibraryPreferences +@Inject class CreateCategoryWithName( private val categoryRepository: CategoryRepository, private val preferences: LibraryPreferences, diff --git a/domain/src/main/java/tachiyomi/domain/category/interactor/DeleteCategory.kt b/domain/src/main/java/tachiyomi/domain/category/interactor/DeleteCategory.kt index 46295d08a..bbdbe3b7d 100644 --- a/domain/src/main/java/tachiyomi/domain/category/interactor/DeleteCategory.kt +++ b/domain/src/main/java/tachiyomi/domain/category/interactor/DeleteCategory.kt @@ -1,5 +1,6 @@ package tachiyomi.domain.category.interactor +import dev.zacsweers.metro.Inject import logcat.LogPriority import tachiyomi.core.common.util.lang.withNonCancellableContext import tachiyomi.core.common.util.system.logcat @@ -7,6 +8,7 @@ import tachiyomi.domain.category.repository.CategoryRepository import tachiyomi.domain.download.service.DownloadPreferences import tachiyomi.domain.library.service.LibraryPreferences +@Inject class DeleteCategory( private val categoryRepository: CategoryRepository, private val libraryPreferences: LibraryPreferences, diff --git a/domain/src/main/java/tachiyomi/domain/category/interactor/GetCategories.kt b/domain/src/main/java/tachiyomi/domain/category/interactor/GetCategories.kt index 1d05ce611..609045499 100644 --- a/domain/src/main/java/tachiyomi/domain/category/interactor/GetCategories.kt +++ b/domain/src/main/java/tachiyomi/domain/category/interactor/GetCategories.kt @@ -1,9 +1,11 @@ package tachiyomi.domain.category.interactor +import dev.zacsweers.metro.Inject import kotlinx.coroutines.flow.Flow import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.repository.CategoryRepository +@Inject class GetCategories( private val categoryRepository: CategoryRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/category/interactor/RenameCategory.kt b/domain/src/main/java/tachiyomi/domain/category/interactor/RenameCategory.kt index 3a939d309..849939e56 100644 --- a/domain/src/main/java/tachiyomi/domain/category/interactor/RenameCategory.kt +++ b/domain/src/main/java/tachiyomi/domain/category/interactor/RenameCategory.kt @@ -1,11 +1,13 @@ package tachiyomi.domain.category.interactor +import dev.zacsweers.metro.Inject import logcat.LogPriority import tachiyomi.core.common.util.lang.withNonCancellableContext import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.repository.CategoryRepository +@Inject class RenameCategory( private val categoryRepository: CategoryRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/category/interactor/ReorderCategory.kt b/domain/src/main/java/tachiyomi/domain/category/interactor/ReorderCategory.kt index 8c46c3cac..106030d29 100644 --- a/domain/src/main/java/tachiyomi/domain/category/interactor/ReorderCategory.kt +++ b/domain/src/main/java/tachiyomi/domain/category/interactor/ReorderCategory.kt @@ -1,5 +1,6 @@ package tachiyomi.domain.category.interactor +import dev.zacsweers.metro.Inject import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import logcat.LogPriority @@ -8,6 +9,7 @@ import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.repository.CategoryRepository +@Inject class ReorderCategory( private val categoryRepository: CategoryRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/category/interactor/ResetCategoryFlags.kt b/domain/src/main/java/tachiyomi/domain/category/interactor/ResetCategoryFlags.kt index 4bf262cc2..9750eeb74 100644 --- a/domain/src/main/java/tachiyomi/domain/category/interactor/ResetCategoryFlags.kt +++ b/domain/src/main/java/tachiyomi/domain/category/interactor/ResetCategoryFlags.kt @@ -1,9 +1,11 @@ package tachiyomi.domain.category.interactor +import dev.zacsweers.metro.Inject import tachiyomi.domain.category.repository.CategoryRepository import tachiyomi.domain.library.model.plus import tachiyomi.domain.library.service.LibraryPreferences +@Inject class ResetCategoryFlags( private val preferences: LibraryPreferences, private val categoryRepository: CategoryRepository, diff --git a/domain/src/main/java/tachiyomi/domain/category/interactor/SetDisplayMode.kt b/domain/src/main/java/tachiyomi/domain/category/interactor/SetDisplayMode.kt index af95abf3b..f31d3f114 100644 --- a/domain/src/main/java/tachiyomi/domain/category/interactor/SetDisplayMode.kt +++ b/domain/src/main/java/tachiyomi/domain/category/interactor/SetDisplayMode.kt @@ -1,8 +1,10 @@ package tachiyomi.domain.category.interactor +import dev.zacsweers.metro.Inject import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.service.LibraryPreferences +@Inject class SetDisplayMode( private val preferences: LibraryPreferences, ) { diff --git a/domain/src/main/java/tachiyomi/domain/category/interactor/SetMangaCategories.kt b/domain/src/main/java/tachiyomi/domain/category/interactor/SetMangaCategories.kt index 604e3f0d1..cdf223c68 100644 --- a/domain/src/main/java/tachiyomi/domain/category/interactor/SetMangaCategories.kt +++ b/domain/src/main/java/tachiyomi/domain/category/interactor/SetMangaCategories.kt @@ -1,9 +1,11 @@ package tachiyomi.domain.category.interactor +import dev.zacsweers.metro.Inject import logcat.LogPriority import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.manga.repository.MangaRepository +@Inject class SetMangaCategories( private val mangaRepository: MangaRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/category/interactor/SetSortModeForCategory.kt b/domain/src/main/java/tachiyomi/domain/category/interactor/SetSortModeForCategory.kt index 34fe916a9..49ca0a0fe 100644 --- a/domain/src/main/java/tachiyomi/domain/category/interactor/SetSortModeForCategory.kt +++ b/domain/src/main/java/tachiyomi/domain/category/interactor/SetSortModeForCategory.kt @@ -1,5 +1,6 @@ package tachiyomi.domain.category.interactor +import dev.zacsweers.metro.Inject import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.repository.CategoryRepository import tachiyomi.domain.library.model.LibrarySort @@ -7,6 +8,7 @@ import tachiyomi.domain.library.model.plus import tachiyomi.domain.library.service.LibraryPreferences import kotlin.random.Random +@Inject class SetSortModeForCategory( private val preferences: LibraryPreferences, private val categoryRepository: CategoryRepository, diff --git a/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetBookmarkedChaptersByMangaId.kt b/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetBookmarkedChaptersByMangaId.kt index 129528156..afd6c671e 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetBookmarkedChaptersByMangaId.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetBookmarkedChaptersByMangaId.kt @@ -1,10 +1,12 @@ package tachiyomi.domain.chapter.interactor +import dev.zacsweers.metro.Inject import logcat.LogPriority import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.repository.ChapterRepository +@Inject class GetBookmarkedChaptersByMangaId( private val chapterRepository: ChapterRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChapter.kt b/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChapter.kt index 4b4b9d94d..28794ec68 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChapter.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChapter.kt @@ -1,10 +1,12 @@ package tachiyomi.domain.chapter.interactor +import dev.zacsweers.metro.Inject import logcat.LogPriority import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.repository.ChapterRepository +@Inject class GetChapter( private val chapterRepository: ChapterRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChapterByUrlAndMangaId.kt b/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChapterByUrlAndMangaId.kt index f0399d17f..2c04f82ac 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChapterByUrlAndMangaId.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChapterByUrlAndMangaId.kt @@ -1,8 +1,10 @@ package tachiyomi.domain.chapter.interactor +import dev.zacsweers.metro.Inject import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.repository.ChapterRepository +@Inject class GetChapterByUrlAndMangaId( private val chapterRepository: ChapterRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChaptersByMangaId.kt b/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChaptersByMangaId.kt index 1dee7770e..38def469c 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChaptersByMangaId.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChaptersByMangaId.kt @@ -1,10 +1,12 @@ package tachiyomi.domain.chapter.interactor +import dev.zacsweers.metro.Inject import logcat.LogPriority import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.repository.ChapterRepository +@Inject class GetChaptersByMangaId( private val chapterRepository: ChapterRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/chapter/interactor/SetMangaDefaultChapterFlags.kt b/domain/src/main/java/tachiyomi/domain/chapter/interactor/SetMangaDefaultChapterFlags.kt index 47ea4329e..ec29364cf 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/interactor/SetMangaDefaultChapterFlags.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/interactor/SetMangaDefaultChapterFlags.kt @@ -1,11 +1,13 @@ package tachiyomi.domain.chapter.interactor +import dev.zacsweers.metro.Inject import tachiyomi.core.common.util.lang.withNonCancellableContext import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.manga.interactor.GetFavorites import tachiyomi.domain.manga.interactor.SetMangaChapterFlags import tachiyomi.domain.manga.model.Manga +@Inject class SetMangaDefaultChapterFlags( private val libraryPreferences: LibraryPreferences, private val setMangaChapterFlags: SetMangaChapterFlags, diff --git a/domain/src/main/java/tachiyomi/domain/chapter/interactor/ShouldUpdateDbChapter.kt b/domain/src/main/java/tachiyomi/domain/chapter/interactor/ShouldUpdateDbChapter.kt index 4e4179dec..6baefe2aa 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/interactor/ShouldUpdateDbChapter.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/interactor/ShouldUpdateDbChapter.kt @@ -1,7 +1,9 @@ package tachiyomi.domain.chapter.interactor +import dev.zacsweers.metro.Inject import tachiyomi.domain.chapter.model.Chapter +@Inject class ShouldUpdateDbChapter { fun await(dbChapter: Chapter, sourceChapter: Chapter): Boolean { diff --git a/domain/src/main/java/tachiyomi/domain/chapter/interactor/UpdateChapter.kt b/domain/src/main/java/tachiyomi/domain/chapter/interactor/UpdateChapter.kt index 3daaf9010..8401e8f76 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/interactor/UpdateChapter.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/interactor/UpdateChapter.kt @@ -1,10 +1,12 @@ package tachiyomi.domain.chapter.interactor +import dev.zacsweers.metro.Inject import logcat.LogPriority import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.chapter.model.ChapterUpdate import tachiyomi.domain.chapter.repository.ChapterRepository +@Inject class UpdateChapter( private val chapterRepository: ChapterRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/download/service/DownloadPreferences.kt b/domain/src/main/java/tachiyomi/domain/download/service/DownloadPreferences.kt index bf5b84ff4..5795dc89e 100644 --- a/domain/src/main/java/tachiyomi/domain/download/service/DownloadPreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/download/service/DownloadPreferences.kt @@ -1,8 +1,13 @@ package tachiyomi.domain.download.service +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.PreferenceStore +@Inject +@SingleIn(AppScope::class) class DownloadPreferences( preferenceStore: PreferenceStore, ) { diff --git a/domain/src/main/java/tachiyomi/domain/history/interactor/GetHistory.kt b/domain/src/main/java/tachiyomi/domain/history/interactor/GetHistory.kt index 8a40096f8..daa01f2eb 100644 --- a/domain/src/main/java/tachiyomi/domain/history/interactor/GetHistory.kt +++ b/domain/src/main/java/tachiyomi/domain/history/interactor/GetHistory.kt @@ -1,10 +1,12 @@ package tachiyomi.domain.history.interactor +import dev.zacsweers.metro.Inject import kotlinx.coroutines.flow.Flow import tachiyomi.domain.history.model.History import tachiyomi.domain.history.model.HistoryWithRelations import tachiyomi.domain.history.repository.HistoryRepository +@Inject class GetHistory( private val repository: HistoryRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/history/interactor/GetNextChapters.kt b/domain/src/main/java/tachiyomi/domain/history/interactor/GetNextChapters.kt index 2e7fefc96..5d7bc22df 100644 --- a/domain/src/main/java/tachiyomi/domain/history/interactor/GetNextChapters.kt +++ b/domain/src/main/java/tachiyomi/domain/history/interactor/GetNextChapters.kt @@ -1,5 +1,6 @@ package tachiyomi.domain.history.interactor +import dev.zacsweers.metro.Inject import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.service.getChapterSort @@ -7,6 +8,7 @@ import tachiyomi.domain.history.repository.HistoryRepository import tachiyomi.domain.manga.interactor.GetManga import kotlin.math.max +@Inject class GetNextChapters( private val getChaptersByMangaId: GetChaptersByMangaId, private val getManga: GetManga, diff --git a/domain/src/main/java/tachiyomi/domain/history/interactor/GetTotalReadDuration.kt b/domain/src/main/java/tachiyomi/domain/history/interactor/GetTotalReadDuration.kt index 9bde36520..989c1db5e 100644 --- a/domain/src/main/java/tachiyomi/domain/history/interactor/GetTotalReadDuration.kt +++ b/domain/src/main/java/tachiyomi/domain/history/interactor/GetTotalReadDuration.kt @@ -1,7 +1,9 @@ package tachiyomi.domain.history.interactor +import dev.zacsweers.metro.Inject import tachiyomi.domain.history.repository.HistoryRepository +@Inject class GetTotalReadDuration( private val repository: HistoryRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/history/interactor/RemoveHistory.kt b/domain/src/main/java/tachiyomi/domain/history/interactor/RemoveHistory.kt index c6c8398ed..340a52127 100644 --- a/domain/src/main/java/tachiyomi/domain/history/interactor/RemoveHistory.kt +++ b/domain/src/main/java/tachiyomi/domain/history/interactor/RemoveHistory.kt @@ -1,8 +1,10 @@ package tachiyomi.domain.history.interactor +import dev.zacsweers.metro.Inject import tachiyomi.domain.history.model.HistoryWithRelations import tachiyomi.domain.history.repository.HistoryRepository +@Inject class RemoveHistory( private val repository: HistoryRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/history/interactor/UpsertHistory.kt b/domain/src/main/java/tachiyomi/domain/history/interactor/UpsertHistory.kt index 86b041b77..1fd4ce45a 100644 --- a/domain/src/main/java/tachiyomi/domain/history/interactor/UpsertHistory.kt +++ b/domain/src/main/java/tachiyomi/domain/history/interactor/UpsertHistory.kt @@ -1,8 +1,10 @@ package tachiyomi.domain.history.interactor +import dev.zacsweers.metro.Inject import tachiyomi.domain.history.model.HistoryUpdate import tachiyomi.domain.history.repository.HistoryRepository +@Inject class UpsertHistory( private val historyRepository: HistoryRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt b/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt index a7f46166c..c402efbf2 100644 --- a/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt @@ -1,5 +1,8 @@ package tachiyomi.domain.library.service +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.PreferenceStore import tachiyomi.core.common.preference.TriState @@ -8,6 +11,8 @@ import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibrarySort import tachiyomi.domain.manga.model.Manga +@Inject +@SingleIn(AppScope::class) class LibraryPreferences( private val preferenceStore: PreferenceStore, ) { diff --git a/domain/src/main/java/tachiyomi/domain/manga/interactor/FetchInterval.kt b/domain/src/main/java/tachiyomi/domain/manga/interactor/FetchInterval.kt index bdc0ba606..a38e9e4d2 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/interactor/FetchInterval.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/interactor/FetchInterval.kt @@ -1,5 +1,6 @@ package tachiyomi.domain.manga.interactor +import dev.zacsweers.metro.Inject import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDateTime import kotlinx.datetime.TimeZone @@ -16,6 +17,7 @@ import kotlin.time.Clock import kotlin.time.Duration.Companion.days import kotlin.time.Instant +@Inject class FetchInterval( private val getChaptersByMangaId: GetChaptersByMangaId, ) { diff --git a/domain/src/main/java/tachiyomi/domain/manga/interactor/GetDuplicateLibraryManga.kt b/domain/src/main/java/tachiyomi/domain/manga/interactor/GetDuplicateLibraryManga.kt index 575f68947..08428dffb 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/interactor/GetDuplicateLibraryManga.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/interactor/GetDuplicateLibraryManga.kt @@ -1,9 +1,11 @@ package tachiyomi.domain.manga.interactor +import dev.zacsweers.metro.Inject import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.MangaWithChapterCount import tachiyomi.domain.manga.repository.MangaRepository +@Inject class GetDuplicateLibraryManga( private val mangaRepository: MangaRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/manga/interactor/GetFavorites.kt b/domain/src/main/java/tachiyomi/domain/manga/interactor/GetFavorites.kt index a158b0f38..95b478c3a 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/interactor/GetFavorites.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/interactor/GetFavorites.kt @@ -1,9 +1,11 @@ package tachiyomi.domain.manga.interactor +import dev.zacsweers.metro.Inject import kotlinx.coroutines.flow.Flow import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.repository.MangaRepository +@Inject class GetFavorites( private val mangaRepository: MangaRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/manga/interactor/GetLibraryManga.kt b/domain/src/main/java/tachiyomi/domain/manga/interactor/GetLibraryManga.kt index 58ef64110..b76b431a7 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/interactor/GetLibraryManga.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/interactor/GetLibraryManga.kt @@ -1,5 +1,6 @@ package tachiyomi.domain.manga.interactor +import dev.zacsweers.metro.Inject import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch @@ -10,6 +11,7 @@ import tachiyomi.domain.library.model.LibraryManga import tachiyomi.domain.manga.repository.MangaRepository import kotlin.time.Duration.Companion.seconds +@Inject class GetLibraryManga( private val mangaRepository: MangaRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/manga/interactor/GetManga.kt b/domain/src/main/java/tachiyomi/domain/manga/interactor/GetManga.kt index bbe22f989..2915db75b 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/interactor/GetManga.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/interactor/GetManga.kt @@ -1,11 +1,13 @@ package tachiyomi.domain.manga.interactor +import dev.zacsweers.metro.Inject import kotlinx.coroutines.flow.Flow import logcat.LogPriority import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.repository.MangaRepository +@Inject class GetManga( private val mangaRepository: MangaRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/manga/interactor/GetMangaByUrlAndSourceId.kt b/domain/src/main/java/tachiyomi/domain/manga/interactor/GetMangaByUrlAndSourceId.kt index c245a7da0..b8f996cb3 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/interactor/GetMangaByUrlAndSourceId.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/interactor/GetMangaByUrlAndSourceId.kt @@ -1,8 +1,10 @@ package tachiyomi.domain.manga.interactor +import dev.zacsweers.metro.Inject import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.repository.MangaRepository +@Inject class GetMangaByUrlAndSourceId( private val mangaRepository: MangaRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/manga/interactor/GetMangaWithChapters.kt b/domain/src/main/java/tachiyomi/domain/manga/interactor/GetMangaWithChapters.kt index 4fddd8140..c35e7a53b 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/interactor/GetMangaWithChapters.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/interactor/GetMangaWithChapters.kt @@ -1,5 +1,6 @@ package tachiyomi.domain.manga.interactor +import dev.zacsweers.metro.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import tachiyomi.domain.chapter.model.Chapter @@ -7,6 +8,7 @@ import tachiyomi.domain.chapter.repository.ChapterRepository import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.repository.MangaRepository +@Inject class GetMangaWithChapters( private val mangaRepository: MangaRepository, private val chapterRepository: ChapterRepository, diff --git a/domain/src/main/java/tachiyomi/domain/manga/interactor/NetworkToLocalManga.kt b/domain/src/main/java/tachiyomi/domain/manga/interactor/NetworkToLocalManga.kt index 69137d9e9..061c2130f 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/interactor/NetworkToLocalManga.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/interactor/NetworkToLocalManga.kt @@ -1,8 +1,10 @@ package tachiyomi.domain.manga.interactor +import dev.zacsweers.metro.Inject import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.repository.MangaRepository +@Inject class NetworkToLocalManga( private val mangaRepository: MangaRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/manga/interactor/ResetViewerFlags.kt b/domain/src/main/java/tachiyomi/domain/manga/interactor/ResetViewerFlags.kt index 44b546e2d..8f4656cf4 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/interactor/ResetViewerFlags.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/interactor/ResetViewerFlags.kt @@ -1,7 +1,9 @@ package tachiyomi.domain.manga.interactor +import dev.zacsweers.metro.Inject import tachiyomi.domain.manga.repository.MangaRepository +@Inject class ResetViewerFlags( private val mangaRepository: MangaRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/manga/interactor/SetMangaChapterFlags.kt b/domain/src/main/java/tachiyomi/domain/manga/interactor/SetMangaChapterFlags.kt index e3cacc4bf..bd7e6a326 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/interactor/SetMangaChapterFlags.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/interactor/SetMangaChapterFlags.kt @@ -1,9 +1,11 @@ package tachiyomi.domain.manga.interactor +import dev.zacsweers.metro.Inject import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.MangaUpdate import tachiyomi.domain.manga.repository.MangaRepository +@Inject class SetMangaChapterFlags( private val mangaRepository: MangaRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/manga/interactor/UpdateMangaNotes.kt b/domain/src/main/java/tachiyomi/domain/manga/interactor/UpdateMangaNotes.kt index cadb3aed7..c6319c5a5 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/interactor/UpdateMangaNotes.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/interactor/UpdateMangaNotes.kt @@ -1,8 +1,10 @@ package tachiyomi.domain.manga.interactor +import dev.zacsweers.metro.Inject import tachiyomi.domain.manga.model.MangaUpdate import tachiyomi.domain.manga.repository.MangaRepository +@Inject class UpdateMangaNotes( private val mangaRepository: MangaRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt b/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt index 49e9849e6..98237f0ca 100644 --- a/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt +++ b/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt @@ -1,8 +1,10 @@ package tachiyomi.domain.release.interactor +import dev.zacsweers.metro.Inject import tachiyomi.domain.release.model.Release import tachiyomi.domain.release.service.ReleaseService +@Inject class GetApplicationRelease( private val service: ReleaseService, ) { diff --git a/domain/src/main/java/tachiyomi/domain/source/interactor/GetRemoteManga.kt b/domain/src/main/java/tachiyomi/domain/source/interactor/GetRemoteManga.kt index 8ec432bbd..772fd6b32 100644 --- a/domain/src/main/java/tachiyomi/domain/source/interactor/GetRemoteManga.kt +++ b/domain/src/main/java/tachiyomi/domain/source/interactor/GetRemoteManga.kt @@ -1,9 +1,11 @@ package tachiyomi.domain.source.interactor +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.source.model.FilterList import tachiyomi.domain.source.repository.SourcePagingSource import tachiyomi.domain.source.repository.SourceRepository +@Inject class GetRemoteManga( private val repository: SourceRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/source/interactor/GetSourcesWithNonLibraryManga.kt b/domain/src/main/java/tachiyomi/domain/source/interactor/GetSourcesWithNonLibraryManga.kt index d53445d0c..31e4f7240 100644 --- a/domain/src/main/java/tachiyomi/domain/source/interactor/GetSourcesWithNonLibraryManga.kt +++ b/domain/src/main/java/tachiyomi/domain/source/interactor/GetSourcesWithNonLibraryManga.kt @@ -1,9 +1,11 @@ package tachiyomi.domain.source.interactor +import dev.zacsweers.metro.Inject import kotlinx.coroutines.flow.Flow import tachiyomi.domain.source.model.SourceWithCount import tachiyomi.domain.source.repository.SourceRepository +@Inject class GetSourcesWithNonLibraryManga( private val repository: SourceRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/storage/service/StorageManager.kt b/domain/src/main/java/tachiyomi/domain/storage/service/StorageManager.kt index 002d976fb..457013e1f 100644 --- a/domain/src/main/java/tachiyomi/domain/storage/service/StorageManager.kt +++ b/domain/src/main/java/tachiyomi/domain/storage/service/StorageManager.kt @@ -3,6 +3,9 @@ package tachiyomi.domain.storage.service import android.content.Context import androidx.core.net.toUri import com.hippo.unifile.UniFile +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import eu.kanade.tachiyomi.util.storage.DiskUtil import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -15,6 +18,8 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.shareIn +@Inject +@SingleIn(AppScope::class) class StorageManager( private val context: Context, storagePreferences: StoragePreferences, diff --git a/domain/src/main/java/tachiyomi/domain/storage/service/StoragePreferences.kt b/domain/src/main/java/tachiyomi/domain/storage/service/StoragePreferences.kt index 159134767..42282f725 100644 --- a/domain/src/main/java/tachiyomi/domain/storage/service/StoragePreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/storage/service/StoragePreferences.kt @@ -1,9 +1,14 @@ package tachiyomi.domain.storage.service +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.PreferenceStore import tachiyomi.core.common.storage.FolderProvider +@Inject +@SingleIn(AppScope::class) class StoragePreferences( folderProvider: FolderProvider, preferenceStore: PreferenceStore, diff --git a/domain/src/main/java/tachiyomi/domain/track/interactor/DeleteTrack.kt b/domain/src/main/java/tachiyomi/domain/track/interactor/DeleteTrack.kt index 4204b3872..418c47dbd 100644 --- a/domain/src/main/java/tachiyomi/domain/track/interactor/DeleteTrack.kt +++ b/domain/src/main/java/tachiyomi/domain/track/interactor/DeleteTrack.kt @@ -1,9 +1,11 @@ package tachiyomi.domain.track.interactor +import dev.zacsweers.metro.Inject import logcat.LogPriority import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.track.repository.TrackRepository +@Inject class DeleteTrack( private val trackRepository: TrackRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/track/interactor/GetTracks.kt b/domain/src/main/java/tachiyomi/domain/track/interactor/GetTracks.kt index 851ecd0ed..bbb2316ab 100644 --- a/domain/src/main/java/tachiyomi/domain/track/interactor/GetTracks.kt +++ b/domain/src/main/java/tachiyomi/domain/track/interactor/GetTracks.kt @@ -1,11 +1,13 @@ package tachiyomi.domain.track.interactor +import dev.zacsweers.metro.Inject import kotlinx.coroutines.flow.Flow import logcat.LogPriority import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.track.model.Track import tachiyomi.domain.track.repository.TrackRepository +@Inject class GetTracks( private val trackRepository: TrackRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/track/interactor/GetTracksPerManga.kt b/domain/src/main/java/tachiyomi/domain/track/interactor/GetTracksPerManga.kt index 36478fdfe..05434e97e 100644 --- a/domain/src/main/java/tachiyomi/domain/track/interactor/GetTracksPerManga.kt +++ b/domain/src/main/java/tachiyomi/domain/track/interactor/GetTracksPerManga.kt @@ -1,10 +1,12 @@ package tachiyomi.domain.track.interactor +import dev.zacsweers.metro.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import tachiyomi.domain.track.model.Track import tachiyomi.domain.track.repository.TrackRepository +@Inject class GetTracksPerManga( private val trackRepository: TrackRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/track/interactor/InsertTrack.kt b/domain/src/main/java/tachiyomi/domain/track/interactor/InsertTrack.kt index 3277e9b9c..1a82bbd3c 100644 --- a/domain/src/main/java/tachiyomi/domain/track/interactor/InsertTrack.kt +++ b/domain/src/main/java/tachiyomi/domain/track/interactor/InsertTrack.kt @@ -1,10 +1,12 @@ package tachiyomi.domain.track.interactor +import dev.zacsweers.metro.Inject import logcat.LogPriority import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.track.model.Track import tachiyomi.domain.track.repository.TrackRepository +@Inject class InsertTrack( private val trackRepository: TrackRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/upcoming/service/UpcomingPreferences.kt b/domain/src/main/java/tachiyomi/domain/upcoming/service/UpcomingPreferences.kt index 7c3c6acf9..009e7b22b 100644 --- a/domain/src/main/java/tachiyomi/domain/upcoming/service/UpcomingPreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/upcoming/service/UpcomingPreferences.kt @@ -1,9 +1,14 @@ package tachiyomi.domain.upcoming.service +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.PreferenceStore import tachiyomi.core.common.preference.getLongArray +@Inject +@SingleIn(AppScope::class) class UpcomingPreferences( preferenceStore: PreferenceStore, ) { diff --git a/domain/src/main/java/tachiyomi/domain/updates/interactor/GetUpdates.kt b/domain/src/main/java/tachiyomi/domain/updates/interactor/GetUpdates.kt index 836358645..59e11f7fa 100644 --- a/domain/src/main/java/tachiyomi/domain/updates/interactor/GetUpdates.kt +++ b/domain/src/main/java/tachiyomi/domain/updates/interactor/GetUpdates.kt @@ -1,10 +1,12 @@ package tachiyomi.domain.updates.interactor +import dev.zacsweers.metro.Inject import kotlinx.coroutines.flow.Flow import tachiyomi.domain.updates.model.UpdatesWithRelations import tachiyomi.domain.updates.repository.UpdatesRepository import kotlin.time.Instant +@Inject class GetUpdates( private val repository: UpdatesRepository, ) { diff --git a/domain/src/main/java/tachiyomi/domain/updates/service/UpdatesPreferences.kt b/domain/src/main/java/tachiyomi/domain/updates/service/UpdatesPreferences.kt index 47e650859..e8a4eeed6 100644 --- a/domain/src/main/java/tachiyomi/domain/updates/service/UpdatesPreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/updates/service/UpdatesPreferences.kt @@ -1,11 +1,16 @@ package tachiyomi.domain.updates.service +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.PreferenceStore import tachiyomi.core.common.preference.TriState import tachiyomi.core.common.preference.getEnum import tachiyomi.core.common.preference.getLongArray +@Inject +@SingleIn(AppScope::class) class UpdatesPreferences( preferenceStore: PreferenceStore, ) { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ced6757bf..8f387d16f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -55,6 +55,9 @@ logcat = "0.4" markdown = "0.43.0" material = "1.12.0" materialKolor = "5.0.0" +metro = "1.4.2" +metrox-viewmodel = "1.4.2" +metrox-viewmodel-compose = "1.4.2" mockk = "1.14.11" moko-resources = "0.26.4" okhttp = "5.5.0" @@ -160,6 +163,9 @@ markdown-coil = { module = "com.mikepenz:multiplatform-markdown-renderer-coil3", markdown-core = { module = "com.mikepenz:multiplatform-markdown-renderer", version.ref = "markdown" } material = { module = "com.google.android.material:material", version.ref = "material" } materialKolor = { module = "com.materialkolor:material-kolor", version.ref = "materialKolor" } +metro-runtime = { module = "dev.zacsweers.metro:runtime", version.ref = "metro" } +metrox-viewmodel = { module = "dev.zacsweers.metro:metrox-viewmodel", version.ref = "metrox-viewmodel" } +metrox-viewmodel-compose = { module = "dev.zacsweers.metro:metrox-viewmodel-compose", version.ref = "metrox-viewmodel-compose" } mockk = { module = "io.mockk:mockk", version.ref = "mockk" } moko-resources = { module = "dev.icerock.moko:resources", version.ref = "moko-resources" } natural-comparator = { module = "com.github.gpanther:java-nat-sort", version = "natural-comparator-1.1" } @@ -208,6 +214,7 @@ kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref kotlin-samWithReceiver = { id = "org.jetbrains.kotlin.plugin.sam.with.receiver", version.ref = "kotlin-gradle" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin-gradle" } moko-resources = { id = "dev.icerock.mobile.multiplatform-resources", version.ref = "moko-resources" } +metro = { id = "dev.zacsweers.metro", version.ref = "metro" } spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } diff --git a/presentation-widget/build.gradle.kts b/presentation-widget/build.gradle.kts index 97935148d..7f123a456 100644 --- a/presentation-widget/build.gradle.kts +++ b/presentation-widget/build.gradle.kts @@ -3,6 +3,7 @@ plugins { alias(mihonx.plugins.compose) alias(mihonx.plugins.spotless) + alias(libs.plugins.metro) } android { @@ -11,9 +12,10 @@ android { dependencies { implementation(projects.core.common) + implementation(projects.core.metro) implementation(projects.domain) implementation(projects.presentationCore) - api(projects.i18n) + implementation(projects.i18n) implementation(libs.androidx.glance.appWidget) implementation(libs.material) @@ -22,5 +24,5 @@ dependencies { implementation(libs.coil.core) - api(libs.injekt) + implementation(libs.metro.runtime) } diff --git a/presentation-widget/src/main/java/mihon/presentation/widget/di/PresentationWidgetGraph.kt b/presentation-widget/src/main/java/mihon/presentation/widget/di/PresentationWidgetGraph.kt new file mode 100644 index 000000000..a70441a64 --- /dev/null +++ b/presentation-widget/src/main/java/mihon/presentation/widget/di/PresentationWidgetGraph.kt @@ -0,0 +1,10 @@ +package mihon.presentation.widget.di + +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesTo +import tachiyomi.presentation.widget.BaseUpdatesGridGlanceWidget + +@ContributesTo(AppScope::class) +interface PresentationWidgetGraph { + fun inject(widget: BaseUpdatesGridGlanceWidget) +} diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt index 11cf4efa6..a2b8dff68 100644 --- a/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt +++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt @@ -1,6 +1,5 @@ package tachiyomi.presentation.widget -import android.app.Application import android.content.Context import android.graphics.Bitmap import android.os.Build @@ -31,12 +30,16 @@ import coil3.request.transformations import coil3.size.Precision import coil3.size.Scale import coil3.transform.RoundedCornersTransformation +import dev.zacsweers.metro.HasMemberInjections +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.core.security.SecurityPreferences import eu.kanade.tachiyomi.util.system.dpToPx import kotlinx.coroutines.flow.map import kotlinx.datetime.DateTimeUnit import kotlinx.datetime.TimeZone import kotlinx.datetime.minus +import mihon.core.metro.metroGraph +import mihon.presentation.widget.di.PresentationWidgetGraph import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.domain.manga.model.MangaCover import tachiyomi.domain.updates.interactor.GetUpdates @@ -47,16 +50,15 @@ import tachiyomi.presentation.widget.components.LockedWidget import tachiyomi.presentation.widget.components.UpdatesWidget import tachiyomi.presentation.widget.util.appWidgetBackgroundRadius import tachiyomi.presentation.widget.util.calculateRowAndColumnCount -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import kotlin.time.Clock import kotlin.time.Instant -abstract class BaseUpdatesGridGlanceWidget( - private val context: Context = Injekt.get(), - private val getUpdates: GetUpdates = Injekt.get(), - private val preferences: SecurityPreferences = Injekt.get(), -) : GlanceAppWidget() { +@HasMemberInjections +abstract class BaseUpdatesGridGlanceWidget : GlanceAppWidget() { + + @Inject private lateinit var getUpdates: GetUpdates + + @Inject private lateinit var preferences: SecurityPreferences override val sizeMode = SizeMode.Exact @@ -66,6 +68,7 @@ abstract class BaseUpdatesGridGlanceWidget( abstract val bottomPadding: Dp override suspend fun provideGlance(context: Context, id: GlanceId) { + context.metroGraph().inject(this) val locked = preferences.useAuthenticator.get() val containerModifier = GlanceModifier .fillMaxSize() @@ -95,7 +98,7 @@ abstract class BaseUpdatesGridGlanceWidget( getUpdates .subscribe(false, DateLimit.toEpochMilliseconds()) .map { rawData -> - rawData.prepareData(rowCount, columnCount) + rawData.prepareData(context, rowCount, columnCount) } } val data by flow.collectAsState(initial = null) @@ -111,6 +114,7 @@ abstract class BaseUpdatesGridGlanceWidget( @OptIn(ExperimentalCoilApi::class) private suspend fun List.prepareData( + context: Context, rowCount: Int, columnCount: Int, ): List> { diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridGlanceReceiver.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridGlanceReceiver.kt index 3ee232781..99558d43e 100644 --- a/presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridGlanceReceiver.kt +++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/UpdatesGridGlanceReceiver.kt @@ -4,6 +4,7 @@ import androidx.glance.appwidget.GlanceAppWidget import androidx.glance.appwidget.GlanceAppWidgetReceiver class UpdatesGridGlanceReceiver : GlanceAppWidgetReceiver() { + override val glanceAppWidget: GlanceAppWidget get() = UpdatesGridGlanceWidget() } diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/WidgetManager.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/WidgetManager.kt index 7280587c5..7105eacd0 100644 --- a/presentation-widget/src/main/java/tachiyomi/presentation/widget/WidgetManager.kt +++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/WidgetManager.kt @@ -3,6 +3,7 @@ package tachiyomi.presentation.widget import android.content.Context import androidx.glance.appwidget.updateAll import androidx.lifecycle.LifecycleCoroutineScope +import dev.zacsweers.metro.Inject import eu.kanade.tachiyomi.core.security.SecurityPreferences import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.combine @@ -14,12 +15,14 @@ import logcat.LogPriority import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.updates.interactor.GetUpdates +@Inject class WidgetManager( private val getUpdates: GetUpdates, private val securityPreferences: SecurityPreferences, ) { - fun Context.init(scope: LifecycleCoroutineScope) { + context(context: Context) + fun init(scope: LifecycleCoroutineScope) { combine( getUpdates.subscribe(read = false, after = BaseUpdatesGridGlanceWidget.DateLimit.toEpochMilliseconds()), securityPreferences.useAuthenticator.changes(), @@ -31,10 +34,10 @@ class WidgetManager( } .onEach { try { - UpdatesGridGlanceWidget().updateAll(this) - UpdatesGridCoverScreenGlanceWidget().updateAll(this) + UpdatesGridGlanceWidget().updateAll(context) + UpdatesGridCoverScreenGlanceWidget().updateAll(context) } catch (e: Exception) { - logcat(LogPriority.ERROR, e) { "Failed to update widget" } + this.logcat(LogPriority.ERROR, e) { "Failed to update widget" } } } .flowOn(Dispatchers.Default) diff --git a/settings.gradle.kts b/settings.gradle.kts index 7ca344609..9ae27d3b9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -34,6 +34,7 @@ include(":baseline-profile") include(":core-metadata") include(":core:archive") include(":core:common") +include(":core:metro") include(":data") include(":domain") include(":i18n") diff --git a/source-api/src/main/kotlin/eu/kanade/tachiyomi/source/ConfigurableSource.kt b/source-api/src/main/kotlin/eu/kanade/tachiyomi/source/ConfigurableSource.kt index 0a4043897..0024866a5 100644 --- a/source-api/src/main/kotlin/eu/kanade/tachiyomi/source/ConfigurableSource.kt +++ b/source-api/src/main/kotlin/eu/kanade/tachiyomi/source/ConfigurableSource.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.source -import android.app.Application import android.content.Context import android.content.SharedPreferences import androidx.preference.PreferenceScreen @@ -15,7 +14,7 @@ interface ConfigurableSource : Source { * @since extensions-lib 1.5 */ fun getSourcePreferences(): SharedPreferences = - Injekt.get().getSharedPreferences(preferenceKey(), Context.MODE_PRIVATE) + Injekt.get().getSharedPreferences(preferenceKey(), Context.MODE_PRIVATE) fun setupPreferenceScreen(screen: PreferenceScreen) } @@ -24,7 +23,7 @@ fun ConfigurableSource.preferenceKey(): String = "source_$id" // TODO: use getSourcePreferences once all extensions are on ext-lib 1.5 fun ConfigurableSource.sourcePreferences(): SharedPreferences = - Injekt.get().getSharedPreferences(preferenceKey(), Context.MODE_PRIVATE) + Injekt.get().getSharedPreferences(preferenceKey(), Context.MODE_PRIVATE) fun sourcePreferences(key: String): SharedPreferences = - Injekt.get().getSharedPreferences(key, Context.MODE_PRIVATE) + Injekt.get().getSharedPreferences(key, Context.MODE_PRIVATE) diff --git a/source-api/src/main/kotlin/eu/kanade/tachiyomi/util/RxExtension.kt b/source-api/src/main/kotlin/eu/kanade/tachiyomi/util/RxExtension.kt deleted file mode 100644 index 511fc93ce..000000000 --- a/source-api/src/main/kotlin/eu/kanade/tachiyomi/util/RxExtension.kt +++ /dev/null @@ -1,6 +0,0 @@ -package eu.kanade.tachiyomi.util - -import rx.Observable -import tachiyomi.core.common.util.lang.awaitSingle - -suspend fun Observable.awaitSingle(): T = awaitSingle() diff --git a/source-local/build.gradle.kts b/source-local/build.gradle.kts index b3117fe8a..a8f6d3b3e 100644 --- a/source-local/build.gradle.kts +++ b/source-local/build.gradle.kts @@ -1,6 +1,8 @@ plugins { alias(mihonx.plugins.android.library) alias(mihonx.plugins.spotless) + + alias(libs.plugins.metro) } android { @@ -22,6 +24,8 @@ dependencies { implementation(projects.coreMetadata) implementation(projects.domain) + implementation(libs.metro.runtime) + implementation(libs.unifile) implementation(libs.bundles.serialization) diff --git a/source-local/src/main/kotlin/tachiyomi/source/local/LocalSource.kt b/source-local/src/main/kotlin/tachiyomi/source/local/LocalSource.kt index e5a307f37..28e111861 100644 --- a/source-local/src/main/kotlin/tachiyomi/source/local/LocalSource.kt +++ b/source-local/src/main/kotlin/tachiyomi/source/local/LocalSource.kt @@ -2,6 +2,9 @@ package tachiyomi.source.local import android.content.Context 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.UnmeteredSource import eu.kanade.tachiyomi.source.model.FilterList @@ -47,6 +50,8 @@ import java.nio.charset.StandardCharsets import kotlin.time.Duration.Companion.days import tachiyomi.domain.source.model.Source as DomainSource +@Inject +@SingleIn(AppScope::class) class LocalSource( private val context: Context, private val fileSystem: LocalSourceFileSystem, diff --git a/source-local/src/main/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt b/source-local/src/main/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt index ce0c8b44e..2dea840a2 100644 --- a/source-local/src/main/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt +++ b/source-local/src/main/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt @@ -2,6 +2,9 @@ package tachiyomi.source.local.image import android.content.Context 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.model.SManga import eu.kanade.tachiyomi.util.storage.DiskUtil import tachiyomi.core.common.storage.nameWithoutExtension @@ -11,6 +14,8 @@ import java.io.InputStream private const val DEFAULT_COVER_NAME = "cover.jpg" +@Inject +@SingleIn(AppScope::class) class LocalCoverManager( private val context: Context, private val fileSystem: LocalSourceFileSystem, diff --git a/source-local/src/main/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt b/source-local/src/main/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt index ea071ec5f..0efb22ce9 100644 --- a/source-local/src/main/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt +++ b/source-local/src/main/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt @@ -1,8 +1,13 @@ package tachiyomi.source.local.io import com.hippo.unifile.UniFile +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn import tachiyomi.domain.storage.service.StorageManager +@Inject +@SingleIn(AppScope::class) class LocalSourceFileSystem( private val storageManager: StorageManager, ) {