mirror of
https://github.com/AdrianKuta/android-challange-adrian-kuta.git
synced 2025-07-03 03:47:59 +02:00
Initial commit
This commit is contained in:
@ -0,0 +1,23 @@
|
||||
package dev.adriankuta.flights
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import dev.adriankuta.flights.ui.home.navigation.HomeRoute
|
||||
import dev.adriankuta.flights.ui.home.navigation.homeScreen
|
||||
|
||||
@Composable
|
||||
fun FlightsNavGraph(
|
||||
modifier: Modifier = Modifier,
|
||||
navController: NavHostController = rememberNavController(),
|
||||
) {
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = HomeRoute,
|
||||
modifier = modifier,
|
||||
) {
|
||||
homeScreen()
|
||||
}
|
||||
}
|
85
app/src/main/kotlin/dev/adriankuta/flights/MainActivity.kt
Normal file
85
app/src/main/kotlin/dev/adriankuta/flights/MainActivity.kt
Normal file
@ -0,0 +1,85 @@
|
||||
package dev.adriankuta.flights
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dev.adriankuta.flights.ui.FlightsApp
|
||||
import dev.adriankuta.flights.ui.designsystem.theme.FlightsTheme
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
val splashScreen = installSplashScreen()
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
var isLoading: Boolean by mutableStateOf(true)
|
||||
|
||||
lifecycleScope.launch {
|
||||
@Suppress("MagicNumber")
|
||||
delay(500)
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
splashScreen.setKeepOnScreenCondition {
|
||||
isLoading
|
||||
}
|
||||
|
||||
enableEdgeToEdge()
|
||||
|
||||
setContent {
|
||||
val darkTheme = shouldUseDarkTheme()
|
||||
// Update the edge to edge configuration to match the theme
|
||||
// This is the same parameters as the default enableEdgeToEdge call, but we manually
|
||||
// resolve whether or not to show dark theme using uiState, since it can be different
|
||||
// than the configuration's dark theme value based on the user preference.
|
||||
DisposableEffect(darkTheme) {
|
||||
enableEdgeToEdge(
|
||||
statusBarStyle = SystemBarStyle.auto(
|
||||
android.graphics.Color.TRANSPARENT,
|
||||
android.graphics.Color.TRANSPARENT,
|
||||
) { darkTheme },
|
||||
navigationBarStyle = SystemBarStyle.auto(
|
||||
lightScrim,
|
||||
darkScrim,
|
||||
) { darkTheme },
|
||||
)
|
||||
onDispose {}
|
||||
}
|
||||
FlightsTheme {
|
||||
FlightsApp()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if dark theme should be used, as a function of the [uiState] and the
|
||||
* current system context.
|
||||
*/
|
||||
@Composable
|
||||
private fun shouldUseDarkTheme(): Boolean = isSystemInDarkTheme()
|
||||
|
||||
/**
|
||||
* The default light scrim, as defined by androidx and the platform:
|
||||
* https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:activity/activity/src/main/java/androidx/activity/EdgeToEdge.kt;l=35-38;drc=27e7d52e8604a080133e8b842db10c89b4482598
|
||||
*/
|
||||
private val lightScrim = android.graphics.Color.argb(0xe6, 0xFF, 0xFF, 0xFF)
|
||||
|
||||
/**
|
||||
* The default dark scrim, as defined by androidx and the platform:
|
||||
* https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:activity/activity/src/main/java/androidx/activity/EdgeToEdge.kt;l=40-44;drc=27e7d52e8604a080133e8b842db10c89b4482598
|
||||
*/
|
||||
private val darkScrim = android.graphics.Color.argb(0x80, 0x1b, 0x1b, 0x1b)
|
25
app/src/main/kotlin/dev/adriankuta/flights/ui/FlightsApp.kt
Normal file
25
app/src/main/kotlin/dev/adriankuta/flights/ui/FlightsApp.kt
Normal file
@ -0,0 +1,25 @@
|
||||
package dev.adriankuta.flights.ui
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import dev.adriankuta.flights.FlightsNavGraph
|
||||
import dev.adriankuta.flights.ui.designsystem.theme.Elevation
|
||||
|
||||
@Composable
|
||||
fun FlightsApp(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Surface(
|
||||
tonalElevation = Elevation.Surface,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Scaffold(
|
||||
snackbarHost = { InAppUpdates() },
|
||||
) { paddingValues ->
|
||||
FlightsNavGraph(Modifier.padding(paddingValues))
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package dev.adriankuta.flights.ui
|
||||
|
||||
import androidx.activity.compose.LocalActivity
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.SnackbarResult
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.google.android.play.core.appupdate.AppUpdateInfo
|
||||
import com.google.android.play.core.appupdate.AppUpdateManager
|
||||
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
|
||||
import com.google.android.play.core.appupdate.AppUpdateOptions
|
||||
import com.google.android.play.core.install.InstallException
|
||||
import com.google.android.play.core.install.InstallStateUpdatedListener
|
||||
import com.google.android.play.core.install.model.AppUpdateType
|
||||
import com.google.android.play.core.install.model.InstallStatus
|
||||
import com.google.android.play.core.install.model.UpdateAvailability
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
@Composable
|
||||
fun InAppUpdates(
|
||||
modifier: Modifier = Modifier,
|
||||
@AppUpdateType updateType: Int = AppUpdateType.FLEXIBLE,
|
||||
) {
|
||||
val activity = LocalActivity.current ?: return
|
||||
val scope = rememberCoroutineScope()
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val appUpdateManager = remember { AppUpdateManagerFactory.create(requireNotNull(activity)) }
|
||||
val flexibleUpdateListener = remember {
|
||||
InstallStateUpdatedListener { state ->
|
||||
if (state.installStatus() == InstallStatus.DOWNLOADED) {
|
||||
// After the update is downloaded, show a notification
|
||||
// and request user confirmation to restart the app.
|
||||
scope.launch {
|
||||
val result = snackbarHostState.showSnackbar(
|
||||
message = "An update has just been downloaded",
|
||||
actionLabel = "Reload",
|
||||
)
|
||||
when (result) {
|
||||
SnackbarResult.Dismissed -> Unit
|
||||
SnackbarResult.ActionPerformed -> {
|
||||
appUpdateManager.completeUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
try {
|
||||
val appUpdateInfo = appUpdateManager.checkUpdateInfo()
|
||||
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE &&
|
||||
appUpdateInfo.isUpdateTypeAllowed(updateType)
|
||||
) {
|
||||
appUpdateManager.startUpdateFlow(
|
||||
appUpdateInfo,
|
||||
activity,
|
||||
AppUpdateOptions.newBuilder(updateType).build(),
|
||||
)
|
||||
}
|
||||
} catch (e: InstallException) {
|
||||
Timber.w(e)
|
||||
}
|
||||
}
|
||||
|
||||
DisposableEffect(appUpdateManager) {
|
||||
appUpdateManager.registerListener(flexibleUpdateListener)
|
||||
onDispose {
|
||||
appUpdateManager.unregisterListener(flexibleUpdateListener)
|
||||
}
|
||||
}
|
||||
|
||||
SnackbarHost(
|
||||
hostState = snackbarHostState,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun AppUpdateManager.checkUpdateInfo() =
|
||||
suspendCoroutine<AppUpdateInfo> { continuation ->
|
||||
appUpdateInfo.addOnSuccessListener {
|
||||
continuation.resumeWith(Result.success(it))
|
||||
}.addOnFailureListener {
|
||||
continuation.resumeWith(Result.failure(it))
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user