207 lines
7.7 KiB
Kotlin
207 lines
7.7 KiB
Kotlin
/*
|
|
* Copyright (C) 2023 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* https://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package com.example.cupcake
|
|
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import androidx.annotation.StringRes
|
|
import androidx.compose.foundation.layout.fillMaxHeight
|
|
import androidx.compose.foundation.layout.fillMaxSize
|
|
import androidx.compose.foundation.layout.padding
|
|
import androidx.compose.foundation.rememberScrollState
|
|
import androidx.compose.foundation.verticalScroll
|
|
import androidx.compose.material.icons.Icons
|
|
import androidx.compose.material.icons.filled.ArrowBack
|
|
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.material3.TopAppBarDefaults
|
|
import androidx.compose.runtime.Composable
|
|
import androidx.compose.runtime.collectAsState
|
|
import androidx.compose.runtime.getValue
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.platform.LocalContext
|
|
import androidx.compose.ui.res.dimensionResource
|
|
import androidx.compose.ui.res.stringResource
|
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
import androidx.navigation.NavHostController
|
|
import androidx.navigation.compose.NavHost
|
|
import androidx.navigation.compose.composable
|
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
|
import androidx.navigation.compose.rememberNavController
|
|
import com.example.cupcake.data.DataSource
|
|
import com.example.cupcake.data.OrderUiState
|
|
import com.example.cupcake.ui.OrderSummaryScreen
|
|
import com.example.cupcake.ui.OrderViewModel
|
|
import com.example.cupcake.ui.SelectOptionScreen
|
|
import com.example.cupcake.ui.StartOrderScreen
|
|
|
|
/**
|
|
* enum values that represent the screens in the app
|
|
*/
|
|
enum class CupcakeScreen(@StringRes val title: Int) {
|
|
Start(title = R.string.app_name),
|
|
Flavor(title = R.string.choose_flavor),
|
|
Pickup(title = R.string.choose_pickup_date),
|
|
Summary(title = R.string.order_summary)
|
|
}
|
|
|
|
/**
|
|
* Composable that displays the topBar and displays back button if back navigation is possible.
|
|
*/
|
|
@Composable
|
|
fun CupcakeAppBar(
|
|
currentScreen: CupcakeScreen,
|
|
canNavigateBack: Boolean,
|
|
navigateUp: () -> Unit,
|
|
modifier: Modifier = Modifier
|
|
) {
|
|
TopAppBar(
|
|
title = { Text(stringResource(currentScreen.title)) },
|
|
colors = TopAppBarDefaults.mediumTopAppBarColors(
|
|
containerColor = MaterialTheme.colorScheme.primaryContainer
|
|
),
|
|
modifier = modifier,
|
|
navigationIcon = {
|
|
if (canNavigateBack) {
|
|
IconButton(onClick = navigateUp) {
|
|
Icon(
|
|
imageVector = Icons.Filled.ArrowBack,
|
|
contentDescription = stringResource(R.string.back_button)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
@Composable
|
|
fun CupcakeApp(
|
|
viewModel: OrderViewModel = viewModel(),
|
|
navController: NavHostController = rememberNavController()
|
|
) {
|
|
// Get current back stack entry
|
|
val backStackEntry by navController.currentBackStackEntryAsState()
|
|
// Get the name of the current screen
|
|
val currentScreen = CupcakeScreen.valueOf(
|
|
backStackEntry?.destination?.route ?: CupcakeScreen.Start.name
|
|
)
|
|
|
|
Scaffold(
|
|
topBar = {
|
|
CupcakeAppBar(
|
|
currentScreen = currentScreen,
|
|
canNavigateBack = navController.previousBackStackEntry != null,
|
|
navigateUp = { navController.navigateUp() }
|
|
)
|
|
}
|
|
) { innerPadding ->
|
|
val uiState by viewModel.uiState.collectAsState()
|
|
|
|
NavHost(
|
|
navController = navController,
|
|
startDestination = CupcakeScreen.Start.name,
|
|
modifier = Modifier
|
|
.fillMaxSize()
|
|
.verticalScroll(rememberScrollState())
|
|
.padding(innerPadding)
|
|
) {
|
|
composable(route = CupcakeScreen.Start.name) {
|
|
StartOrderScreen(
|
|
quantityOptions = DataSource.quantityOptions,
|
|
onNextButtonClicked = {
|
|
viewModel.setQuantity(it)
|
|
navController.navigate(CupcakeScreen.Flavor.name)
|
|
},
|
|
modifier = Modifier
|
|
.fillMaxSize()
|
|
.padding(dimensionResource(R.dimen.padding_medium))
|
|
)
|
|
}
|
|
composable(route = CupcakeScreen.Flavor.name) {
|
|
val context = LocalContext.current
|
|
SelectOptionScreen(
|
|
subtotal = uiState.price,
|
|
onNextButtonClicked = { navController.navigate(CupcakeScreen.Pickup.name) },
|
|
onCancelButtonClicked = {
|
|
cancelOrderAndNavigateToStart(viewModel, navController)
|
|
},
|
|
options = DataSource.flavors.map { id -> context.resources.getString(id) },
|
|
onSelectionChanged = { viewModel.setFlavor(it) },
|
|
modifier = Modifier.fillMaxHeight()
|
|
)
|
|
}
|
|
composable(route = CupcakeScreen.Pickup.name) {
|
|
SelectOptionScreen(
|
|
subtotal = uiState.price,
|
|
onNextButtonClicked = { navController.navigate(CupcakeScreen.Summary.name) },
|
|
onCancelButtonClicked = {
|
|
cancelOrderAndNavigateToStart(viewModel, navController)
|
|
},
|
|
options = uiState.pickupOptions,
|
|
onSelectionChanged = { viewModel.setDate(it) },
|
|
modifier = Modifier.fillMaxHeight()
|
|
)
|
|
}
|
|
composable(route = CupcakeScreen.Summary.name) {
|
|
val context = LocalContext.current
|
|
OrderSummaryScreen(
|
|
orderUiState = uiState,
|
|
onCancelButtonClicked = {
|
|
cancelOrderAndNavigateToStart(viewModel, navController)
|
|
},
|
|
onSendButtonClicked = { subject: String, summary: String ->
|
|
shareOrder(context, subject = subject, summary = summary)
|
|
},
|
|
modifier = Modifier.fillMaxHeight()
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resets the [OrderUiState] and pops up to [CupcakeScreen.Start]
|
|
*/
|
|
private fun cancelOrderAndNavigateToStart(
|
|
viewModel: OrderViewModel,
|
|
navController: NavHostController
|
|
) {
|
|
viewModel.resetOrder()
|
|
navController.popBackStack(CupcakeScreen.Start.name, inclusive = false)
|
|
}
|
|
|
|
/**
|
|
* Creates an intent to share order details
|
|
*/
|
|
private fun shareOrder(context: Context, subject: String, summary: String) {
|
|
// Create an ACTION_SEND implicit intent with order details in the intent extras
|
|
val intent = Intent(Intent.ACTION_SEND).apply {
|
|
type = "text/plain"
|
|
putExtra(Intent.EXTRA_SUBJECT, subject)
|
|
putExtra(Intent.EXTRA_TEXT, summary)
|
|
}
|
|
context.startActivity(
|
|
Intent.createChooser(
|
|
intent,
|
|
context.getString(R.string.new_cupcake_order)
|
|
)
|
|
)
|
|
}
|