diff --git a/app/build.gradle b/app/build.gradle index 7523fcb..d33f5ea 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -43,6 +43,7 @@ android { excludes += '/META-INF/{AL2.0,LGPL2.1}' } } + namespace 'com.example.dessertclicker' } dependencies { @@ -56,4 +57,5 @@ dependencies { implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version" + testImplementation("junit:junit:4.13.2") } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0c5fdde..561a526 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,8 +15,7 @@ limitations under the License. --> + xmlns:tools="http://schemas.android.com/tools"> Unit, - modifier: Modifier = Modifier + onDessertClicked: () -> Unit ) { Scaffold( topBar = { diff --git a/app/src/main/java/com/example/dessertclicker/data/Datasource.kt b/app/src/main/java/com/example/dessertclicker/data/Datasource.kt index 9071928..47af2e8 100644 --- a/app/src/main/java/com/example/dessertclicker/data/Datasource.kt +++ b/app/src/main/java/com/example/dessertclicker/data/Datasource.kt @@ -35,6 +35,7 @@ object Datasource { Dessert(R.drawable.lollipop, 3000, 4000), Dessert(R.drawable.marshmallow, 4000, 8000), Dessert(R.drawable.nougat, 5000, 16000), - Dessert(R.drawable.oreo, 6000, 20000) + Dessert(R.drawable.oreo, 6000, 20000), + Dessert(R.drawable.out_of_stock, 1, 20001) ) } \ No newline at end of file diff --git a/app/src/main/java/com/example/dessertclicker/ui/DessertViewModel.kt b/app/src/main/java/com/example/dessertclicker/ui/DessertViewModel.kt index 6ba1147..8f172a3 100644 --- a/app/src/main/java/com/example/dessertclicker/ui/DessertViewModel.kt +++ b/app/src/main/java/com/example/dessertclicker/ui/DessertViewModel.kt @@ -13,18 +13,34 @@ class DessertViewModel : ViewModel() { private val _dessertUiState = MutableStateFlow(DessertUiState()) val dessertUiState: StateFlow = _dessertUiState.asStateFlow() - fun onDessertClicked() { - _dessertUiState.update { cupcakeUiState -> - val dessertsSold = cupcakeUiState.dessertsSold + 1 - val nextDessertIndex = determineDessertIndex(dessertsSold) - cupcakeUiState.copy( - currentDessertIndex = nextDessertIndex, - revenue = cupcakeUiState.revenue + cupcakeUiState.currentDessertPrice, - dessertsSold = dessertsSold, - currentDessertImageId = dessertList[nextDessertIndex].imageId, - currentDessertPrice = dessertList[nextDessertIndex].price - ) - } + fun onDessertClicked(){ + _dessertUiState.update { cupCakeUiState -> + val dessertsSold: Int = if (cupCakeUiState.currentDessertIndex >= 13){ + cupCakeUiState.dessertsSold + }else{ + cupCakeUiState.dessertsSold + 1 + } + val nextDessertIndex = determineDessertIndex(dessertsSold) + if(cupCakeUiState.currentDessertIndex >= 13){ + cupCakeUiState.copy( + currentDessertIndex = nextDessertIndex, + dessertsSold = dessertsSold, + revenue = cupCakeUiState.revenue, + currentDessertPrice = dessertList[nextDessertIndex].price, + currentDessertImageId = dessertList[nextDessertIndex].imageId + ) + }else{ + cupCakeUiState.copy( + currentDessertIndex = nextDessertIndex, + dessertsSold = dessertsSold, + revenue = cupCakeUiState.revenue + cupCakeUiState.currentDessertPrice, + currentDessertPrice = dessertList[nextDessertIndex].price, + currentDessertImageId = dessertList[nextDessertIndex].imageId + ) + } + + } + } private fun determineDessertIndex(dessertsSold: Int): Int { @@ -32,14 +48,9 @@ class DessertViewModel : ViewModel() { for (index in dessertList.indices) { if (dessertsSold >= dessertList[index].startProductionAmount) { dessertIndex = index - } 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 dessertIndex + return if (dessertIndex <= 12) dessertIndex + else 13 } } \ No newline at end of file diff --git a/app/src/main/res/drawable-nodpi/out_of_stock.png b/app/src/main/res/drawable-nodpi/out_of_stock.png new file mode 100644 index 0000000..b3f04b6 Binary files /dev/null and b/app/src/main/res/drawable-nodpi/out_of_stock.png differ diff --git a/app/src/test/java/com/example/dessertclicker/DessertViewModelTest.kt b/app/src/test/java/com/example/dessertclicker/DessertViewModelTest.kt new file mode 100644 index 0000000..37f3a16 --- /dev/null +++ b/app/src/test/java/com/example/dessertclicker/DessertViewModelTest.kt @@ -0,0 +1,50 @@ +package com.example.dessertclicker + +import com.example.dessertclicker.data.Datasource.dessertList +import com.example.dessertclicker.data.DessertUiState +import com.example.dessertclicker.ui.DessertViewModel +import junit.framework.Assert.assertEquals +import org.junit.Test + +class DessertViewModelTest { + private var viewModel = DessertViewModel() + private var testUiState = DessertUiState() + @Test + fun onDessertClicked_updates_UI_state_correctly() { + // Set the initial state with some values + val initialUiState = DessertUiState( + currentDessertIndex = 0, + dessertsSold = 0, + revenue = 0, + currentDessertPrice = dessertList[0].price, + currentDessertImageId = dessertList[0].imageId + ) + + // Simulate clicking the dessert + viewModel.onDessertClicked() + val nextDessertIndex = testDetermineDessertIndex(initialUiState.dessertsSold) + // Expected UI state after the click + val expectedUiState = initialUiState.copy( + + currentDessertIndex = nextDessertIndex, + dessertsSold = 1, + revenue = dessertList[initialUiState.currentDessertIndex].price, + currentDessertPrice = dessertList[initialUiState.currentDessertIndex].price, + currentDessertImageId = dessertList[initialUiState.currentDessertIndex].imageId + ) + + // Assert that the UI state has been updated correctly + assertEquals(expectedUiState, viewModel.dessertUiState.value) + } + + private fun testDetermineDessertIndex(dessertsSold: Int): Int { + var dessertIndex = 0 + for (index in dessertList.indices) { + if (dessertsSold >= dessertList[index].startProductionAmount) { + dessertIndex = index + } + } + return if (dessertIndex <= 12) dessertIndex + else 13 + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 5d3f21b..877534b 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,8 @@ buildscript { } }// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '7.4.1' apply false - id 'com.android.library' version '7.4.1' apply false + id 'com.android.application' version '8.5.0' apply false + id 'com.android.library' version '8.5.0' apply false id 'org.jetbrains.kotlin.android' version '1.8.0' apply false } diff --git a/gradle.properties b/gradle.properties index cd0519b..022338b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,4 +20,6 @@ kotlin.code.style=official # Enables namespacing of each library's R class so that its R class includes only the # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library -android.nonTransitiveRClass=true \ No newline at end of file +android.nonTransitiveRClass=true +android.defaults.buildfeatures.buildconfig=true +android.nonFinalResIds=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 137e528..6b58354 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Tue Jun 14 16:14:31 EDT 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME