Compare commits

..

No commits in common. "ea22a66fa6e4744715d31019bd2efa25b2784ae8" and "fa202ff162b3bc3bf2cb6d165603e54514837506" have entirely different histories.

16 changed files with 101 additions and 156 deletions

View File

@ -1,6 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"local>android/.github:renovate-config"
]
}

View File

@ -5,12 +5,15 @@ This app contains an order flow for cupcakes with options for quantity, flavor,
The order details get displayed on an order summary screen and can be shared to another app to
send the order.
perubahan
TODO
Pre-requisites
--------------
* Experience with Kotlin syntax.
* How to create and run a project in Android Studio.
* How to create composable functions
* TODO
Getting Started

View File

@ -20,12 +20,12 @@ plugins {
android {
namespace = "com.example.cupcake"
compileSdk = 34
compileSdk = 33
defaultConfig {
applicationId = "com.example.cupcake"
minSdk = 24
targetSdk = 34
targetSdk = 33
versionCode = 1
versionName = "1.0"
@ -56,7 +56,7 @@ android {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.3"
kotlinCompilerExtensionVersion = "1.4.7"
}
packaging {
resources {
@ -67,24 +67,24 @@ android {
dependencies {
implementation(platform("androidx.compose:compose-bom:2023.10.01"))
implementation("androidx.activity:activity-compose:1.8.0")
implementation(platform("androidx.compose:compose-bom:2023.05.01"))
implementation("androidx.activity:activity-compose:1.7.2")
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.runtime:runtime")
implementation("androidx.compose.runtime:runtime-livedata")
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.core:core-ktx:1.10.1")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:${rootProject.extra["lifecycle_version"]}")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:${rootProject.extra["lifecycle_version"]}")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:${rootProject.extra["lifecycle_version"]}")
implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:${rootProject.extra["lifecycle_version"]}")
implementation("androidx.navigation:navigation-compose:2.7.4")
implementation("androidx.navigation:navigation-compose:2.5.3")
androidTestImplementation(platform("androidx.compose:compose-bom:2023.10.01"))
androidTestImplementation(platform("androidx.compose:compose-bom:2023.05.01"))
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
androidTestImplementation("androidx.navigation:navigation-testing:2.7.4")
androidTestImplementation("androidx.navigation:navigation-testing:2.5.3")
androidTestImplementation("androidx.test.espresso:espresso-intents:3.5.1")
androidTestImplementation("androidx.test.ext:junit:1.1.5")

View File

@ -76,25 +76,25 @@ class CupcakeOrderScreenTest {
@Test
fun selectOptionScreen_verifyContent() {
// Given list of options
val flavors = listOf("Vanilla", "Chocolate", "Hazelnut", "Cookie", "Mango")
val flavours = listOf("Vanilla", "Chocolate", "Hazelnut", "Cookie", "Mango")
// And sub total
val subtotal = "$100"
val subTotal = "$100"
// When SelectOptionScreen is loaded
composeTestRule.setContent {
SelectOptionScreen(subtotal = subtotal, options = flavors)
SelectOptionScreen(subtotal = subTotal, options = flavours)
}
// Then all the options are displayed on the screen.
flavors.forEach { flavor ->
composeTestRule.onNodeWithText(flavor).assertIsDisplayed()
flavours.forEach { flavour ->
composeTestRule.onNodeWithText(flavour).assertIsDisplayed()
}
// And then the subtotal is displayed correctly.
composeTestRule.onNodeWithText(
composeTestRule.activity.getString(
R.string.subtotal_price,
subtotal
subTotal
)
).assertIsDisplayed()
@ -148,6 +148,6 @@ class CupcakeOrderScreenTest {
R.string.subtotal_price,
fakeOrderUiState.price
)
).assertIsDisplayed()
)
}
}

View File

@ -21,8 +21,6 @@ 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
@ -117,10 +115,7 @@ fun CupcakeApp(
NavHost(
navController = navController,
startDestination = CupcakeScreen.Start.name,
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(innerPadding)
modifier = Modifier.padding(innerPadding)
) {
composable(route = CupcakeScreen.Start.name) {
StartOrderScreen(

View File

@ -18,13 +18,13 @@ package com.example.cupcake
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.core.view.WindowCompat
import com.example.cupcake.ui.theme.CupcakeTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
CupcakeTheme {
CupcakeApp()

View File

@ -19,16 +19,16 @@ import com.example.cupcake.R
object DataSource {
val flavors = listOf(
R.string.coconut,
R.string.vanilla,
R.string.chocolate,
R.string.cheese,
R.string.mocha,
R.string.red_velvet,
R.string.salted_caramel,
R.string.coffee
)
val quantityOptions = listOf(
Pair(R.string.one_bread, 1),
Pair(R.string.six_bread, 6),
Pair(R.string.twelve_bread, 12)
Pair(R.string.one_cupcake, 1),
Pair(R.string.six_cupcakes, 6),
Pair(R.string.twelve_cupcakes, 12)
)
}

View File

@ -27,10 +27,10 @@ import java.util.Calendar
import java.util.Locale
/** Price for a single cupcake */
private const val PRICE_PER_BREAD = 2.50
private const val PRICE_PER_CUPCAKE = 2.00
/** Additional cost for same day pickup of an order */
private const val PRICE_FOR_SAME_DAY_PICKUP = 4.00
private const val PRICE_FOR_SAME_DAY_PICKUP = 3.00
/**
* [OrderViewModel] holds information about a cupcake order in terms of quantity, flavor, and
@ -92,7 +92,7 @@ class OrderViewModel : ViewModel() {
quantity: Int = _uiState.value.quantity,
pickupDate: String = _uiState.value.date
): String {
var calculatedPrice = quantity * PRICE_PER_BREAD
var calculatedPrice = quantity * PRICE_PER_CUPCAKE
// If the user selected the first option (today) for pickup, add the surcharge
if (pickupOptions()[0] == pickupDate) {
calculatedPrice += PRICE_FOR_SAME_DAY_PICKUP

View File

@ -39,7 +39,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.example.cupcake.R
import com.example.cupcake.ui.components.FormattedPriceLabel
import com.example.cupcake.ui.theme.CupcakeTheme
/**
* Composable that displays the list of items as [RadioButton] options,
@ -101,14 +100,12 @@ fun SelectOptionScreen(
Row(
modifier = Modifier
.fillMaxWidth()
.padding(dimensionResource(R.dimen.padding_medium)),
.padding(dimensionResource(R.dimen.padding_medium))
.weight(1f, false),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_medium)),
verticalAlignment = Alignment.Bottom
){
OutlinedButton(
modifier = Modifier.weight(1f),
onClick = onCancelButtonClicked
) {
OutlinedButton(modifier = Modifier.weight(1f), onClick = onCancelButtonClicked) {
Text(stringResource(R.string.cancel))
}
Button(
@ -127,11 +124,9 @@ fun SelectOptionScreen(
@Preview
@Composable
fun SelectOptionPreview(){
CupcakeTheme {
SelectOptionScreen(
subtotal = "299.99",
options = listOf("Option 1", "Option 2", "Option 3", "Option 4"),
modifier = Modifier.fillMaxHeight()
)
}
}

View File

@ -19,7 +19,9 @@ import androidx.annotation.StringRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@ -39,7 +41,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.cupcake.R
import com.example.cupcake.data.DataSource
import com.example.cupcake.ui.theme.CupcakeTheme
/**
* Composable that allows the user to select the desired cupcake quantity and expects
@ -63,17 +64,18 @@ fun StartOrderScreen(
) {
Spacer(modifier = Modifier.height(dimensionResource(R.dimen.padding_medium)))
Image(
painter = painterResource(R.drawable.bread),//bagian yang diganti gambar
painter = painterResource(R.drawable.cupcake),
contentDescription = null,
modifier = Modifier.width(300.dp)
)
Spacer(modifier = Modifier.height(dimensionResource(R.dimen.padding_medium)))
Text(
text = stringResource(R.string.order_bread),
text = stringResource(R.string.order_cupcakes),
style = MaterialTheme.typography.headlineSmall
)
Spacer(modifier = Modifier.height(dimensionResource(R.dimen.padding_small)))
}
Row(modifier = Modifier.weight(1f, false)) {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
@ -84,13 +86,13 @@ fun StartOrderScreen(
quantityOptions.forEach { item ->
SelectQuantityButton(
labelResourceId = item.first,
onClick = { onNextButtonClicked(item.second) },
modifier = Modifier.fillMaxWidth(),
onClick = { onNextButtonClicked(item.second) }
)
}
}
}
}
}
/**
* Customizable button composable that displays the [labelResourceId]
@ -113,13 +115,9 @@ fun SelectQuantityButton(
@Preview
@Composable
fun StartOrderPreview(){
CupcakeTheme {
StartOrderScreen(
quantityOptions = DataSource.quantityOptions,
onNextButtonClicked = {},
modifier = Modifier
.fillMaxSize()
.padding(dimensionResource(R.dimen.padding_medium))
modifier = Modifier.fillMaxSize().padding(dimensionResource(R.dimen.padding_medium))
)
}
}

View File

@ -38,7 +38,6 @@ import androidx.compose.ui.tooling.preview.Preview
import com.example.cupcake.R
import com.example.cupcake.data.OrderUiState
import com.example.cupcake.ui.components.FormattedPriceLabel
import com.example.cupcake.ui.theme.CupcakeTheme
/**
* This composable expects [orderUiState] that represents the order state, [onCancelButtonClicked]
@ -55,7 +54,7 @@ fun OrderSummaryScreen(
val resources = LocalContext.current.resources
val numberOfCupcakes = resources.getQuantityString(
R.plurals.bread,
R.plurals.cupcakes,
orderUiState.quantity,
orderUiState.quantity
)
@ -67,7 +66,7 @@ fun OrderSummaryScreen(
orderUiState.date,
orderUiState.quantity
)
val newOrder = stringResource(R.string.new_bread_order)
val newOrder = stringResource(R.string.new_cupcake_order)
//Create a list of order summary to display
val items = listOf(
// Summary line 1: display selected quantity
@ -98,7 +97,9 @@ fun OrderSummaryScreen(
)
}
Row(
modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium))
modifier = Modifier
.weight(1f, false)
.padding(dimensionResource(R.dimen.padding_medium))
) {
Column(
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_small))
@ -123,7 +124,6 @@ fun OrderSummaryScreen(
@Preview
@Composable
fun OrderSummaryPreview(){
CupcakeTheme {
OrderSummaryScreen(
orderUiState = OrderUiState(0, "Test", "Test", "$300.00"),
onSendButtonClicked = { subject: String, summary: String -> },
@ -131,4 +131,3 @@ fun OrderSummaryPreview() {
modifier = Modifier.fillMaxHeight()
)
}
}

View File

@ -1,39 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="800dp"
android:height="800dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M175.4,866.3c39.1,25.7 166.1,114.6 488.5,-229.7s223,-425.9 196.2,-454.9c-26.8,-29 -263.6,-149.6 -556,122.5C21.6,567 175.4,866.3 175.4,866.3z"
android:fillColor="#EAAD6A"/>
<path
android:pathData="M241.9,696.5s26.7,26.1 48.6,4.5c21.9,-21.7 -2.2,-40.2 -2.2,-40.2L171.6,514l-27.3,67.4 97.6,115.1zM623.3,323.5s26.7,26.1 48.6,4.5c21.9,-21.7 -2.2,-40.2 -2.2,-40.2L565.9,153.9l-62.1,19.2 119.5,150.4zM386.5,463.8s26.7,26.1 48.6,4.5c21.9,-21.7 -2.2,-40.2 -2.2,-40.2L322.4,287.5 280.8,332l105.7,131.8z"
android:fillColor="#FFC661"/>
<path
android:pathData="M604.8,577.4c-227.5,243 -389.5,280.3 -455.1,229.3 10.4,34.3 20.7,54.5 20.7,54.5 39.1,25.7 166.1,114.6 488.5,-229.7s223,-425.9 196.2,-454.9c-6.1,-6.6 -22.9,-17.8 -48.6,-28.1 36.5,54.7 15.2,197.1 -201.7,428.9z"
android:fillColor="#D19152"/>
<path
android:pathData="M310.5,281c-4.3,4 -8.6,8.1 -12.9,12.4 -0.7,0.7 -1.5,1.4 -2.2,2.1 -0.4,0.4 -0.7,0.8 -1.1,1.1 -3.8,3.8 -7.7,7.9 -11.7,12.2 -183.1,193.9 -212.5,464 -112.4,564.1 90.5,90.5 259.8,16.9 489.7,-213 229.8,-229.8 303.5,-399.2 213,-489.7C773.1,70.4 504.2,99.3 310.5,281zM659.6,288.6c3.5,3.1 5.5,7.4 5.6,12 0.2,4.7 -1.6,9.1 -4.9,12.4 -6.3,6.3 -16.3,6.6 -23,0.7l-2.8,-2.4c-37,-32.7 -69.3,-70.4 -95.9,-112L524.3,177c1.2,-0.5 2.5,-0.9 3.7,-1.4 8.8,-3.4 17.6,-6.6 26.3,-9.4 0.4,-0.1 0.7,-0.2 1.1,-0.4l3.4,5.3c28.1,43.6 61.9,83.2 100.8,117.5zM434.6,443.1c3.5,3.1 5.5,7.4 5.6,12 0.2,4.7 -1.6,9.1 -4.9,12.4 -6.3,6.3 -16.3,6.6 -23,0.7l-2.8,-2.4c-37,-32.7 -69.3,-70.4 -95.9,-112l-14.9,-23.4c6.7,-7.2 13.6,-14.3 20.7,-21.2 0.8,-0.8 1.7,-1.6 2.5,-2.4l11.9,18.7c28.1,43.6 61.9,83.2 100.8,117.6zM280.1,668.1c3.5,3.1 5.5,7.4 5.6,12 0.2,4.7 -1.6,9.1 -4.9,12.4 -6.3,6.3 -16.3,6.6 -23,0.7l-2.8,-2.4c-37,-32.7 -69.3,-70.4 -95.8,-112l-0.4,-0.6c3.7,-13.1 7.9,-26.5 12.8,-39.9l7.8,12.2c28,43.6 61.9,83.2 100.7,117.6zM641.1,641.1c-124.2,124.2 -348,317 -452,213 -47.2,-47.2 -60.4,-139.1 -38.7,-241.5 25.2,35.9 54.2,69 87.2,98.1l2.8,2.4c17.1,15.1 43.2,14.3 59.4,-1.8 8.4,-8.4 13.1,-20.1 12.7,-32 -0.4,-11.9 -5.7,-23.3 -14.6,-31.2 -37,-32.7 -69.3,-70.4 -95.9,-112l-18.1,-28.3c23,-54 55.2,-107.7 96.7,-156.7l10.9,17c27.9,43.7 61.8,83.3 100.6,117.6l2.8,2.4c17.1,15.1 43.2,14.4 59.4,-1.8 8.4,-8.4 13.1,-20.1 12.7,-32 -0.4,-11.9 -5.7,-23.3 -14.6,-31.2 -37,-32.7 -69.3,-70.4 -95.9,-112L342,288.4c49,-43.2 103.1,-76.9 157.6,-101.2l16.8,26.3c27.9,43.7 61.8,83.3 100.6,117.6l2.8,2.4c17.1,15.1 43.2,14.3 59.4,-1.8 8.4,-8.4 13.1,-20.1 12.7,-32 -0.4,-11.9 -5.7,-23.3 -14.6,-31.2 -36.7,-32.4 -68.6,-69.8 -95.1,-110.9 114.4,-31.4 220.1,-20.6 272,31.3 103.9,104.2 -88.9,328 -213.1,452.2z"
android:fillColor="#004364"/>
<path
android:pathData="M462,257.8m-15.4,0a15.4,15.4 0,1 0,30.8 0,15.4 15.4,0 1,0 -30.8,0Z"
android:fillColor="#004364"/>
<path
android:pathData="M686.7,188.2m-15.4,0a15.4,15.4 0,1 0,30.8 0,15.4 15.4,0 1,0 -30.8,0Z"
android:fillColor="#004364"/>
<path
android:pathData="M681,520.2m-15.4,0a15.4,15.4 0,1 0,30.8 0,15.4 15.4,0 1,0 -30.8,0Z"
android:fillColor="#004364"/>
<path
android:pathData="M607,601.3m-15.4,0a15.4,15.4 0,1 0,30.8 0,15.4 15.4,0 1,0 -30.8,0Z"
android:fillColor="#004364"/>
<path
android:pathData="M249.5,457.8m-15.4,0a15.4,15.4 0,1 0,30.8 0,15.4 15.4,0 1,0 -30.8,0Z"
android:fillColor="#004364"/>
<path
android:pathData="M392.9,292.7m-15.4,0a15.4,15.4 0,1 0,30.8 0,15.4 15.4,0 1,0 -30.8,0Z"
android:fillColor="#004364"/>
<path
android:pathData="M197,758.3m-15.4,0a15.4,15.4 0,1 0,30.8 0,15.4 15.4,0 1,0 -30.8,0Z"
android:fillColor="#004364"/>
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

View File

@ -14,16 +14,16 @@
limitations under the License.
-->
<resources>
<string name="app_name">Bread</string>
<string name="order_bread">Order Bread</string>
<string name="one_bread">One Bread</string>
<string name="six_bread">Six Breads</string>
<string name="twelve_bread">Twelve Breads</string>
<string name="app_name">Cupcake</string>
<string name="order_cupcakes">Order Cupcakes</string>
<string name="one_cupcake">One Cupcake</string>
<string name="six_cupcakes">Six Cupcakes</string>
<string name="twelve_cupcakes">Twelve Cupcakes</string>
<string name="choose_flavor">Choose Flavor</string>
<string name="coconut">Coconut</string>
<string name="vanilla">Vanilla</string>
<string name="chocolate">Chocolate</string>
<string name="cheese">Cheese</string>
<string name="mocha">Mocha</string>
<string name="red_velvet">Red Velvet</string>
<string name="salted_caramel">Salted Caramel</string>
<string name="coffee">Coffee</string>
<string name="special_flavor">Special Flavor</string>
<string name="cancel">Cancel</string>
@ -36,11 +36,11 @@
<string name="pickup_date">Pickup date</string>
<string name="subtotal_price">Subtotal %s</string>
<string name="total_price">Total %s</string>
<string name="new_bread_order">New Bread Order</string>
<string name="new_cupcake_order">New Cupcake Order</string>
<string name="order_details">Quantity: %1$s \nFlavor: %2$s \nPickup date: %3$s \nTotal: %4$s \n\nThank you!</string>
<string name="back_button">Back</string>
<plurals name="bread">
<item quantity="one">%d bread</item>
<item quantity="other">%d bread</item>
<plurals name="cupcakes">
<item quantity="one">%d cupcake</item>
<item quantity="other">%d cupcakes</item>
</plurals>
</resources>

View File

@ -16,11 +16,11 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
extra.apply {
set("lifecycle_version", "2.6.2")
set("lifecycle_version", "2.6.1")
}
}
plugins {
id("com.android.application") version "8.1.2" apply false
id("com.android.library") version "8.1.2" apply false
id("org.jetbrains.kotlin.android") version "1.9.10" apply false
id("com.android.application") version "8.0.2" apply false
id("com.android.library") version "8.0.2" apply false
id("org.jetbrains.kotlin.android") version "1.8.21" apply false
}

View File

@ -1,6 +1,6 @@
#Sun Mar 19 17:30:22 PDT 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists