|
/* |
|
* 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 |
|
* |
|
* http://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.dessertclicker |
|
|
|
import android.content.ActivityNotFoundException |
|
import android.content.Context |
|
import android.content.Intent |
|
import android.os.Bundle |
|
import android.util.Log |
|
import android.widget.Toast |
|
import androidx.activity.ComponentActivity |
|
import androidx.activity.compose.setContent |
|
import androidx.activity.enableEdgeToEdge |
|
import androidx.annotation.DrawableRes |
|
import androidx.compose.foundation.Image |
|
import androidx.compose.foundation.background |
|
import androidx.compose.foundation.clickable |
|
import androidx.compose.foundation.layout.Arrangement |
|
import androidx.compose.foundation.layout.Box |
|
import androidx.compose.foundation.layout.Column |
|
import androidx.compose.foundation.layout.Row |
|
import androidx.compose.foundation.layout.WindowInsets |
|
import androidx.compose.foundation.layout.asPaddingValues |
|
import androidx.compose.foundation.layout.calculateEndPadding |
|
import androidx.compose.foundation.layout.calculateStartPadding |
|
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.statusBarsPadding |
|
import androidx.compose.foundation.layout.width |
|
import androidx.compose.material.icons.Icons |
|
import androidx.compose.material.icons.filled.Share |
|
import androidx.compose.material3.Scaffold |
|
import androidx.compose.material3.Icon |
|
import androidx.compose.material3.IconButton |
|
import androidx.compose.material3.MaterialTheme |
|
import androidx.compose.material3.Surface |
|
import androidx.compose.material3.Text |
|
import androidx.compose.runtime.Composable |
|
import androidx.compose.runtime.getValue |
|
import androidx.compose.runtime.mutableStateOf |
|
import androidx.compose.runtime.saveable.rememberSaveable |
|
import androidx.compose.runtime.setValue |
|
import androidx.compose.ui.Alignment |
|
import androidx.compose.ui.Modifier |
|
import androidx.compose.ui.layout.ContentScale |
|
import androidx.compose.ui.platform.LocalContext |
|
import androidx.compose.ui.platform.LocalLayoutDirection |
|
import androidx.compose.ui.res.dimensionResource |
|
import androidx.compose.ui.res.painterResource |
|
import androidx.compose.ui.res.stringResource |
|
import androidx.compose.ui.text.style.TextAlign |
|
import androidx.compose.ui.tooling.preview.Preview |
|
import androidx.core.content.ContextCompat |
|
import com.example.dessertclicker.data.Datasource |
|
import com.example.dessertclicker.model.Dessert |
|
import com.example.dessertclicker.ui.theme.DessertClickerTheme |
|
|
|
// Tag for logging |
|
private const val TAG = "MainActivity" |
|
|
|
class MainActivity : ComponentActivity() { |
|
override fun onCreate(savedInstanceState: Bundle?) { |
|
enableEdgeToEdge() |
|
super.onCreate(savedInstanceState) |
|
Log.d(TAG, "onCreate Called") |
|
setContent { |
|
DessertClickerTheme { |
|
// A surface container using the 'background' color from the theme |
|
Surface( |
|
modifier = Modifier |
|
.fillMaxSize() |
|
.statusBarsPadding(), |
|
) { |
|
DessertClickerApp(desserts = Datasource.dessertList) |
|
} |
|
} |
|
} |
|
} |
|
|
|
override fun onStart() { |
|
super.onStart() |
|
Log.d(TAG, "onStart Called") |
|
} |
|
|
|
override fun onResume() { |
|
super.onResume() |
|
Log.d(TAG, "onResume Called") |
|
} |
|
|
|
override fun onRestart() { |
|
super.onRestart() |
|
Log.d(TAG, "onRestart Called") |
|
} |
|
|
|
override fun onPause() { |
|
super.onPause() |
|
Log.d(TAG, "onPause Called") |
|
} |
|
|
|
override fun onStop() { |
|
super.onStop() |
|
Log.d(TAG, "onStop Called") |
|
} |
|
|
|
override fun onDestroy() { |
|
super.onDestroy() |
|
Log.d(TAG, "onDestroy Called") |
|
} |
|
} |
|
|
|
/** |
|
* Determine which dessert to show. |
|
*/ |
|
fun determineDessertToShow( |
|
desserts: List<Dessert>, |
|
dessertsSold: Int |
|
): Dessert { |
|
var dessertToShow = desserts.first() |
|
for (dessert in desserts) { |
|
if (dessertsSold >= dessert.startProductionAmount) { |
|
dessertToShow = dessert |
|
} else { |
|
// The list of desserts is sorted by startProductionAmount. As you sell more desserts, |
|
// you'll start producing more expensive desserts as determined by startProductionAmount |
|
// We know to break as soon as we see a dessert who's "startProductionAmount" is greater |
|
// than the amount sold. |
|
break |
|
} |
|
} |
|
|
|
return dessertToShow |
|
} |
|
|
|
/** |
|
* Share desserts sold information using ACTION_SEND intent |
|
*/ |
|
private fun shareSoldDessertsInformation(intentContext: Context, dessertsSold: Int, revenue: Int) { |
|
val sendIntent = Intent().apply { |
|
action = Intent.ACTION_SEND |
|
putExtra( |
|
Intent.EXTRA_TEXT, |
|
intentContext.getString(R.string.share_text, dessertsSold, revenue) |
|
) |
|
type = "text/plain" |
|
} |
|
|
|
val shareIntent = Intent.createChooser(sendIntent, null) |
|
|
|
try { |
|
ContextCompat.startActivity(intentContext, shareIntent, null) |
|
} catch (e: ActivityNotFoundException) { |
|
Toast.makeText( |
|
intentContext, |
|
intentContext.getString(R.string.sharing_not_available), |
|
Toast.LENGTH_LONG |
|
).show() |
|
} |
|
} |
|
|
|
@Composable |
|
private fun DessertClickerApp( |
|
desserts: List<Dessert> |
|
) { |
|
|
|
var revenue by rememberSaveable { mutableStateOf(0) } |
|
var dessertsSold by rememberSaveable { mutableStateOf(0) } |
|
|
|
val currentDessertIndex by rememberSaveable { mutableStateOf(0) } |
|
|
|
var currentDessertPrice by rememberSaveable { |
|
mutableStateOf(desserts[currentDessertIndex].price) |
|
} |
|
var currentDessertImageId by rememberSaveable { |
|
mutableStateOf(desserts[currentDessertIndex].imageId) |
|
} |
|
|
|
Scaffold( |
|
topBar = { |
|
val intentContext = LocalContext.current |
|
val layoutDirection = LocalLayoutDirection.current |
|
DessertClickerAppBar( |
|
onShareButtonClicked = { |
|
shareSoldDessertsInformation( |
|
intentContext = intentContext, |
|
dessertsSold = dessertsSold, |
|
revenue = revenue |
|
) |
|
}, |
|
modifier = Modifier |
|
.fillMaxWidth() |
|
.padding( |
|
start = WindowInsets.safeDrawing.asPaddingValues() |
|
.calculateStartPadding(layoutDirection), |
|
end = WindowInsets.safeDrawing.asPaddingValues() |
|
.calculateEndPadding(layoutDirection), |
|
) |
|
.background(MaterialTheme.colorScheme.primary) |
|
) |
|
} |
|
) { contentPadding -> |
|
DessertClickerScreen( |
|
revenue = revenue, |
|
dessertsSold = dessertsSold, |
|
dessertImageId = currentDessertImageId, |
|
onDessertClicked = { |
|
|
|
// Update the revenue |
|
revenue += currentDessertPrice |
|
dessertsSold++ |
|
|
|
// Show the next dessert |
|
val dessertToShow = determineDessertToShow(desserts, dessertsSold) |
|
currentDessertImageId = dessertToShow.imageId |
|
currentDessertPrice = dessertToShow.price |
|
}, |
|
modifier = Modifier.padding(contentPadding) |
|
) |
|
} |
|
} |
|
|
|
@Composable |
|
private fun DessertClickerAppBar( |
|
onShareButtonClicked: () -> Unit, |
|
modifier: Modifier = Modifier |
|
) { |
|
Row( |
|
modifier = modifier, |
|
horizontalArrangement = Arrangement.SpaceBetween, |
|
verticalAlignment = Alignment.CenterVertically, |
|
) { |
|
Text( |
|
text = stringResource(R.string.app_name), |
|
modifier = Modifier.padding(start = dimensionResource(R.dimen.padding_medium)), |
|
color = MaterialTheme.colorScheme.onPrimary, |
|
style = MaterialTheme.typography.titleLarge, |
|
) |
|
IconButton( |
|
onClick = onShareButtonClicked, |
|
modifier = Modifier.padding(end = dimensionResource(R.dimen.padding_medium)), |
|
) { |
|
Icon( |
|
imageVector = Icons.Filled.Share, |
|
contentDescription = stringResource(R.string.share), |
|
tint = MaterialTheme.colorScheme.onPrimary |
|
) |
|
} |
|
} |
|
} |
|
|
|
@Composable |
|
fun DessertClickerScreen( |
|
revenue: Int, |
|
dessertsSold: Int, |
|
@DrawableRes dessertImageId: Int, |
|
onDessertClicked: () -> Unit, |
|
modifier: Modifier = Modifier |
|
) { |
|
Box(modifier = modifier) { |
|
Image( |
|
painter = painterResource(R.drawable.bakery_back), |
|
contentDescription = null, |
|
contentScale = ContentScale.Crop |
|
) |
|
Column { |
|
Box( |
|
modifier = Modifier |
|
.weight(1f) |
|
.fillMaxWidth(), |
|
) { |
|
Image( |
|
painter = painterResource(dessertImageId), |
|
contentDescription = null, |
|
modifier = Modifier |
|
.width(dimensionResource(R.dimen.image_size)) |
|
.height(dimensionResource(R.dimen.image_size)) |
|
.align(Alignment.Center) |
|
.clickable { onDessertClicked() }, |
|
contentScale = ContentScale.Crop, |
|
) |
|
} |
|
TransactionInfo( |
|
revenue = revenue, |
|
dessertsSold = dessertsSold, |
|
modifier = Modifier.background(MaterialTheme.colorScheme.secondaryContainer) |
|
) |
|
} |
|
} |
|
} |
|
|
|
@Composable |
|
private fun TransactionInfo( |
|
revenue: Int, |
|
dessertsSold: Int, |
|
modifier: Modifier = Modifier |
|
) { |
|
Column(modifier = modifier) { |
|
DessertsSoldInfo( |
|
dessertsSold = dessertsSold, |
|
modifier = Modifier |
|
.fillMaxWidth() |
|
.padding(dimensionResource(R.dimen.padding_medium)) |
|
) |
|
RevenueInfo( |
|
revenue = revenue, |
|
modifier = Modifier |
|
.fillMaxWidth() |
|
.padding(dimensionResource(R.dimen.padding_medium)) |
|
) |
|
} |
|
} |
|
|
|
@Composable |
|
private fun RevenueInfo(revenue: Int, modifier: Modifier = Modifier) { |
|
Row( |
|
modifier = modifier, |
|
horizontalArrangement = Arrangement.SpaceBetween, |
|
) { |
|
Text( |
|
text = stringResource(R.string.total_revenue), |
|
style = MaterialTheme.typography.headlineMedium, |
|
color = MaterialTheme.colorScheme.onSecondaryContainer |
|
) |
|
Text( |
|
text = "$${revenue}", |
|
textAlign = TextAlign.Right, |
|
style = MaterialTheme.typography.headlineMedium, |
|
color = MaterialTheme.colorScheme.onSecondaryContainer |
|
) |
|
} |
|
} |
|
|
|
@Composable |
|
private fun DessertsSoldInfo(dessertsSold: Int, modifier: Modifier = Modifier) { |
|
Row( |
|
modifier = modifier, |
|
horizontalArrangement = Arrangement.SpaceBetween, |
|
) { |
|
Text( |
|
text = stringResource(R.string.dessert_sold), |
|
style = MaterialTheme.typography.titleLarge, |
|
color = MaterialTheme.colorScheme.onSecondaryContainer |
|
) |
|
Text( |
|
text = dessertsSold.toString(), |
|
style = MaterialTheme.typography.titleLarge, |
|
color = MaterialTheme.colorScheme.onSecondaryContainer |
|
) |
|
} |
|
} |
|
|
|
@Preview |
|
@Composable |
|
fun MyDessertClickerAppPreview() { |
|
DessertClickerTheme { |
|
DessertClickerApp(listOf(Dessert(R.drawable.cupcake, 5, 0))) |
|
} |
|
} |