Compare commits
No commits in common. "82b7dc7723a7b2b86e3b5d04bc5e8cab9a6e133b" and "066aa68e80bc0a0a7d5875c7fc0ffee495eef0ce" have entirely different histories.
82b7dc7723
...
066aa68e80
6
.github/renovate.json
vendored
6
.github/renovate.json
vendored
@ -1,6 +0,0 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"local>android/.github:renovate-config"
|
||||
]
|
||||
}
|
||||
@ -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.
|
||||
|
||||
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
|
||||
|
||||
90
app/build.gradle
Normal file
90
app/build.gradle
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.example.cupcake'
|
||||
compileSdk 33
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.example.cupcake"
|
||||
minSdk 24
|
||||
targetSdk 33
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary true
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
freeCompilerArgs += "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api"
|
||||
}
|
||||
buildFeatures {
|
||||
compose true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion '1.4.7'
|
||||
}
|
||||
packagingOptions {
|
||||
resources {
|
||||
excludes += '/META-INF/{AL2.0,LGPL2.1}'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation platform('androidx.compose:compose-bom:2023.05.00')
|
||||
implementation 'androidx.activity:activity-compose:1.7.1'
|
||||
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.10.0'
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
|
||||
implementation 'androidx.navigation:navigation-compose:2.5.3'
|
||||
|
||||
androidTestImplementation platform('androidx.compose:compose-bom:2023.05.00')
|
||||
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
|
||||
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'
|
||||
|
||||
debugImplementation 'androidx.compose.ui:ui-test-manifest'
|
||||
debugImplementation 'androidx.compose.ui:ui-tooling'
|
||||
}
|
||||
@ -1,93 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.example.cupcake"
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.example.cupcake"
|
||||
minSdk = 24
|
||||
targetSdk = 34
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
freeCompilerArgs += "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.3"
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation(platform("androidx.compose:compose-bom:2023.10.01"))
|
||||
implementation("androidx.activity:activity-compose:1.8.0")
|
||||
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.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")
|
||||
|
||||
androidTestImplementation(platform("androidx.compose:compose-bom:2023.10.01"))
|
||||
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
||||
androidTestImplementation("androidx.navigation:navigation-testing:2.7.4")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-intents:3.5.1")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
|
||||
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||
}
|
||||
@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -74,6 +75,7 @@ fun StartOrderScreen(
|
||||
)
|
||||
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))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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]
|
||||
@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,12 +15,12 @@
|
||||
*/
|
||||
// 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")
|
||||
ext {
|
||||
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.1' apply false
|
||||
id 'com.android.library' version '8.0.1' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.8.21' apply false
|
||||
}
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -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.0-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@ -28,4 +28,4 @@ dependencyResolutionManagement {
|
||||
}
|
||||
}
|
||||
rootProject.name = "Cupcake"
|
||||
include(":app")
|
||||
include ':app'
|
||||
Loading…
x
Reference in New Issue
Block a user