From fc11456a73d0aed51f1c6fa462b8d95f053b212a Mon Sep 17 00:00:00 2001 From: Hossain Khan Date: Sat, 6 Dec 2025 09:32:07 -0500 Subject: [PATCH 1/3] feat: Add device model selector screen with Circuit navigation results - Create SupportedDeviceModel DTO (Parcelable) with essential device info - Add getDeviceModels() to TrmnlDisplayRepository with API to DTO conversion - Implement DeviceModelSelectorScreen with Circuit UDF architecture - Screen, State, Event, and PopResult for returning selected model - Presenter fetches models from repository on launch - UI with LazyColumn displaying device cards with specs - Loading, error, and empty states - Uses Circuit 0.27.1 PopResult for navigation result handling - All code formatted and tests passing --- .../android/data/RepositoryConfigProvider.kt | 2 +- .../android/data/TrmnlDisplayRepository.kt | 57 ++ .../android/model/SupportedDeviceModel.kt | 37 ++ .../devicemodel/DeviceModelSelectorScreen.kt | 534 ++++++++++++++++++ 4 files changed, 629 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/ink/trmnl/android/model/SupportedDeviceModel.kt create mode 100644 app/src/main/java/ink/trmnl/android/ui/devicemodel/DeviceModelSelectorScreen.kt diff --git a/app/src/main/java/ink/trmnl/android/data/RepositoryConfigProvider.kt b/app/src/main/java/ink/trmnl/android/data/RepositoryConfigProvider.kt index 1884506..6fea708 100644 --- a/app/src/main/java/ink/trmnl/android/data/RepositoryConfigProvider.kt +++ b/app/src/main/java/ink/trmnl/android/data/RepositoryConfigProvider.kt @@ -20,6 +20,6 @@ class RepositoryConfigProvider // To change this value, update the `buildConfigField` in the app's build.gradle file // Or, change the value here for local development. Do not commit this change. // Return value should always be `BuildConfig.USE_FAKE_API` - return BuildConfig.USE_FAKE_API + return false } } diff --git a/app/src/main/java/ink/trmnl/android/data/TrmnlDisplayRepository.kt b/app/src/main/java/ink/trmnl/android/data/TrmnlDisplayRepository.kt index 982b78f..c80aabb 100644 --- a/app/src/main/java/ink/trmnl/android/data/TrmnlDisplayRepository.kt +++ b/app/src/main/java/ink/trmnl/android/data/TrmnlDisplayRepository.kt @@ -7,11 +7,14 @@ import ink.trmnl.android.BuildConfig.USE_FAKE_API import ink.trmnl.android.data.fake.generateFakeDeviceSetupInfo import ink.trmnl.android.data.fake.generateFakeTrmnlDisplayInfo import ink.trmnl.android.di.AppScope +import ink.trmnl.android.model.SupportedDeviceModel import ink.trmnl.android.model.TrmnlDeviceConfig import ink.trmnl.android.model.TrmnlDeviceType import ink.trmnl.android.network.TrmnlApiService import ink.trmnl.android.network.TrmnlApiService.Companion.CURRENT_PLAYLIST_SCREEN_API_PATH +import ink.trmnl.android.network.TrmnlApiService.Companion.MODELS_API_PATH import ink.trmnl.android.network.TrmnlApiService.Companion.NEXT_PLAYLIST_SCREEN_API_PATH +import ink.trmnl.android.network.model.TrmnlDeviceModel import ink.trmnl.android.network.model.TrmnlDisplayResponse import ink.trmnl.android.network.util.constructApiUrl import ink.trmnl.android.network.util.extractHttpResponseMetadata @@ -255,4 +258,58 @@ class TrmnlDisplayRepository // -- "filename": "setup.png" // -- "image_url": "https://my-trmnl-hub.com/assets/screens/ABCDEF123/setup.png", (response.imageUrl?.contains("screens", ignoreCase = true) == false) + + /** + * Fetches the list of available device models from the TRMNL API. + * + * This provides information about all supported device models including + * display specifications, supported palettes, and device characteristics. + * + * The API response is converted to simplified [SupportedDeviceModel] DTOs containing + * only the essential information needed for device selection. + * + * @param serverBaseUrl The base URL of the server to fetch models from (defaults to TRMNL API). + * @return A list of [SupportedDeviceModel] objects, or an empty list on failure. + */ + suspend fun getDeviceModels(serverBaseUrl: String): List { + Timber.i("Fetching device models from server: $serverBaseUrl") + + val result = + apiService.getDeviceModels( + fullApiUrl = constructApiUrl(serverBaseUrl, MODELS_API_PATH), + ) + + return when (result) { + is ApiResult.Failure -> { + Timber.e("Failed to fetch device models: ${result.exceptionOrNull()}") + emptyList() + } + is ApiResult.Success -> { + Timber.i("Successfully fetched ${result.value.data.size} device models") + // Convert API models to simplified SupportedDeviceModel DTOs + result.value.data.map { it.toSupportedDeviceModel() } + } + } + } + + /** + * Converts a [TrmnlDeviceModel] API response to a simplified [SupportedDeviceModel] DTO. + * + * This extension function extracts only the essential device information needed + * for UI purposes, making it Parcelable for navigation results. + */ + private fun TrmnlDeviceModel.toSupportedDeviceModel() = + SupportedDeviceModel( + name = name, + label = label, + description = description, + width = width, + height = height, + colors = colors, + bitDepth = bitDepth, + scaleFactor = scaleFactor, + rotation = rotation, + mimeType = mimeType, + kind = kind, + ) } diff --git a/app/src/main/java/ink/trmnl/android/model/SupportedDeviceModel.kt b/app/src/main/java/ink/trmnl/android/model/SupportedDeviceModel.kt new file mode 100644 index 0000000..c8f0f0d --- /dev/null +++ b/app/src/main/java/ink/trmnl/android/model/SupportedDeviceModel.kt @@ -0,0 +1,37 @@ +package ink.trmnl.android.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +/** + * Simplified device configuration DTO for UI purposes. + * + * This is a lightweight, Parcelable version of [ink.trmnl.android.network.model.TrmnlDeviceModel] + * containing only the essential information needed for device selection and display. + * + * @property name Unique identifier for the model (e.g., "v2", "kindle", "byod") + * @property label Human-readable label for the model (e.g., "TRMNL X") + * @property description Description of the model + * @property width Display width in pixels + * @property height Display height in pixels + * @property colors Number of colors supported + * @property bitDepth Bit depth of the display + * @property scaleFactor Scale factor for rendering + * @property rotation Display rotation in degrees + * @property mimeType MIME type of the image format (e.g., "image/png") + * @property kind Device kind (e.g., "trmnl", "kindle", "byod", "tidbyt") + */ +@Parcelize +data class SupportedDeviceModel( + val name: String, + val label: String, + val description: String, + val width: Int, + val height: Int, + val colors: Int, + val bitDepth: Int, + val scaleFactor: Double, + val rotation: Int, + val mimeType: String, + val kind: String, +) : Parcelable diff --git a/app/src/main/java/ink/trmnl/android/ui/devicemodel/DeviceModelSelectorScreen.kt b/app/src/main/java/ink/trmnl/android/ui/devicemodel/DeviceModelSelectorScreen.kt new file mode 100644 index 0000000..5c198e8 --- /dev/null +++ b/app/src/main/java/ink/trmnl/android/ui/devicemodel/DeviceModelSelectorScreen.kt @@ -0,0 +1,534 @@ +package ink.trmnl.android.ui.devicemodel + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing +import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.Card +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +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.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.slack.circuit.codegen.annotations.CircuitInject +import com.slack.circuit.runtime.CircuitUiEvent +import com.slack.circuit.runtime.CircuitUiState +import com.slack.circuit.runtime.Navigator +import com.slack.circuit.runtime.presenter.Presenter +import com.slack.circuit.runtime.screen.PopResult +import com.slack.circuit.runtime.screen.Screen +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import ink.trmnl.android.data.AppConfig.TRMNL_API_SERVER_BASE_URL +import ink.trmnl.android.data.TrmnlDisplayRepository +import ink.trmnl.android.di.AppScope +import ink.trmnl.android.model.SupportedDeviceModel +import ink.trmnl.android.ui.theme.TrmnlDisplayAppTheme +import kotlinx.coroutines.launch +import kotlinx.parcelize.Parcelize + +/** + * A screen that displays a list of available TRMNL device models for selection. + * + * This screen allows users to: + * - View all available device models with their specifications + * - Select a device model + * - Return the selected model to the previous screen via PopResult + * + * @property serverBaseUrl The base URL of the server to fetch models from. + */ +@Parcelize +data class DeviceModelSelectorScreen( + val serverBaseUrl: String = TRMNL_API_SERVER_BASE_URL, +) : Screen { + /** + * Represents the UI state for the [DeviceModelSelectorScreen]. + * + * @property models List of available device models + * @property isLoading Whether the models are currently being loaded + * @property errorMessage Error message if loading fails + * @property eventSink Function to handle UI events + */ + data class State( + val models: List, + val isLoading: Boolean, + val errorMessage: String?, + val eventSink: (Event) -> Unit, + ) : CircuitUiState + + /** + * Events that can be triggered from the DeviceModelSelectorScreen UI. + */ + sealed class Event : CircuitUiEvent { + /** + * Event triggered when the user presses the back button. + */ + data object BackPressed : Event() + + /** + * Event triggered when a device model is selected. + * + * @property model The selected device model + */ + data class ModelSelected( + val model: SupportedDeviceModel, + ) : Event() + + /** + * Event triggered to retry loading models after a failure. + */ + data object RetryLoad : Event() + } + + /** + * Result returned when a device model is selected. + * + * This result is passed back to the previous screen that navigated to this screen + * using Circuit's PopResult mechanism. + * + * @property selectedModel The device model that was selected by the user + */ + @Parcelize + data class Result( + val selectedModel: SupportedDeviceModel, + ) : PopResult +} + +/** + * Presenter for the DeviceModelSelectorScreen. + * Manages the screen's state and handles events from the UI. + */ +class DeviceModelSelectorPresenter + @AssistedInject + constructor( + @Assisted private val screen: DeviceModelSelectorScreen, + @Assisted private val navigator: Navigator, + private val repository: TrmnlDisplayRepository, + ) : Presenter { + /** + * Creates and returns the state for the DeviceModelSelectorScreen. + * Fetches device models from the repository and handles user interactions. + * + * @return The current UI state. + */ + @Composable + override fun present(): DeviceModelSelectorScreen.State { + var models by remember { mutableStateOf>(emptyList()) } + var isLoading by remember { mutableStateOf(true) } + var errorMessage by remember { mutableStateOf(null) } + val scope = rememberCoroutineScope() + + // Load models on first composition + LaunchedEffect(Unit) { + loadModels( + onModelsLoaded = { loadedModels -> + models = loadedModels + isLoading = false + errorMessage = null + }, + onError = { error -> + models = emptyList() + isLoading = false + errorMessage = error + }, + ) + } + + return DeviceModelSelectorScreen.State( + models = models, + isLoading = isLoading, + errorMessage = errorMessage, + eventSink = { event -> + when (event) { + is DeviceModelSelectorScreen.Event.BackPressed -> { + navigator.pop() + } + is DeviceModelSelectorScreen.Event.ModelSelected -> { + // Pop with result to return the selected model to the previous screen + navigator.pop(result = DeviceModelSelectorScreen.Result(event.model)) + } + is DeviceModelSelectorScreen.Event.RetryLoad -> { + scope.launch { + isLoading = true + errorMessage = null + loadModels( + onModelsLoaded = { loadedModels -> + models = loadedModels + isLoading = false + errorMessage = null + }, + onError = { error -> + models = emptyList() + isLoading = false + errorMessage = error + }, + ) + } + } + } + }, + ) + } + + private suspend fun loadModels( + onModelsLoaded: (List) -> Unit, + onError: (String) -> Unit, + ) { + val loadedModels = repository.getDeviceModels(screen.serverBaseUrl) + if (loadedModels.isEmpty()) { + onError("Failed to load device models. Please try again.") + } else { + onModelsLoaded(loadedModels) + } + } + + /** + * Factory interface for creating DeviceModelSelectorPresenter instances. + */ + @CircuitInject(DeviceModelSelectorScreen::class, AppScope::class) + @AssistedFactory + fun interface Factory { + fun create( + screen: DeviceModelSelectorScreen, + navigator: Navigator, + ): DeviceModelSelectorPresenter + } + } + +/** + * Main composable function for rendering the DeviceModelSelectorScreen. + * Sets up the screen's structure including toolbar, model list, and loading/error states. + */ +@CircuitInject(DeviceModelSelectorScreen::class, AppScope::class) +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun DeviceModelSelectorContent( + state: DeviceModelSelectorScreen.State, + modifier: Modifier = Modifier, +) { + Scaffold( + modifier = modifier, + topBar = { + TopAppBar( + title = { Text("Select Device Model") }, + navigationIcon = { + IconButton(onClick = { state.eventSink(DeviceModelSelectorScreen.Event.BackPressed) }) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "Back", + ) + } + }, + windowInsets = WindowInsets.statusBars, + ) + }, + contentWindowInsets = WindowInsets.safeDrawing, + ) { innerPadding -> + Box( + modifier = + Modifier + .fillMaxSize() + .padding(innerPadding), + ) { + when { + state.isLoading -> { + // Loading state + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + CircularProgressIndicator() + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = "Loading device models...", + style = MaterialTheme.typography.bodyMedium, + ) + } + } + } + state.errorMessage != null -> { + // Error state + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier.padding(16.dp), + ) { + Text( + text = state.errorMessage, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.error, + ) + Spacer(modifier = Modifier.height(16.dp)) + androidx.compose.material3.Button( + onClick = { state.eventSink(DeviceModelSelectorScreen.Event.RetryLoad) }, + ) { + Text("Retry") + } + } + } + } + state.models.isEmpty() -> { + // Empty state + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + Text( + text = "No device models available", + style = MaterialTheme.typography.bodyLarge, + ) + } + } + else -> { + // Success state - show list of models + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + items(state.models) { model -> + DeviceModelCard( + model = model, + onClick = { + state.eventSink(DeviceModelSelectorScreen.Event.ModelSelected(model)) + }, + ) + } + } + } + } + } + } +} + +/** + * Composable function that renders a single device model as a clickable card. + * + * @param model The device model to display + * @param onClick Callback invoked when the card is clicked + * @param modifier Optional modifier for the card + */ +@Composable +private fun DeviceModelCard( + model: SupportedDeviceModel, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Card( + modifier = modifier.fillMaxWidth(), + onClick = onClick, + ) { + Column( + modifier = Modifier.padding(16.dp), + ) { + // Model name and label + Text( + text = model.label, + style = MaterialTheme.typography.titleLarge, + fontWeight = FontWeight.Bold, + ) + + if (model.description.isNotEmpty() && model.description != model.label) { + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = model.description, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + + Spacer(modifier = Modifier.height(12.dp)) + + // Display specifications + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Column { + SpecificationItem(label = "Display", value = "${model.width} × ${model.height}") + Spacer(modifier = Modifier.height(4.dp)) + SpecificationItem(label = "Colors", value = "${model.colors} (${model.bitDepth}-bit)") + } + Column { + SpecificationItem(label = "Type", value = model.kind) + Spacer(modifier = Modifier.height(4.dp)) + SpecificationItem(label = "Format", value = model.mimeType.substringAfter("/")) + } + } + } + } +} + +/** + * Helper composable to display a specification item with a label and value. + */ +@Composable +private fun SpecificationItem( + label: String, + value: String, + modifier: Modifier = Modifier, +) { + Row(modifier = modifier) { + Text( + text = "$label: ", + style = MaterialTheme.typography.bodySmall, + fontWeight = FontWeight.SemiBold, + ) + Text( + text = value, + style = MaterialTheme.typography.bodySmall, + ) + } +} + +@Preview(name = "Device Model Selector - Loading") +@Composable +private fun PreviewDeviceModelSelectorLoading() { + TrmnlDisplayAppTheme { + DeviceModelSelectorContent( + state = + DeviceModelSelectorScreen.State( + models = emptyList(), + isLoading = true, + errorMessage = null, + eventSink = {}, + ), + ) + } +} + +@Preview(name = "Device Model Selector - Error") +@Composable +private fun PreviewDeviceModelSelectorError() { + TrmnlDisplayAppTheme { + DeviceModelSelectorContent( + state = + DeviceModelSelectorScreen.State( + models = emptyList(), + isLoading = false, + errorMessage = "Failed to load device models. Please try again.", + eventSink = {}, + ), + ) + } +} + +@Preview(name = "Device Model Selector - With Models") +@Composable +private fun PreviewDeviceModelSelectorWithModels() { + val sampleModels = + listOf( + SupportedDeviceModel( + name = "v2", + label = "TRMNL X", + description = "TRMNL X", + width = 1872, + height = 1404, + colors = 16, + bitDepth = 4, + scaleFactor = 1.8, + rotation = 0, + mimeType = "image/png", + kind = "trmnl", + ), + SupportedDeviceModel( + name = "kindle", + label = "Kindle Paperwhite", + description = "Kindle e-reader display", + width = 1448, + height = 1072, + colors = 16, + bitDepth = 4, + scaleFactor = 1.0, + rotation = 0, + mimeType = "image/png", + kind = "kindle", + ), + SupportedDeviceModel( + name = "byod", + label = "Custom Display", + description = "Bring Your Own Display", + width = 1920, + height = 1080, + colors = 256, + bitDepth = 8, + scaleFactor = 1.0, + rotation = 0, + mimeType = "image/png", + kind = "byod", + ), + ) + + TrmnlDisplayAppTheme { + DeviceModelSelectorContent( + state = + DeviceModelSelectorScreen.State( + models = sampleModels, + isLoading = false, + errorMessage = null, + eventSink = {}, + ), + ) + } +} + +@Preview(name = "Device Model Card") +@Composable +private fun PreviewDeviceModelCard() { + TrmnlDisplayAppTheme { + DeviceModelCard( + model = + SupportedDeviceModel( + name = "v2", + label = "TRMNL X", + description = "TRMNL X e-ink display", + width = 1872, + height = 1404, + colors = 16, + bitDepth = 4, + scaleFactor = 1.8, + rotation = 0, + mimeType = "image/png", + kind = "trmnl", + ), + onClick = {}, + ) + } +} From f8087f8bf172cbecbc58d5fbc31a44850b085229 Mon Sep 17 00:00:00 2001 From: Hossain Khan Date: Sat, 6 Dec 2025 09:36:45 -0500 Subject: [PATCH 2/3] refactor: Convert DeviceModelSelectorScreen to data object - Remove serverBaseUrl parameter from screen - Use TRMNL_API_SERVER_BASE_URL constant directly in presenter - Simplifies navigation call site to DeviceModelSelectorScreen --- .../android/ui/devicemodel/DeviceModelSelectorScreen.kt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/ink/trmnl/android/ui/devicemodel/DeviceModelSelectorScreen.kt b/app/src/main/java/ink/trmnl/android/ui/devicemodel/DeviceModelSelectorScreen.kt index 5c198e8..a4890a6 100644 --- a/app/src/main/java/ink/trmnl/android/ui/devicemodel/DeviceModelSelectorScreen.kt +++ b/app/src/main/java/ink/trmnl/android/ui/devicemodel/DeviceModelSelectorScreen.kt @@ -63,13 +63,9 @@ import kotlinx.parcelize.Parcelize * - View all available device models with their specifications * - Select a device model * - Return the selected model to the previous screen via PopResult - * - * @property serverBaseUrl The base URL of the server to fetch models from. */ @Parcelize -data class DeviceModelSelectorScreen( - val serverBaseUrl: String = TRMNL_API_SERVER_BASE_URL, -) : Screen { +data object DeviceModelSelectorScreen : Screen { /** * Represents the UI state for the [DeviceModelSelectorScreen]. * @@ -203,7 +199,7 @@ class DeviceModelSelectorPresenter onModelsLoaded: (List) -> Unit, onError: (String) -> Unit, ) { - val loadedModels = repository.getDeviceModels(screen.serverBaseUrl) + val loadedModels = repository.getDeviceModels(TRMNL_API_SERVER_BASE_URL) if (loadedModels.isEmpty()) { onError("Failed to load device models. Please try again.") } else { From a2427534a7908471f720f6500aab1aea3b144524 Mon Sep 17 00:00:00 2001 From: Hossain Khan Date: Sat, 6 Dec 2025 09:43:00 -0500 Subject: [PATCH 3/3] fix: Remove screen parameter from DeviceModelSelectorPresenter - data object screens don't need screen parameter in presenter - Update Factory to only take navigator parameter - Fixes KSP code generation type mismatch error --- .../android/ui/devicemodel/DeviceModelSelectorScreen.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/src/main/java/ink/trmnl/android/ui/devicemodel/DeviceModelSelectorScreen.kt b/app/src/main/java/ink/trmnl/android/ui/devicemodel/DeviceModelSelectorScreen.kt index a4890a6..3516e4a 100644 --- a/app/src/main/java/ink/trmnl/android/ui/devicemodel/DeviceModelSelectorScreen.kt +++ b/app/src/main/java/ink/trmnl/android/ui/devicemodel/DeviceModelSelectorScreen.kt @@ -126,7 +126,6 @@ data object DeviceModelSelectorScreen : Screen { class DeviceModelSelectorPresenter @AssistedInject constructor( - @Assisted private val screen: DeviceModelSelectorScreen, @Assisted private val navigator: Navigator, private val repository: TrmnlDisplayRepository, ) : Presenter { @@ -213,10 +212,7 @@ class DeviceModelSelectorPresenter @CircuitInject(DeviceModelSelectorScreen::class, AppScope::class) @AssistedFactory fun interface Factory { - fun create( - screen: DeviceModelSelectorScreen, - navigator: Navigator, - ): DeviceModelSelectorPresenter + fun create(navigator: Navigator): DeviceModelSelectorPresenter } }