Resolve Injekt dependencies directly from the Metro graph (#3965)
The previous setup had a circular dependency between Injekt registration and extension loading. In regard to #3931, I've moved the culprit ExtensionManager out of Injekt altogether, alongside CoverCache, PreferenceStore and TrackPreferences, none of which a source needs.
This commit is contained in:
@@ -72,7 +72,7 @@ fun Manga.copyFrom(other: SManga): Manga {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
|
fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get<Context>().appGraph.coverCache): Boolean {
|
||||||
return coverCache.getCustomCoverFile(id).exists()
|
return coverCache.getCustomCoverFile(id).exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-5
@@ -40,7 +40,6 @@ import androidx.compose.ui.text.style.TextAlign
|
|||||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import eu.kanade.domain.ui.UiPreferences
|
|
||||||
import eu.kanade.domain.ui.model.AppTheme
|
import eu.kanade.domain.ui.model.AppTheme
|
||||||
import eu.kanade.presentation.manga.components.MangaCover
|
import eu.kanade.presentation.manga.components.MangaCover
|
||||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||||
@@ -48,13 +47,10 @@ import eu.kanade.tachiyomi.util.system.DeviceUtil
|
|||||||
import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable
|
import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable
|
||||||
import mihon.icons.materialsymbols.MaterialSymbols
|
import mihon.icons.materialsymbols.MaterialSymbols
|
||||||
import mihon.icons.materialsymbols.roundedfilled.CheckCircle
|
import mihon.icons.materialsymbols.roundedfilled.CheckCircle
|
||||||
import tachiyomi.core.common.preference.InMemoryPreferenceStore
|
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.util.secondaryItemAlpha
|
import tachiyomi.presentation.core.util.secondaryItemAlpha
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.fullType
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun AppThemePreferenceWidget(
|
internal fun AppThemePreferenceWidget(
|
||||||
@@ -261,7 +257,6 @@ fun AppThemePreviewItem(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun AppThemesListPreview() {
|
private fun AppThemesListPreview() {
|
||||||
var appTheme by remember { mutableStateOf(AppTheme.DEFAULT) }
|
var appTheme by remember { mutableStateOf(AppTheme.DEFAULT) }
|
||||||
Injekt.addSingleton(fullType<UiPreferences>(), UiPreferences(InMemoryPreferenceStore()))
|
|
||||||
TachiyomiTheme(appTheme = appTheme) {
|
TachiyomiTheme(appTheme = appTheme) {
|
||||||
Surface {
|
Surface {
|
||||||
AppThemesList(
|
AppThemesList(
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ import logcat.AndroidLogcatLogger
|
|||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import logcat.LogcatLogger
|
import logcat.LogcatLogger
|
||||||
import mihon.app.di.AppGraph
|
import mihon.app.di.AppGraph
|
||||||
import mihon.app.di.injekt.MetroInteropModule
|
import mihon.app.di.injekt.MetroInjektRegistrar
|
||||||
import mihon.core.metro.GraphProvider
|
import mihon.core.metro.GraphProvider
|
||||||
import mihon.core.migration.Migration
|
import mihon.core.migration.Migration
|
||||||
import mihon.core.migration.Migrator
|
import mihon.core.migration.Migrator
|
||||||
@@ -66,7 +66,7 @@ import tachiyomi.domain.source.service.SourceManager
|
|||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.widget.WidgetManager
|
import tachiyomi.presentation.widget.WidgetManager
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.addSingleton
|
import uy.kohesive.injekt.api.InjektScope
|
||||||
import java.security.Security
|
import java.security.Security
|
||||||
|
|
||||||
class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factory, GraphProvider<AppGraph> {
|
class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factory, GraphProvider<AppGraph> {
|
||||||
@@ -93,8 +93,6 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
|
|||||||
|
|
||||||
@Inject private lateinit var widgetManager: WidgetManager
|
@Inject private lateinit var widgetManager: WidgetManager
|
||||||
|
|
||||||
@Inject private lateinit var injektMetroInteropModule: MetroInteropModule
|
|
||||||
|
|
||||||
@Inject private lateinit var migrations: Set<Migration>
|
@Inject private lateinit var migrations: Set<Migration>
|
||||||
|
|
||||||
private val disableIncognitoReceiver = DisableIncognitoReceiver()
|
private val disableIncognitoReceiver = DisableIncognitoReceiver()
|
||||||
@@ -110,8 +108,8 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
|
|||||||
if (packageName != process) WebView.setDataDirectorySuffix(process)
|
if (packageName != process) WebView.setDataDirectorySuffix(process)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Injekt = InjektScope(MetroInjektRegistrar(application = this, graphProvider = this))
|
||||||
graph.inject(this)
|
graph.inject(this)
|
||||||
setupInjekt()
|
|
||||||
|
|
||||||
TelemetryConfig.init(applicationContext)
|
TelemetryConfig.init(applicationContext)
|
||||||
|
|
||||||
@@ -185,12 +183,6 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
|
|||||||
initializeMigrator()
|
initializeMigrator()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupInjekt() {
|
|
||||||
Injekt.addSingleton<Application>(this)
|
|
||||||
Injekt.addSingleton<Context>(this)
|
|
||||||
Injekt.importModule(injektMetroInteropModule)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initializeMigrator() {
|
private fun initializeMigrator() {
|
||||||
val preference = preferenceStore.getInt(Preference.appStateKey("last_version_code"), 0)
|
val preference = preferenceStore.getInt(Preference.appStateKey("last_version_code"), 0)
|
||||||
logcat {
|
logcat {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import android.net.Uri
|
|||||||
import androidx.annotation.CallSuper
|
import androidx.annotation.CallSuper
|
||||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||||
import eu.kanade.tachiyomi.extension.model.InstallStep
|
import eu.kanade.tachiyomi.extension.model.InstallStep
|
||||||
import uy.kohesive.injekt.injectLazy
|
import mihon.app.di.appGraph
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
import java.util.concurrent.CopyOnWriteArraySet
|
import java.util.concurrent.CopyOnWriteArraySet
|
||||||
import kotlin.concurrent.atomics.AtomicReference
|
import kotlin.concurrent.atomics.AtomicReference
|
||||||
@@ -17,7 +17,7 @@ import kotlin.concurrent.atomics.ExperimentalAtomicApi
|
|||||||
@OptIn(ExperimentalAtomicApi::class)
|
@OptIn(ExperimentalAtomicApi::class)
|
||||||
abstract class Installer(private val service: Service) {
|
abstract class Installer(private val service: Service) {
|
||||||
|
|
||||||
private val extensionManager: ExtensionManager by injectLazy()
|
private val extensionManager: ExtensionManager by lazy { service.appGraph.extensionManager }
|
||||||
|
|
||||||
private var waitingInstall = AtomicReference<Entry?>(null)
|
private var waitingInstall = AtomicReference<Entry?>(null)
|
||||||
private val queue = Collections.synchronizedList(mutableListOf<Entry>())
|
private val queue = Collections.synchronizedList(mutableListOf<Entry>())
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import eu.kanade.tachiyomi.core.security.SecurityPreferences
|
|||||||
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
|
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
|
||||||
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
|
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
|
||||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||||
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadCache
|
import eu.kanade.tachiyomi.data.download.DownloadCache
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadJob
|
import eu.kanade.tachiyomi.data.download.DownloadJob
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
@@ -29,6 +30,7 @@ import eu.kanade.tachiyomi.data.track.TrackerManager
|
|||||||
import eu.kanade.tachiyomi.data.updater.AppUpdateChecker
|
import eu.kanade.tachiyomi.data.updater.AppUpdateChecker
|
||||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||||
import eu.kanade.tachiyomi.extension.util.ExtensionInstallActivity
|
import eu.kanade.tachiyomi.extension.util.ExtensionInstallActivity
|
||||||
|
import eu.kanade.tachiyomi.network.JavaScriptEngine
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.network.NetworkPreferences
|
import eu.kanade.tachiyomi.network.NetworkPreferences
|
||||||
import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegateImpl
|
import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegateImpl
|
||||||
@@ -39,8 +41,10 @@ import eu.kanade.tachiyomi.ui.setting.track.BaseOAuthLoginActivity
|
|||||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||||
import eu.kanade.tachiyomi.util.CrashLogUtil
|
import eu.kanade.tachiyomi.util.CrashLogUtil
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.protobuf.ProtoBuf
|
||||||
import mihon.core.metro.IsDebugBuild
|
import mihon.core.metro.IsDebugBuild
|
||||||
import mihon.domain.extension.interactor.GetExtensionStoreCountAsFlow
|
import mihon.domain.extension.interactor.GetExtensionStoreCountAsFlow
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XML
|
||||||
import tachiyomi.domain.backup.service.BackupPreferences
|
import tachiyomi.domain.backup.service.BackupPreferences
|
||||||
import tachiyomi.domain.category.interactor.GetCategories
|
import tachiyomi.domain.category.interactor.GetCategories
|
||||||
import tachiyomi.domain.category.interactor.ResetCategoryFlags
|
import tachiyomi.domain.category.interactor.ResetCategoryFlags
|
||||||
@@ -101,10 +105,14 @@ interface AppGraph : ViewModelGraph {
|
|||||||
val trackerManager: TrackerManager
|
val trackerManager: TrackerManager
|
||||||
val extensionManager: ExtensionManager
|
val extensionManager: ExtensionManager
|
||||||
val chapterCache: ChapterCache
|
val chapterCache: ChapterCache
|
||||||
|
val coverCache: CoverCache
|
||||||
val downloadCache: DownloadCache
|
val downloadCache: DownloadCache
|
||||||
|
|
||||||
val json: Json
|
val json: Json
|
||||||
|
val protoBuf: ProtoBuf
|
||||||
|
val xml: XML
|
||||||
val networkHelper: NetworkHelper
|
val networkHelper: NetworkHelper
|
||||||
|
val javaScriptEngine: JavaScriptEngine
|
||||||
|
|
||||||
val getFavorites: GetFavorites
|
val getFavorites: GetFavorites
|
||||||
val getCategories: GetCategories
|
val getCategories: GetCategories
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package mihon.app.di.injekt
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.Context
|
||||||
|
import eu.kanade.tachiyomi.network.JavaScriptEngine
|
||||||
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.protobuf.ProtoBuf
|
||||||
|
import mihon.app.di.AppGraph
|
||||||
|
import mihon.core.metro.GraphProvider
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XML
|
||||||
|
import uy.kohesive.injekt.api.InjektModule
|
||||||
|
import uy.kohesive.injekt.api.InjektRegistrar
|
||||||
|
import uy.kohesive.injekt.api.InjektionException
|
||||||
|
import uy.kohesive.injekt.api.TypeReference
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
|
class MetroInjektRegistrar(
|
||||||
|
private val application: Application,
|
||||||
|
private val graphProvider: GraphProvider<AppGraph>,
|
||||||
|
) : InjektRegistrar {
|
||||||
|
|
||||||
|
private val graph: AppGraph inline get() = graphProvider.graph
|
||||||
|
|
||||||
|
private val bindings = mapOf<Type, () -> Any>(
|
||||||
|
Application::class.java to { application },
|
||||||
|
Context::class.java to { application },
|
||||||
|
|
||||||
|
Json::class.java to { graph.json },
|
||||||
|
ProtoBuf::class.java to { graph.protoBuf },
|
||||||
|
XML::class.java to { graph.xml },
|
||||||
|
|
||||||
|
NetworkHelper::class.java to { graph.networkHelper },
|
||||||
|
JavaScriptEngine::class.java to { graph.javaScriptEngine },
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun <R : Any> getInstance(forType: Type): R = getInstanceOrNull(forType)
|
||||||
|
?: throw InjektionException("$forType is not exposed to Injekt, add it to ${this::class.simpleName}")
|
||||||
|
|
||||||
|
override fun <R : Any> getInstanceOrElse(forType: Type, default: R): R = getInstanceOrNull(forType) ?: default
|
||||||
|
|
||||||
|
override fun <R : Any> getInstanceOrElse(forType: Type, default: () -> R): R =
|
||||||
|
getInstanceOrNull(forType) ?: default()
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
override fun <R : Any> getInstanceOrNull(forType: Type): R? = bindings[forType]?.invoke() as R?
|
||||||
|
|
||||||
|
override fun <R : Any, K : Any> getKeyedInstance(forType: Type, key: K): R = unsupported("Keyed injection")
|
||||||
|
|
||||||
|
override fun <R : Any, K : Any> getKeyedInstanceOrElse(forType: Type, key: K, default: R): R =
|
||||||
|
unsupported("Keyed injection")
|
||||||
|
|
||||||
|
override fun <R : Any, K : Any> getKeyedInstanceOrElse(forType: Type, key: K, default: () -> R): R =
|
||||||
|
unsupported("Keyed injection")
|
||||||
|
|
||||||
|
@Suppress("RedundantNullableReturnType")
|
||||||
|
override fun <R : Any, K : Any> getKeyedInstanceOrNull(forType: Type, key: K): R? = unsupported("Keyed injection")
|
||||||
|
|
||||||
|
override fun <R : Any> getLogger(expectedLoggerType: Type, byName: String): R = unsupported("Logger injection")
|
||||||
|
|
||||||
|
override fun <R : Any, T : Any> getLogger(expectedLoggerType: Type, forClass: Class<T>): R =
|
||||||
|
unsupported("Logger injection")
|
||||||
|
|
||||||
|
override fun <T : Any> addSingleton(forType: TypeReference<T>, singleInstance: T) = readOnly()
|
||||||
|
|
||||||
|
override fun <R : Any> addSingletonFactory(forType: TypeReference<R>, factoryCalledOnce: () -> R) = readOnly()
|
||||||
|
|
||||||
|
override fun <R : Any> addFactory(forType: TypeReference<R>, factoryCalledEveryTime: () -> R) = readOnly()
|
||||||
|
|
||||||
|
override fun <R : Any> addPerThreadFactory(forType: TypeReference<R>, factoryCalledOncePerThread: () -> R) =
|
||||||
|
readOnly()
|
||||||
|
|
||||||
|
override fun <R : Any, K : Any> addPerKeyFactory(forType: TypeReference<R>, factoryCalledPerKey: (K) -> R) =
|
||||||
|
readOnly()
|
||||||
|
|
||||||
|
override fun <R : Any, K : Any> addPerThreadPerKeyFactory(
|
||||||
|
forType: TypeReference<R>,
|
||||||
|
factoryCalledPerKeyPerThread: (K) -> R,
|
||||||
|
) = readOnly()
|
||||||
|
|
||||||
|
override fun <R : Any> addLoggerFactory(
|
||||||
|
forLoggerType: TypeReference<R>,
|
||||||
|
factoryByName: (String) -> R,
|
||||||
|
factoryByClass: (Class<Any>) -> R,
|
||||||
|
) = readOnly()
|
||||||
|
|
||||||
|
override fun <O : Any, T : O> addAlias(
|
||||||
|
existingRegisteredType: TypeReference<T>,
|
||||||
|
otherAncestorOrInterface: TypeReference<O>,
|
||||||
|
) = readOnly()
|
||||||
|
|
||||||
|
override fun <T : Any> hasFactory(forType: TypeReference<T>): Boolean = forType.type in bindings
|
||||||
|
|
||||||
|
override fun importModule(submodule: InjektModule) = readOnly()
|
||||||
|
|
||||||
|
private fun readOnly(): Nothing = throw UnsupportedOperationException("Mihon's Injekt instance is read-only")
|
||||||
|
|
||||||
|
private fun unsupported(feature: String): Nothing =
|
||||||
|
throw UnsupportedOperationException("$feature is not supported by Mihon's Injekt instance")
|
||||||
|
}
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user