Skip to content

Commit 5c1f520

Browse files
committed
migrate to Metro
1 parent dd03300 commit 5c1f520

11 files changed

Lines changed: 111 additions & 69 deletions

File tree

app/build.gradle.kts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ plugins {
33
alias(libs.plugins.google.ksp)
44
alias(libs.plugins.kotlin.compose)
55
alias(libs.plugins.kotlin.serialization)
6-
alias(libs.plugins.hilt.android)
6+
alias(libs.plugins.metro)
77
}
88

99
android {
@@ -12,7 +12,7 @@ android {
1212

1313
defaultConfig {
1414
applicationId = "com.thisisnotajoke.interview"
15-
minSdk = 26
15+
minSdk = 28
1616
targetSdk = 37
1717
versionCode = 1
1818
versionName = "1.0"
@@ -39,8 +39,6 @@ android {
3939
}
4040

4141
dependencies {
42-
ksp(libs.hilt.compiler)
43-
4442
implementation(libs.androidx.lifecycle.runtime.compose)
4543
implementation(libs.androidx.activity.compose)
4644
implementation(libs.androidx.lifecycle.viewmodel.compose)
@@ -52,10 +50,11 @@ dependencies {
5250
implementation(libs.coil.compose)
5351
implementation(libs.coil.network.okhttp)
5452
implementation(libs.kotlinx.serialization)
53+
implementation(libs.metrox.android)
54+
implementation(libs.metrox.viewmodel.compose)
5555
implementation(libs.square.retrofit)
5656
implementation(libs.square.retrofit.converter.kotlinx.serialization)
5757
implementation(libs.square.okhttp3)
58-
implementation(libs.hilt.android)
5958
testImplementation(libs.junit)
6059
androidTestImplementation(libs.androidx.junit)
6160
androidTestImplementation(libs.androidx.espresso.core)

app/src/main/java/com/thisisnotajoke/interview/MainActivity.kt

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,31 @@
11
package com.thisisnotajoke.interview
22

3+
import android.app.Activity
34
import android.os.Bundle
45
import androidx.activity.ComponentActivity
56
import androidx.activity.compose.setContent
67
import androidx.activity.enableEdgeToEdge
7-
import androidx.compose.foundation.layout.Box
8-
import androidx.compose.foundation.layout.fillMaxSize
9-
import androidx.compose.foundation.layout.padding
10-
import androidx.compose.foundation.layout.safeDrawingPadding
11-
import androidx.compose.material3.Scaffold
12-
import androidx.compose.ui.Modifier
13-
import com.thisisnotajoke.interview.repository.MobileInterviewRepository
14-
import com.thisisnotajoke.interview.ui.theme.InterviewTheme
15-
import com.thisisnotajoke.interview.view.PeopleList
16-
import dagger.hilt.android.AndroidEntryPoint
17-
import javax.inject.Inject
8+
import androidx.compose.runtime.CompositionLocalProvider
9+
import com.thisisnotajoke.interview.view.App
10+
import dev.zacsweers.metro.AppScope
11+
import dev.zacsweers.metro.ContributesIntoMap
12+
import dev.zacsweers.metro.Inject
13+
import dev.zacsweers.metro.binding
14+
import dev.zacsweers.metrox.android.ActivityKey
15+
import dev.zacsweers.metrox.viewmodel.LocalMetroViewModelFactory
16+
import dev.zacsweers.metrox.viewmodel.MetroViewModelFactory
1817

19-
@AndroidEntryPoint
20-
class MainActivity : ComponentActivity() {
21-
22-
@Inject
23-
lateinit var service: MobileInterviewRepository
18+
@ContributesIntoMap(AppScope::class, binding<Activity>())
19+
@ActivityKey
20+
@Inject
21+
class MainActivity(private val metroVmf: MetroViewModelFactory) : ComponentActivity() {
2422

2523
override fun onCreate(savedInstanceState: Bundle?) {
2624
super.onCreate(savedInstanceState)
2725
enableEdgeToEdge()
2826
setContent {
29-
InterviewTheme {
30-
Box(Modifier.safeDrawingPadding()) {
31-
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
32-
PeopleList(
33-
modifier = Modifier.padding(innerPadding)
34-
)
35-
}
36-
}
27+
CompositionLocalProvider(LocalMetroViewModelFactory provides metroVmf) {
28+
App()
3729
}
3830
}
3931
}
Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
package com.thisisnotajoke.interview
22

33
import android.app.Application
4-
import dagger.hilt.android.HiltAndroidApp
4+
import com.thisisnotajoke.interview.di.AppGraph
5+
import dev.zacsweers.metro.createGraph
6+
import dev.zacsweers.metrox.android.MetroAppComponentProviders
7+
import dev.zacsweers.metrox.android.MetroApplication
58

6-
@HiltAndroidApp
7-
class MainApplication : Application()
9+
class MainApplication : Application(), MetroApplication {
10+
private val appGraph by lazy { createGraph<AppGraph>() }
11+
override val appComponentProviders: MetroAppComponentProviders
12+
get() = appGraph
13+
}

app/src/main/java/com/thisisnotajoke/interview/PeopleListViewModel.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,18 @@ import androidx.lifecycle.viewModelScope
66
import com.thisisnotajoke.interview.model.People
77
import com.thisisnotajoke.interview.model.PeopleState
88
import com.thisisnotajoke.interview.repository.MobileInterviewRepository
9-
import dagger.hilt.android.lifecycle.HiltViewModel
9+
import dev.zacsweers.metro.AppScope
10+
import dev.zacsweers.metro.ContributesIntoMap
11+
import dev.zacsweers.metro.Inject
12+
import dev.zacsweers.metrox.viewmodel.ViewModelKey
1013
import kotlinx.coroutines.flow.MutableStateFlow
1114
import kotlinx.coroutines.flow.StateFlow
1215
import kotlinx.coroutines.launch
13-
import javax.inject.Inject
1416

15-
@HiltViewModel
16-
class PeopleListViewModel @Inject constructor(
17+
@Inject
18+
@ViewModelKey
19+
@ContributesIntoMap(AppScope::class)
20+
class PeopleListViewModel(
1721
private val service: MobileInterviewRepository
1822
) : ViewModel() {
1923

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.thisisnotajoke.interview.di
2+
3+
import com.thisisnotajoke.interview.repository.MobileInterviewRepository
4+
import dev.zacsweers.metro.AppScope
5+
import dev.zacsweers.metro.DependencyGraph
6+
import dev.zacsweers.metro.Provides
7+
import dev.zacsweers.metrox.android.MetroAppComponentProviders
8+
import dev.zacsweers.metrox.viewmodel.ViewModelGraph
9+
import kotlinx.serialization.json.Json
10+
import okhttp3.MediaType.Companion.toMediaType
11+
import retrofit2.Retrofit
12+
import retrofit2.converter.kotlinx.serialization.asConverterFactory
13+
14+
@DependencyGraph(AppScope::class)
15+
interface AppGraph : MetroAppComponentProviders, ViewModelGraph {
16+
17+
@Provides
18+
fun provideMobileInterviewService(): MobileInterviewRepository = Retrofit.Builder()
19+
.baseUrl("https://gist.githubusercontent.com/kurtisnelson/2c08d0b40d5f1f4326e5f5eed1b446fa/raw/1c5f9698a67dc554dfe6237f1b4d5c7371a6c3ff/")
20+
.addConverterFactory(
21+
Json.asConverterFactory(
22+
"application/json; charset=UTF8".toMediaType()))
23+
.build()
24+
.create(MobileInterviewRepository::class.java)
25+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.thisisnotajoke.interview.di
2+
3+
import androidx.lifecycle.ViewModel
4+
import dev.zacsweers.metro.AppScope
5+
import dev.zacsweers.metro.ContributesBinding
6+
import dev.zacsweers.metro.Inject
7+
import dev.zacsweers.metro.SingleIn
8+
import dev.zacsweers.metrox.viewmodel.ManualViewModelAssistedFactory
9+
import dev.zacsweers.metrox.viewmodel.MetroViewModelFactory
10+
import dev.zacsweers.metrox.viewmodel.ViewModelAssistedFactory
11+
import kotlin.reflect.KClass
12+
13+
@Inject
14+
@ContributesBinding(AppScope::class)
15+
@SingleIn(AppScope::class)
16+
class ViewModelFactory(
17+
override val viewModelProviders: Map<KClass<out ViewModel>, () -> ViewModel>,
18+
override val assistedFactoryProviders: Map<KClass<out ViewModel>, () -> ViewModelAssistedFactory>,
19+
override val manualAssistedFactoryProviders: Map<KClass<out ManualViewModelAssistedFactory>, () -> ManualViewModelAssistedFactory>,
20+
) : MetroViewModelFactory()

app/src/main/java/com/thisisnotajoke/interview/repository/RepositoryModule.kt

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.thisisnotajoke.interview.view
2+
3+
import androidx.compose.foundation.layout.Box
4+
import androidx.compose.foundation.layout.fillMaxSize
5+
import androidx.compose.foundation.layout.padding
6+
import androidx.compose.foundation.layout.safeDrawingPadding
7+
import androidx.compose.material3.Scaffold
8+
import androidx.compose.runtime.Composable
9+
import androidx.compose.ui.Modifier
10+
import com.thisisnotajoke.interview.ui.theme.InterviewTheme
11+
12+
@Composable
13+
fun App() {
14+
InterviewTheme {
15+
Box(Modifier.safeDrawingPadding()) {
16+
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
17+
PeopleList(
18+
modifier = Modifier.padding(innerPadding)
19+
)
20+
}
21+
}
22+
}
23+
}

app/src/main/java/com/thisisnotajoke/interview/view/PeopleList.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
1919
import androidx.lifecycle.viewmodel.compose.viewModel
2020
import com.thisisnotajoke.interview.PeopleListViewModel
2121
import com.thisisnotajoke.interview.model.PeopleState
22+
import dev.zacsweers.metrox.viewmodel.metroViewModel
2223

2324

24-
@OptIn(ExperimentalMaterial3Api::class)
2525
@Composable
2626
fun PeopleList(
2727
modifier: Modifier = Modifier,
28-
viewModel: PeopleListViewModel = viewModel(),
28+
viewModel: PeopleListViewModel = metroViewModel(),
2929
) {
3030
val response by viewModel.people.collectAsStateWithLifecycle()
3131
var isRefreshing by remember { mutableStateOf(false) }

build.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,5 @@ plugins {
33
alias(libs.plugins.android.application) apply false
44
alias(libs.plugins.kotlin.compose) apply false
55
alias(libs.plugins.google.ksp) apply false
6-
alias(libs.plugins.hilt.android) apply false
76
alias(libs.plugins.kotlin.serialization) apply false
87
}

0 commit comments

Comments
 (0)