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()
|
||||
}
|
||||
|
||||
|
||||
-5
@@ -40,7 +40,6 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.app.ActivityCompat
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
import eu.kanade.domain.ui.model.AppTheme
|
||||
import eu.kanade.presentation.manga.components.MangaCover
|
||||
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 mihon.icons.materialsymbols.MaterialSymbols
|
||||
import mihon.icons.materialsymbols.roundedfilled.CheckCircle
|
||||
import tachiyomi.core.common.preference.InMemoryPreferenceStore
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.util.secondaryItemAlpha
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.fullType
|
||||
|
||||
@Composable
|
||||
internal fun AppThemePreferenceWidget(
|
||||
@@ -261,7 +257,6 @@ fun AppThemePreviewItem(
|
||||
@Composable
|
||||
private fun AppThemesListPreview() {
|
||||
var appTheme by remember { mutableStateOf(AppTheme.DEFAULT) }
|
||||
Injekt.addSingleton(fullType<UiPreferences>(), UiPreferences(InMemoryPreferenceStore()))
|
||||
TachiyomiTheme(appTheme = appTheme) {
|
||||
Surface {
|
||||
AppThemesList(
|
||||
|
||||
@@ -52,7 +52,7 @@ import logcat.AndroidLogcatLogger
|
||||
import logcat.LogPriority
|
||||
import logcat.LogcatLogger
|
||||
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.migration.Migration
|
||||
import mihon.core.migration.Migrator
|
||||
@@ -66,7 +66,7 @@ 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.addSingleton
|
||||
import uy.kohesive.injekt.api.InjektScope
|
||||
import java.security.Security
|
||||
|
||||
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 injektMetroInteropModule: MetroInteropModule
|
||||
|
||||
@Inject private lateinit var migrations: Set<Migration>
|
||||
|
||||
private val disableIncognitoReceiver = DisableIncognitoReceiver()
|
||||
@@ -110,8 +108,8 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
|
||||
if (packageName != process) WebView.setDataDirectorySuffix(process)
|
||||
}
|
||||
|
||||
Injekt = InjektScope(MetroInjektRegistrar(application = this, graphProvider = this))
|
||||
graph.inject(this)
|
||||
setupInjekt()
|
||||
|
||||
TelemetryConfig.init(applicationContext)
|
||||
|
||||
@@ -185,12 +183,6 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
|
||||
initializeMigrator()
|
||||
}
|
||||
|
||||
private fun setupInjekt() {
|
||||
Injekt.addSingleton<Application>(this)
|
||||
Injekt.addSingleton<Context>(this)
|
||||
Injekt.importModule(injektMetroInteropModule)
|
||||
}
|
||||
|
||||
private fun initializeMigrator() {
|
||||
val preference = preferenceStore.getInt(Preference.appStateKey("last_version_code"), 0)
|
||||
logcat {
|
||||
|
||||
@@ -5,7 +5,7 @@ import android.net.Uri
|
||||
import androidx.annotation.CallSuper
|
||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||
import eu.kanade.tachiyomi.extension.model.InstallStep
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import mihon.app.di.appGraph
|
||||
import java.util.Collections
|
||||
import java.util.concurrent.CopyOnWriteArraySet
|
||||
import kotlin.concurrent.atomics.AtomicReference
|
||||
@@ -17,7 +17,7 @@ import kotlin.concurrent.atomics.ExperimentalAtomicApi
|
||||
@OptIn(ExperimentalAtomicApi::class)
|
||||
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 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.restore.BackupRestoreJob
|
||||
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.DownloadJob
|
||||
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.extension.ExtensionManager
|
||||
import eu.kanade.tachiyomi.extension.util.ExtensionInstallActivity
|
||||
import eu.kanade.tachiyomi.network.JavaScriptEngine
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.network.NetworkPreferences
|
||||
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.util.CrashLogUtil
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.protobuf.ProtoBuf
|
||||
import mihon.core.metro.IsDebugBuild
|
||||
import mihon.domain.extension.interactor.GetExtensionStoreCountAsFlow
|
||||
import nl.adaptivity.xmlutil.serialization.XML
|
||||
import tachiyomi.domain.backup.service.BackupPreferences
|
||||
import tachiyomi.domain.category.interactor.GetCategories
|
||||
import tachiyomi.domain.category.interactor.ResetCategoryFlags
|
||||
@@ -101,10 +105,14 @@ interface AppGraph : ViewModelGraph {
|
||||
val trackerManager: TrackerManager
|
||||
val extensionManager: ExtensionManager
|
||||
val chapterCache: ChapterCache
|
||||
val coverCache: CoverCache
|
||||
val downloadCache: DownloadCache
|
||||
|
||||
val json: Json
|
||||
val protoBuf: ProtoBuf
|
||||
val xml: XML
|
||||
val networkHelper: NetworkHelper
|
||||
val javaScriptEngine: JavaScriptEngine
|
||||
|
||||
val getFavorites: GetFavorites
|
||||
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