-
Notifications
You must be signed in to change notification settings - Fork 106
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* [ANDROID_TV] Try to create Android TV UI * [SHARED] Update libs * [SHARED] Remove magic numbers * [SHARED] Change native libs icon to button * [SHARED] Change selection color * [SHARED] Fix apps action row * [SHARED] Update kotlin * [SHARED] Update xcode project structure, disable iOS arm simulator * [ANDROID] Update native cpuinfo * [IOS] Preparations for dependencies cleanup * [SHARED] Update AGP * [SHARED] Update navigation * [IOS] Update cpuinfo lib * [TV] Add TabRow for info section * [TV] Handle info TabRow content * [TV] Remove focusable workaround * [TV] Add banner * [TV] Adjust colors * [TV] Adjust info screens
- Loading branch information
Showing
33 changed files
with
1,221 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
androidApp/src/androidMain/kotlin/com/kgurgul/cpuinfo/TvActivity.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package com.kgurgul.cpuinfo | ||
|
||
import android.os.Build | ||
import android.os.Bundle | ||
import androidx.activity.SystemBarStyle | ||
import androidx.activity.compose.setContent | ||
import androidx.activity.enableEdgeToEdge | ||
import androidx.appcompat.app.AppCompatActivity | ||
import androidx.compose.material3.dynamicDarkColorScheme | ||
import androidx.compose.material3.dynamicLightColorScheme | ||
import androidx.compose.runtime.DisposableEffect | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.mutableStateOf | ||
import androidx.compose.runtime.setValue | ||
import androidx.compose.ui.graphics.toArgb | ||
import androidx.compose.ui.platform.LocalContext | ||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen | ||
import androidx.lifecycle.Lifecycle | ||
import androidx.lifecycle.lifecycleScope | ||
import androidx.lifecycle.repeatOnLifecycle | ||
import com.kgurgul.cpuinfo.features.HostViewModel | ||
import com.kgurgul.cpuinfo.features.TvHostScreen | ||
import com.kgurgul.cpuinfo.ui.shouldUseDarkTheme | ||
import com.kgurgul.cpuinfo.ui.theme.CpuInfoTheme | ||
import com.kgurgul.cpuinfo.ui.theme.DarkColors | ||
import com.kgurgul.cpuinfo.ui.theme.LightColors | ||
import com.kgurgul.cpuinfo.ui.theme.darkPrimary | ||
import com.kgurgul.cpuinfo.ui.theme.lightPrimary | ||
import kotlinx.coroutines.flow.collect | ||
import kotlinx.coroutines.flow.onEach | ||
import kotlinx.coroutines.launch | ||
import org.koin.androidx.viewmodel.ext.android.viewModel | ||
|
||
class TvActivity : AppCompatActivity() { | ||
|
||
private val viewModel: HostViewModel by viewModel() | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
val splashScreen = installSplashScreen() | ||
super.onCreate(savedInstanceState) | ||
|
||
var uiState: HostViewModel.UiState by mutableStateOf(HostViewModel.UiState()) | ||
lifecycleScope.launch { | ||
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { | ||
viewModel.uiStateFlow | ||
.onEach { uiState = it } | ||
.collect() | ||
} | ||
} | ||
splashScreen.setKeepOnScreenCondition { uiState.isLoading } | ||
|
||
enableEdgeToEdge() | ||
|
||
setContent { | ||
val darkTheme = shouldUseDarkTheme(uiState) | ||
val systemBarScrim = (if (darkTheme) darkPrimary else lightPrimary).toArgb() | ||
DisposableEffect(darkTheme) { | ||
enableEdgeToEdge( | ||
statusBarStyle = SystemBarStyle.dark(systemBarScrim), | ||
navigationBarStyle = SystemBarStyle.dark(systemBarScrim), | ||
) | ||
onDispose {} | ||
} | ||
val dynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S | ||
val colors = when { | ||
dynamicColor && darkTheme -> { | ||
val dynamicDarkColors = dynamicDarkColorScheme(LocalContext.current) | ||
DarkColors.copy( | ||
secondary = dynamicDarkColors.primaryContainer, | ||
onSecondary = dynamicDarkColors.onPrimaryContainer, | ||
) | ||
} | ||
|
||
dynamicColor && !darkTheme -> { | ||
val dynamicLightColors = dynamicLightColorScheme(LocalContext.current) | ||
LightColors.copy( | ||
secondary = dynamicLightColors.secondary, | ||
onSecondary = dynamicLightColors.onSecondary, | ||
) | ||
} | ||
|
||
darkTheme -> DarkColors | ||
else -> LightColors | ||
} | ||
CpuInfoTheme( | ||
useDarkTheme = darkTheme, | ||
colors = colors, | ||
) { | ||
TvHostScreen( | ||
viewModel = viewModel, | ||
) | ||
} | ||
} | ||
} | ||
} |
178 changes: 178 additions & 0 deletions
178
androidApp/src/androidMain/kotlin/com/kgurgul/cpuinfo/features/TvHostScreen.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
package com.kgurgul.cpuinfo.features | ||
|
||
import androidx.compose.animation.fadeIn | ||
import androidx.compose.animation.fadeOut | ||
import androidx.compose.material3.Icon | ||
import androidx.compose.material3.MaterialTheme | ||
import androidx.compose.material3.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.ui.text.style.TextOverflow | ||
import androidx.lifecycle.compose.collectAsStateWithLifecycle | ||
import androidx.navigation.NavDestination.Companion.hierarchy | ||
import androidx.navigation.NavGraph.Companion.findStartDestination | ||
import androidx.navigation.compose.NavHost | ||
import androidx.navigation.compose.composable | ||
import androidx.navigation.compose.currentBackStackEntryAsState | ||
import androidx.navigation.compose.rememberNavController | ||
import com.kgurgul.cpuinfo.features.applications.ApplicationsScreen | ||
import com.kgurgul.cpuinfo.features.information.TvInfoContainerScreen | ||
import com.kgurgul.cpuinfo.features.settings.SettingsScreen | ||
import com.kgurgul.cpuinfo.features.temperature.TemperatureScreen | ||
import com.kgurgul.cpuinfo.shared.Res | ||
import com.kgurgul.cpuinfo.shared.hardware | ||
import com.kgurgul.cpuinfo.shared.ic_cpu | ||
import com.kgurgul.cpuinfo.shared.ic_settings | ||
import com.kgurgul.cpuinfo.shared.settings | ||
import com.kgurgul.cpuinfo.ui.components.CpuNavigationSuiteScaffold | ||
import com.kgurgul.cpuinfo.ui.components.CpuNavigationSuiteScaffoldDefault | ||
import org.jetbrains.compose.resources.DrawableResource | ||
import org.jetbrains.compose.resources.StringResource | ||
import org.jetbrains.compose.resources.painterResource | ||
import org.jetbrains.compose.resources.stringResource | ||
import org.koin.compose.viewmodel.koinViewModel | ||
|
||
@Composable | ||
fun TvHostScreen( | ||
viewModel: HostViewModel = koinViewModel(), | ||
) { | ||
val uiState by viewModel.uiStateFlow.collectAsStateWithLifecycle() | ||
TvHostScreen( | ||
uiState = uiState, | ||
) | ||
} | ||
|
||
@Composable | ||
fun TvHostScreen( | ||
uiState: HostViewModel.UiState, | ||
) { | ||
val navController = rememberNavController() | ||
val navBackStackEntry by navController.currentBackStackEntryAsState() | ||
val currentDestination = navBackStackEntry?.destination | ||
val itemDefaultColors = CpuNavigationSuiteScaffoldDefault.itemDefaultColors() | ||
CpuNavigationSuiteScaffold( | ||
navigationSuiteItems = { | ||
TvHostNavigationItem.bottomNavigationItems( | ||
isApplicationsVisible = uiState.isApplicationSectionVisible, | ||
).forEach { item -> | ||
item( | ||
icon = { | ||
Icon( | ||
painter = painterResource(item.icon), | ||
contentDescription = stringResource(item.label), | ||
) | ||
}, | ||
label = { | ||
Text( | ||
text = stringResource(item.label), | ||
style = MaterialTheme.typography.labelSmall, | ||
maxLines = 1, | ||
overflow = TextOverflow.Ellipsis, | ||
) | ||
}, | ||
selected = currentDestination?.hierarchy | ||
?.any { it.route == item.route } == true, | ||
onClick = { | ||
navController.navigate(item.route) { | ||
navController.graph.findStartDestination().route?.let { | ||
popUpTo(it) { | ||
saveState = true | ||
} | ||
} | ||
launchSingleTop = true | ||
restoreState = true | ||
} | ||
}, | ||
colors = itemDefaultColors, | ||
) | ||
} | ||
}, | ||
) { | ||
NavHost( | ||
navController = navController, | ||
startDestination = TvHostScreen.Information.route, | ||
) { | ||
composable( | ||
route = TvHostScreen.Information.route, | ||
enterTransition = { fadeIn() }, | ||
exitTransition = { fadeOut() }, | ||
popEnterTransition = { fadeIn() }, | ||
popExitTransition = { fadeOut() }, | ||
) { TvInfoContainerScreen() } | ||
composable( | ||
route = TvHostScreen.Applications.route, | ||
enterTransition = { fadeIn() }, | ||
exitTransition = { fadeOut() }, | ||
popEnterTransition = { fadeIn() }, | ||
popExitTransition = { fadeOut() }, | ||
) { ApplicationsScreen() } | ||
composable( | ||
route = TvHostScreen.Temperatures.route, | ||
enterTransition = { fadeIn() }, | ||
exitTransition = { fadeOut() }, | ||
popEnterTransition = { fadeIn() }, | ||
popExitTransition = { fadeOut() }, | ||
) { TemperatureScreen() } | ||
composable( | ||
route = TvHostScreen.Settings.route, | ||
enterTransition = { fadeIn() }, | ||
exitTransition = { fadeOut() }, | ||
popEnterTransition = { fadeIn() }, | ||
popExitTransition = { fadeOut() }, | ||
) { SettingsScreen() } | ||
} | ||
} | ||
} | ||
|
||
sealed class TvHostScreen(val route: String) { | ||
data object Information : TvHostScreen("information_route") | ||
data object Applications : TvHostScreen("applications_route") | ||
data object Temperatures : TvHostScreen("temperatures_route") | ||
data object Settings : TvHostScreen("settings_route") | ||
} | ||
|
||
data class TvHostNavigationItem( | ||
val label: StringResource, | ||
val icon: DrawableResource, | ||
val route: String, | ||
) { | ||
|
||
companion object { | ||
fun bottomNavigationItems( | ||
isApplicationsVisible: Boolean, | ||
): List<HostNavigationItem> { | ||
return buildList { | ||
add( | ||
HostNavigationItem( | ||
label = Res.string.hardware, | ||
icon = Res.drawable.ic_cpu, | ||
route = HostScreen.Information.route, | ||
), | ||
) | ||
/*if (isApplicationsVisible) { | ||
add( | ||
HostNavigationItem( | ||
label = Res.string.applications, | ||
icon = Res.drawable.ic_android, | ||
route = HostScreen.Applications.route, | ||
), | ||
) | ||
} | ||
add( | ||
HostNavigationItem( | ||
label = Res.string.temp, | ||
icon = Res.drawable.ic_temperature, | ||
route = HostScreen.Temperatures.route, | ||
), | ||
)*/ | ||
add( | ||
HostNavigationItem( | ||
label = Res.string.settings, | ||
icon = Res.drawable.ic_settings, | ||
route = HostScreen.Settings.route, | ||
), | ||
) | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.