diff --git a/app/src/main/java/ink/trmnl/android/data/AppConfig.kt b/app/src/main/java/ink/trmnl/android/data/AppConfig.kt index 08a207f..333c8c7 100644 --- a/app/src/main/java/ink/trmnl/android/data/AppConfig.kt +++ b/app/src/main/java/ink/trmnl/android/data/AppConfig.kt @@ -19,4 +19,9 @@ object AppConfig { * Ref: https://discord.com/channels/1281055965508141100/1284986536357662740/1364623667337760910 */ const val TRMNL_API_SERVER_BASE_URL = "https://trmnl.app/" + + /** + * URL for the TRMNL Android app on GitHub. + */ + const val TRMNL_ANDROID_APP_GITHUB_URL = "https://github.com/usetrmnl/trmnl-android" } diff --git a/app/src/main/java/ink/trmnl/android/ui/aboutapp/AppInfoScreen.kt b/app/src/main/java/ink/trmnl/android/ui/aboutapp/AppInfoScreen.kt new file mode 100644 index 0000000..d4c346e --- /dev/null +++ b/app/src/main/java/ink/trmnl/android/ui/aboutapp/AppInfoScreen.kt @@ -0,0 +1,200 @@ +package ink.trmnl.android.ui.aboutapp + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing +import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.Button +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.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.unit.dp +import com.slack.circuit.codegen.annotations.CircuitInject +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.Screen +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import ink.trmnl.android.BuildConfig +import ink.trmnl.android.R +import ink.trmnl.android.data.AppConfig.TRMNL_ANDROID_APP_GITHUB_URL +import ink.trmnl.android.di.AppScope +import ink.trmnl.android.ui.aboutapp.AppInfoScreen.Event +import ink.trmnl.android.ui.aboutapp.AppInfoScreen.State +import ink.trmnl.android.ui.theme.TrmnlDisplayAppTheme +import ink.trmnl.android.ui.theme.TrmnlOrange +import kotlinx.parcelize.Parcelize + +/** + * Screen for displaying app information and links. + */ +@Parcelize +data object AppInfoScreen : Screen { + data class State( + val appVersion: String, + val buildType: String, + val eventSink: (Event) -> Unit, + ) : CircuitUiState + + sealed class Event { + data object BackPressed : Event() + + data object OpenGithub : Event() + } +} + +class AppInfoPresenter + @AssistedInject + constructor( + @Assisted private val navigator: Navigator, + ) : Presenter { + @Composable + override fun present(): AppInfoScreen.State { + val uriHandler = LocalUriHandler.current + val appVersion = BuildConfig.VERSION_NAME + val buildType = BuildConfig.BUILD_TYPE + + return State( + appVersion = appVersion, + buildType = buildType, + eventSink = { event -> + when (event) { + Event.BackPressed -> navigator.pop() + Event.OpenGithub -> { + // Handled in the UI + uriHandler.openUri(TRMNL_ANDROID_APP_GITHUB_URL) + } + } + }, + ) + } + + @CircuitInject(AppInfoScreen::class, AppScope::class) + @AssistedFactory + fun interface Factory { + fun create(navigator: Navigator): AppInfoPresenter + } + } + +@CircuitInject(AppInfoScreen::class, AppScope::class) +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AppInfoContent( + state: State, + modifier: Modifier = Modifier, +) { + Scaffold( + modifier = modifier, + topBar = { + TopAppBar( + title = { Text("App Info") }, + navigationIcon = { + IconButton(onClick = { state.eventSink(Event.BackPressed) }) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "Back", + ) + } + }, + windowInsets = WindowInsets.statusBars, + ) + }, + contentWindowInsets = WindowInsets.safeDrawing, + ) { innerPadding -> + Column( + modifier = + Modifier + .padding(innerPadding) + .padding(horizontal = 32.dp) + .verticalScroll(rememberScrollState()) + .fillMaxWidth() + .navigationBarsPadding(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + Spacer(modifier = Modifier.height(24.dp)) + + Icon( + painter = painterResource(R.drawable.trmnl_logo_plain), + contentDescription = null, + tint = TrmnlOrange, + modifier = Modifier.height(64.dp), + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = stringResource(R.string.app_name), + style = MaterialTheme.typography.headlineSmall, + fontWeight = FontWeight.Bold, + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = "Version: ${state.appVersion}", + style = MaterialTheme.typography.bodyLarge, + ) + + Spacer(modifier = Modifier.height(4.dp)) + + Text( + text = "Build Type: ${state.buildType}", + style = MaterialTheme.typography.bodyMedium, + ) + + Spacer(modifier = Modifier.height(32.dp)) + + Button( + onClick = { state.eventSink(Event.OpenGithub) }, + modifier = Modifier.fillMaxWidth(0.7f), + ) { + Icon( + painter = painterResource(R.drawable.code_24dp), + contentDescription = null, + modifier = Modifier.padding(end = 8.dp), + ) + Text("View Project on GitHub") + } + } + } +} + +@PreviewLightDark +@Composable +private fun AppInfoScreenPreview() { + TrmnlDisplayAppTheme { + AppInfoContent( + state = + State( + appVersion = "1.0.0", + buildType = "debug", + eventSink = {}, + ), + ) + } +} diff --git a/app/src/main/java/ink/trmnl/android/ui/settings/AppSettingsScreen.kt b/app/src/main/java/ink/trmnl/android/ui/settings/AppSettingsScreen.kt index 4af16ae..db8a551 100644 --- a/app/src/main/java/ink/trmnl/android/ui/settings/AppSettingsScreen.kt +++ b/app/src/main/java/ink/trmnl/android/ui/settings/AppSettingsScreen.kt @@ -91,6 +91,7 @@ import ink.trmnl.android.data.TrmnlDisplayRepository import ink.trmnl.android.di.AppScope import ink.trmnl.android.model.TrmnlDeviceConfig import ink.trmnl.android.model.TrmnlDeviceType +import ink.trmnl.android.ui.aboutapp.AppInfoScreen import ink.trmnl.android.ui.display.TrmnlMirrorDisplayScreen import ink.trmnl.android.ui.settings.AppSettingsScreen.ValidationResult import ink.trmnl.android.ui.settings.AppSettingsScreen.ValidationResult.Failure @@ -193,6 +194,11 @@ data class AppSettingsScreen( data class ServerUrlChanged( val url: String, ) : Event() + + /** + * Event triggered when the info icon is clicked. + */ + data object AppInfoPressed : Event() } } @@ -358,6 +364,11 @@ class AppSettingsPresenter validationResult = null } } + + AppSettingsScreen.Event.AppInfoPressed -> { + // Navigate to AppInfoScreen + navigator.goTo(AppInfoScreen) + } } }, ) @@ -439,6 +450,15 @@ fun AppSettingsContent( } } }, + // Add the info action button to navigate to AppInfoScreen + actions = { + IconButton(onClick = { state.eventSink(AppSettingsScreen.Event.AppInfoPressed) }) { + Icon( + painter = painterResource(R.drawable.info_24dp), + contentDescription = "App Info", + ) + } + }, // Configure the TopAppBar to handle status bar insets windowInsets = WindowInsets.statusBars, ) diff --git a/app/src/main/java/ink/trmnl/android/ui/theme/Color.kt b/app/src/main/java/ink/trmnl/android/ui/theme/Color.kt index 73af0d1..d326f95 100644 --- a/app/src/main/java/ink/trmnl/android/ui/theme/Color.kt +++ b/app/src/main/java/ink/trmnl/android/ui/theme/Color.kt @@ -14,3 +14,6 @@ val Grey80 = Color(0xFFECEFF1) val Blue40 = Color(0xFF2236B0) val BlueGrey40 = Color(0xFF546E7A) val Grey40 = Color(0xFF47676F) + +// Add TRMNL color - #F8654B +val TrmnlOrange = Color(0xFFF8654B) diff --git a/app/src/main/res/drawable/code_24dp.xml b/app/src/main/res/drawable/code_24dp.xml new file mode 100644 index 0000000..8a8060a --- /dev/null +++ b/app/src/main/res/drawable/code_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/info_24dp.xml b/app/src/main/res/drawable/info_24dp.xml new file mode 100644 index 0000000..cc74731 --- /dev/null +++ b/app/src/main/res/drawable/info_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml index 96da163..10b82bf 100644 --- a/app/src/main/res/values/ic_launcher_background.xml +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -1,4 +1,4 @@ - #F86527 + #F8654B \ No newline at end of file diff --git a/project-resources/logo/app-icon-bg-color.txt b/project-resources/logo/app-icon-bg-color.txt index 6f2e5c1..4b43623 100644 --- a/project-resources/logo/app-icon-bg-color.txt +++ b/project-resources/logo/app-icon-bg-color.txt @@ -1,2 +1,2 @@ -F86527 +F8654B