Skip to content

Commit dbbb6c4

Browse files
committed
Merge branch 'develop' into BOOK-473-refactor/#229
2 parents c0c35a5 + 67fd699 commit dbbb6c4

File tree

120 files changed

+913
-887
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

120 files changed

+913
-887
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777

7878
- [Circuit](https://github.com/slackhq/circuit)
7979
- ~~Google ML Kit~~ Google Cloud Vision
80-
- Dagger Hilt
80+
- ~~Dagger Hilt~~ Metro
8181
- Retrofit, OkHttp3
8282
- Lottie-Compose
8383
- Firebase(Analytics, Crashlytics, Remote Config)

app/build.gradle.kts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
@file:Suppress("INLINE_FROM_HIGHER_PLATFORM")
22

33
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
4+
import com.google.devtools.ksp.gradle.KspExtension
5+
import org.gradle.kotlin.dsl.configure
46
import java.util.Properties
57

68
plugins {
79
alias(libs.plugins.booket.android.application)
810
alias(libs.plugins.booket.android.application.compose)
9-
alias(libs.plugins.booket.android.hilt)
1011
alias(libs.plugins.booket.android.firebase)
12+
alias(libs.plugins.metro)
13+
alias(libs.plugins.ksp)
1114
}
1215

1316
android {
@@ -63,8 +66,8 @@ composeStabilityAnalyzer {
6366
enabled.set(true)
6467
}
6568

66-
ksp {
67-
arg("circuit.codegen.mode", "hilt")
69+
extensions.configure<KspExtension> {
70+
arg("circuit.codegen.mode", "metro")
6871
}
6972

7073
dependencies {
@@ -75,6 +78,7 @@ dependencies {
7578
projects.core.datastore.api,
7679
projects.core.datastore.impl,
7780
projects.core.designsystem,
81+
projects.core.di,
7882
projects.core.model,
7983
projects.core.network,
8084
projects.core.ui,

app/src/main/AndroidManifest.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
<application
1414
android:name=".BooketApplication"
15+
android:appComponentFactory="com.ninecraft.booket.di.MetroAppComponentFactory"
1516
android:allowBackup="true"
1617
android:dataExtractionRules="@xml/data_extraction_rules"
1718
android:fullBackupContent="@xml/backup_rules"
@@ -21,7 +22,8 @@
2122
android:roundIcon="@mipmap/ic_launcher_round"
2223
android:supportsRtl="true"
2324
android:theme="@style/Theme.Booket"
24-
tools:targetApi="31">
25+
tools:targetApi="31"
26+
tools:replace="android:appComponentFactory">
2527

2628
<provider
2729
android:name="androidx.startup.InitializationProvider"

app/src/main/kotlin/com/ninecraft/booket/BooketApplication.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ import coil.ImageLoader
55
import coil.ImageLoaderFactory
66
import coil.disk.DiskCache
77
import coil.util.DebugLogger
8-
import dagger.hilt.android.HiltAndroidApp
8+
import com.ninecraft.booket.di.AppGraph
9+
import dev.zacsweers.metro.createGraphFactory
910

10-
@HiltAndroidApp
1111
class BooketApplication : Application(), ImageLoaderFactory {
12+
13+
val appGraph by lazy { createGraphFactory<AppGraph.Factory>().create(this) }
14+
1215
override fun newImageLoader(): ImageLoader {
1316
return ImageLoader.Builder(this)
1417
.diskCache {

app/src/main/kotlin/com/ninecraft/booket/ReedFirebaseMessagingService.kt

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.ninecraft.booket
33
import android.app.NotificationChannel
44
import android.app.NotificationManager
55
import android.app.PendingIntent
6+
import android.app.Service
67
import android.content.Context
78
import android.content.Intent
89
import androidx.core.app.NotificationCompat
@@ -11,20 +12,24 @@ import com.google.firebase.messaging.FirebaseMessagingService
1112
import com.google.firebase.messaging.RemoteMessage
1213
import com.ninecraft.booket.core.data.api.repository.UserRepository
1314
import com.ninecraft.booket.core.designsystem.R
15+
import com.ninecraft.booket.core.di.ServiceKey
1416
import com.ninecraft.booket.feature.main.MainActivity
15-
import dagger.hilt.android.AndroidEntryPoint
17+
import dev.zacsweers.metro.AppScope
18+
import dev.zacsweers.metro.ContributesIntoMap
19+
import dev.zacsweers.metro.Inject
20+
import dev.zacsweers.metro.binding
1621
import kotlinx.coroutines.CoroutineScope
1722
import kotlinx.coroutines.Dispatchers
1823
import kotlinx.coroutines.SupervisorJob
1924
import kotlinx.coroutines.cancel
2025
import kotlinx.coroutines.launch
21-
import javax.inject.Inject
2226

23-
@AndroidEntryPoint
24-
class ReedFirebaseMessagingService : FirebaseMessagingService() {
25-
26-
@Inject
27-
lateinit var userRepository: UserRepository
27+
@ContributesIntoMap(AppScope::class, binding = binding<Service>())
28+
@ServiceKey(ReedFirebaseMessagingService::class)
29+
@Inject
30+
class ReedFirebaseMessagingService(
31+
private val userRepository: UserRepository,
32+
) : FirebaseMessagingService() {
2833

2934
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
3035

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.ninecraft.booket.di
2+
3+
import android.app.Activity
4+
import android.app.Service
5+
import android.content.Context
6+
import com.ninecraft.booket.core.di.ApplicationContext
7+
import com.ninecraft.booket.core.di.DataScope
8+
import dev.zacsweers.metro.AppScope
9+
import dev.zacsweers.metro.DependencyGraph
10+
import dev.zacsweers.metro.Multibinds
11+
import dev.zacsweers.metro.Provider
12+
import dev.zacsweers.metro.Provides
13+
import kotlin.reflect.KClass
14+
15+
@DependencyGraph(
16+
scope = AppScope::class,
17+
additionalScopes = [DataScope::class],
18+
)
19+
interface AppGraph {
20+
21+
@Multibinds(allowEmpty = true)
22+
val activityProviders: Map<KClass<out Activity>, Provider<Activity>>
23+
24+
@Multibinds(allowEmpty = true)
25+
val serviceProviders: Map<KClass<out Service>, Provider<Service>>
26+
27+
@DependencyGraph.Factory
28+
fun interface Factory {
29+
fun create(@ApplicationContext @Provides context: Context): AppGraph
30+
}
31+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.ninecraft.booket.di
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.text.BasicText
5+
import androidx.compose.ui.graphics.Color
6+
import androidx.compose.ui.text.TextStyle
7+
import com.slack.circuit.foundation.Circuit
8+
import com.slack.circuit.foundation.LocalCircuit
9+
import com.slack.circuit.runtime.Navigator
10+
import com.slack.circuit.runtime.presenter.Presenter
11+
import com.slack.circuit.runtime.ui.Ui
12+
import dev.zacsweers.metro.AppScope
13+
import dev.zacsweers.metro.ContributesTo
14+
import dev.zacsweers.metro.Multibinds
15+
import dev.zacsweers.metro.Provides
16+
17+
@ContributesTo(AppScope::class)
18+
interface CircuitGraph {
19+
20+
@Multibinds(allowEmpty = true)
21+
fun presenterFactories(): Set<Presenter.Factory>
22+
23+
@Multibinds(allowEmpty = true)
24+
fun uiFactories(): Set<Ui.Factory>
25+
26+
@Provides
27+
fun provideCircuit(
28+
presenterFactories: Set<Presenter.Factory>,
29+
uiFactories: Set<Ui.Factory>,
30+
): Circuit {
31+
return Circuit.Builder()
32+
.addPresenterFactories(presenterFactories)
33+
.addUiFactories(uiFactories)
34+
.setAnimatedNavDecoratorFactory(CrossFadeNavDecoratorFactory())
35+
.setOnUnavailableContent { screen, modifier ->
36+
val circuit = LocalCircuit.current
37+
BasicText(
38+
text = """
39+
Route not available: ${screen.javaClass.name}.
40+
Presenter: ${circuit?.presenter(screen, Navigator.NoOp)?.javaClass}
41+
UI: ${circuit?.ui(screen)?.javaClass}
42+
All presenterFactories: ${circuit?.newBuilder()?.presenterFactories}
43+
All uiFactories: ${circuit?.newBuilder()?.uiFactories}
44+
"""
45+
.trimIndent(),
46+
modifier = modifier.background(Color.Red),
47+
style = TextStyle(color = Color.Yellow),
48+
)
49+
}
50+
.build()
51+
}
52+
}

app/src/main/kotlin/com/ninecraft/booket/di/CircuitModule.kt

Lines changed: 0 additions & 34 deletions
This file was deleted.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.ninecraft.booket.di
2+
3+
import android.app.Activity
4+
import android.app.Application
5+
import android.app.Service
6+
import android.content.Intent
7+
import androidx.annotation.Keep
8+
import androidx.core.app.AppComponentFactory
9+
import com.ninecraft.booket.BooketApplication
10+
import dev.zacsweers.metro.Provider
11+
import kotlin.reflect.KClass
12+
13+
/**
14+
* AppComponentFactory that uses Metro for constructor injection of Activities and Services.
15+
*
16+
* Requires minSdk 28+. For lower versions, use manual graph access.
17+
*/
18+
@Keep
19+
class MetroAppComponentFactory : AppComponentFactory() {
20+
21+
private inline fun <reified T : Any> getInstance(
22+
cl: ClassLoader,
23+
className: String,
24+
providers: Map<KClass<out T>, Provider<T>>,
25+
): T? {
26+
val clazz = Class.forName(className, false, cl).asSubclass(T::class.java)
27+
val modelProvider = providers[clazz.kotlin] ?: return null
28+
return modelProvider()
29+
}
30+
31+
override fun instantiateActivityCompat(
32+
cl: ClassLoader,
33+
className: String,
34+
intent: Intent?,
35+
): Activity {
36+
return getInstance(cl, className, activityProviders)
37+
?: super.instantiateActivityCompat(cl, className, intent)
38+
}
39+
40+
override fun instantiateServiceCompat(
41+
cl: ClassLoader,
42+
className: String,
43+
intent: Intent?,
44+
): Service {
45+
return getInstance(cl, className, serviceProviders)
46+
?: super.instantiateServiceCompat(cl, className, intent)
47+
}
48+
49+
override fun instantiateApplicationCompat(cl: ClassLoader, className: String): Application {
50+
val app = super.instantiateApplicationCompat(cl, className)
51+
val appGraph = (app as BooketApplication).appGraph
52+
activityProviders = appGraph.activityProviders
53+
serviceProviders = appGraph.serviceProviders
54+
return app
55+
}
56+
57+
companion object {
58+
private lateinit var activityProviders: Map<KClass<out Activity>, Provider<Activity>>
59+
private lateinit var serviceProviders: Map<KClass<out Service>, Provider<Service>>
60+
}
61+
}

build-logic/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ dependencies {
99
compileOnly(libs.android.gradle.plugin)
1010
compileOnly(libs.kotlin.gradle.plugin)
1111
compileOnly(libs.compose.compiler.gradle.plugin)
12+
compileOnly(libs.ksp.gradle.plugin)
1213
implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
1314
}
1415

@@ -20,7 +21,6 @@ gradlePlugin {
2021
"android.library.compose" to "AndroidLibraryComposeConventionPlugin",
2122
"android.feature" to "AndroidFeatureConventionPlugin",
2223
"android.firebase" to "AndroidFirebaseConventionPlugin",
23-
"android.hilt" to "AndroidHiltConventionPlugin",
2424
"android.retrofit" to "AndroidRetrofitConventionPlugin",
2525
"jvm.library" to "JvmLibraryConventionPlugin",
2626
"kotlin.library.serialization" to "KotlinLibrarySerializationConventionPlugin",

0 commit comments

Comments
 (0)