diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 1bdc8e679..2d738377c 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -7,19 +7,19 @@
## 완료한 코드랩
완료한 코드랩을 체크 해주세요. 16개의 코드랩 중 12개 이상 완주하셔야 Compose Camp가 준비한 트래킹 완주 기념품을 배송해 드립니다.
-- [ ] Unit1/Pathway3/BirthdayCard
-- [ ] Unit1/Pathway3/ComposeArticle
-- [ ] Unit1/Pathway3/ComposeQuadrant
-- [ ] Unit1/Pathway3/TaskCompleted
-- [ ] Unit2/Pathway2/DiceRoller
-- [ ] Unit2/Pathway2/Lemonade
-- [ ] Unit2/Pathway3/TipTime
-- [ ] Unit2/Pathway3/ArtSpace
-- [ ] Unit3/Pathway2/AffirmationsCodelab
-- [ ] Unit3/Pathway3/WoofCodelab
-- [ ] Unit4/Pathway1/DessertClicker
-- [ ] Unit4/Pathway1/Unscamble
-- [ ] Unit4/Pathway2/CupCake
+- [O] Unit1/Pathway3/BirthdayCard
+- [O] Unit1/Pathway3/ComposeArticle
+- [O] Unit1/Pathway3/ComposeQuadrant
+- [O] Unit1/Pathway3/TaskCompleted
+- [O] Unit2/Pathway2/DiceRoller
+- [O] Unit2/Pathway2/Lemonade
+- [O] Unit2/Pathway3/TipTime
+- [O] Unit2/Pathway3/ArtSpace
+- [O] Unit3/Pathway2/AffirmationsCodelab
+- [O] Unit3/Pathway3/WoofCodelab
+- [O] Unit4/Pathway1/DessertClicker
+- [O] Unit4/Pathway1/Unscamble
+- [O] Unit4/Pathway2/CupCake
- [ ] Unit4/Pathway2/LaunchTray
- [ ] Unit4/Pathway3/ReplyApp
- [ ] Unit4/Pathway3/TrainingSports
diff --git a/Unit1/Pathway3/BirthdayCard/app/src/main/java/com/example/happybirthday/MainActivity.kt b/Unit1/Pathway3/BirthdayCard/app/src/main/java/com/example/happybirthday/MainActivity.kt
index 8fe0316fb..d041cd475 100644
--- a/Unit1/Pathway3/BirthdayCard/app/src/main/java/com/example/happybirthday/MainActivity.kt
+++ b/Unit1/Pathway3/BirthdayCard/app/src/main/java/com/example/happybirthday/MainActivity.kt
@@ -20,6 +20,7 @@ import android.text.Layout
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
+import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.*
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
@@ -38,7 +39,15 @@ import com.example.happybirthday.ui.theme.HappyBirthdayTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContent { }
+ setContent {
+ HappyBirthdayTheme {
+ // A surface container using the 'background' color from the theme
+ Surface(color = MaterialTheme.colors.background) {
+ BirthdayGreetingWithImage(message = getString(R.string.happy_birthday_text), from = getString(
+ R.string.signature_text))
+ }
+ }
+ }
}
}
@@ -46,15 +55,56 @@ class MainActivity : ComponentActivity() {
@Composable
fun BirthdayGreetingWithText(message: String, from: String) {
// Create a column so that texts don't overlap
- Column { }
+ Column {
+ Text(
+ text = message,
+ fontSize = 36.sp,
+ modifier = Modifier
+ .fillMaxWidth()
+// .wrapContentWidth(Alignment.Start)
+ .wrapContentWidth(Alignment.CenterHorizontally)
+ .padding(start = 16.dp, top = 16.dp)
+ )
+ Text(
+ text = from,
+ fontSize = 24.sp,
+ modifier = Modifier
+ .fillMaxWidth()
+// .wrapContentWidth(Alignment.End)
+ .wrapContentWidth(Alignment.CenterHorizontally)
+ .padding(start = 16.dp, end = 16.dp)
+ )
+ }
}
// 5. Box 레이아웃 추
@Composable
-fun BirthdayGreetingWithImage(message: String, from: String) { }
+fun BirthdayGreetingWithImage(message: String, from: String) {
+ val image = painterResource(R.drawable.androidparty)
+ Box {
+ Image(
+ painter = image,
+ contentDescription = null,
+ modifier = Modifier
+ .fillMaxHeight()
+ .fillMaxWidth(),
+ contentScale = ContentScale.Crop
+
+ )
+ BirthdayGreetingWithText(message = message, from = from)
+ }
+
+
+}
// 4. 이미지 컴포저블 추가
-@Preview(showBackground = false)
+@Preview(showBackground = true)
@Composable
-private fun BirthdayCardPreview() { }
+private fun BirthdayCardPreview() {
+ HappyBirthdayTheme() {
+ BirthdayGreetingWithImage(message = stringResource(R.string.preview_happy_birthday_text), from = stringResource(
+ R.string.preview_signature_text) )
+ }
+
+}
diff --git a/Unit1/Pathway3/BirthdayCard/app/src/main/res/drawable-nodpi/androidparty.png b/Unit1/Pathway3/BirthdayCard/app/src/main/res/drawable-nodpi/androidparty.png
new file mode 100644
index 000000000..87124d659
Binary files /dev/null and b/Unit1/Pathway3/BirthdayCard/app/src/main/res/drawable-nodpi/androidparty.png differ
diff --git a/Unit1/Pathway3/BirthdayCard/app/src/main/res/values/strings.xml b/Unit1/Pathway3/BirthdayCard/app/src/main/res/values/strings.xml
index 3a180adfa..9db28cfbc 100644
--- a/Unit1/Pathway3/BirthdayCard/app/src/main/res/values/strings.xml
+++ b/Unit1/Pathway3/BirthdayCard/app/src/main/res/values/strings.xml
@@ -16,4 +16,8 @@
-->
Happy Birthday
+ Happy Birthday DE!
+ - Harim
+ Happy Birthday Sam!
+ - from Emma
\ No newline at end of file
diff --git a/Unit1/Pathway3/ComposeArticle/app/src/main/java/com/example/composearticle/MainActivity.kt b/Unit1/Pathway3/ComposeArticle/app/src/main/java/com/example/composearticle/MainActivity.kt
index da2245997..a4c0c28a6 100644
--- a/Unit1/Pathway3/ComposeArticle/app/src/main/java/com/example/composearticle/MainActivity.kt
+++ b/Unit1/Pathway3/ComposeArticle/app/src/main/java/com/example/composearticle/MainActivity.kt
@@ -4,15 +4,14 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.*
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
@@ -24,12 +23,24 @@ import com.example.composearticle.ui.theme.ComposeArticleTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContent { }
+ setContent {
+ androidx.compose.material.Surface(color = MaterialTheme.colors.background) {
+ ComposeArticleApp()
+ }
+ }
}
}
@Composable
-fun ComposeArticleApp() { }
+fun ComposeArticleApp() {
+ ArticleCard(title = stringResource(R.string.title_jetpack_compose_tutorial),
+ shortDescription = stringResource(R.string.compose_short_desc),
+ longDescription = stringResource(R.string.compose_long_desc),
+ imagePainter = painterResource(id = R.drawable.bg_compose_background),
+ modifier = Modifier
+ )
+
+}
@Composable
private fun ArticleCard(
@@ -39,10 +50,52 @@ private fun ArticleCard(
imagePainter: Painter,
modifier: Modifier = Modifier,
) {
- Column() { }
+ Column() {
+ Image(painter = imagePainter,
+ contentDescription = null,
+ contentScale = ContentScale.FillWidth
+ )
+ Text(
+ text = title,
+ fontSize = 24.sp,
+ modifier = modifier
+ .padding(start = 16.dp, end = 16.dp, bottom = 16.dp, top = 16.dp)
+ )
+ Text(
+ text = shortDescription,
+ textAlign = TextAlign.Justify,
+ modifier = modifier
+ .padding(start = 16.dp, end = 16.dp)
+ )
+ Text(
+ text = longDescription,
+ textAlign = TextAlign.Justify,
+ modifier = modifier
+ .padding(start = 16.dp, end = 16.dp, bottom = 16.dp, top = 16.dp)
+ )
+ }
+
}
@Preview(showBackground = true)
@Composable
-fun DefaultPreview() { }
\ No newline at end of file
+fun DefaultPreview() {
+ ComposeArticleTheme() {
+ Surface {
+ ComposeArticleApp()
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Unit1/Pathway3/ComposeQuadrant/app/src/main/java/com/example/composequadrant/MainActivity.kt b/Unit1/Pathway3/ComposeQuadrant/app/src/main/java/com/example/composequadrant/MainActivity.kt
index 61b87324c..0c59c57fd 100644
--- a/Unit1/Pathway3/ComposeQuadrant/app/src/main/java/com/example/composequadrant/MainActivity.kt
+++ b/Unit1/Pathway3/ComposeQuadrant/app/src/main/java/com/example/composequadrant/MainActivity.kt
@@ -13,6 +13,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
@@ -22,15 +23,56 @@ import com.example.composequadrant.ui.theme.ComposeQuadrantTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContent { }
+ setContent {
+ ComposeQuadrantTheme() {
+ Surface{
+ ComposeQuadrantApp()
+ }
+ }
+ }
}
}
@Composable
fun ComposeQuadrantApp() {
- Column() {
- Row() { }
- Row() { }
+ Column(modifier = Modifier
+ .fillMaxWidth()
+ .fillMaxHeight()
+ ) {
+ Row(
+ modifier = Modifier
+ .weight(1f)
+ ) {
+ ComposableInfoCard(
+ title = stringResource(id = R.string.first_title),
+ description = stringResource(id = R.string.first_description),
+ backgroundColor = Color.Green,
+ modifier = Modifier.weight(1f)
+ )
+ ComposableInfoCard(
+ title = stringResource(id = R.string.second_title),
+ description = stringResource(id = R.string.second_description),
+ backgroundColor = Color.Yellow,
+ modifier = Modifier.weight(1f)
+ )
+ }
+ Row(
+ modifier = Modifier
+ .weight(1f)
+ ) {
+ ComposableInfoCard(
+ title = stringResource(id = R.string.third_title),
+ description = stringResource(id = R.string.third_description),
+ backgroundColor = Color.Cyan,
+ modifier = Modifier.weight(1f)
+ )
+ ComposableInfoCard(
+ title = stringResource(id = R.string.fourth_title),
+ description = stringResource(id = R.string.fourth_description),
+ backgroundColor = Color.LightGray,
+ modifier = Modifier.weight(1f)
+ )
+ }
}
}
@@ -41,10 +83,47 @@ private fun ComposableInfoCard(
backgroundColor: Color,
modifier: Modifier = Modifier
) {
- Column( ) { }
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center,
+ modifier = modifier
+ .fillMaxWidth()
+ .fillMaxHeight()
+ .background(backgroundColor)
+ .padding(16.dp)
+ ) {
+ Text(
+ text = title,
+ fontWeight = FontWeight.Bold,
+ modifier = Modifier.padding(bottom = 16.dp)
+ )
+ Text(
+ text = description,
+ textAlign = TextAlign.Justify
+ )
+ }
}
@Preview(showBackground = true)
@Composable
-fun DefaultPreview() { }
\ No newline at end of file
+fun DefaultPreview() {
+ ComposeQuadrantTheme() {
+ Surface{
+ ComposeQuadrantApp()
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Unit1/Pathway3/TaskCompleted/app/src/main/java/com/example/taskcompleted/MainActivity.kt b/Unit1/Pathway3/TaskCompleted/app/src/main/java/com/example/taskcompleted/MainActivity.kt
index 840cfd96c..85a613869 100644
--- a/Unit1/Pathway3/TaskCompleted/app/src/main/java/com/example/taskcompleted/MainActivity.kt
+++ b/Unit1/Pathway3/TaskCompleted/app/src/main/java/com/example/taskcompleted/MainActivity.kt
@@ -10,7 +10,10 @@ import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
+import androidx.compose.ui.Alignment.Companion.CenterHorizontally
+import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier
+import androidx.compose.ui.modifier.modifierLocalOf
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
@@ -22,15 +25,50 @@ import com.example.taskcompleted.ui.theme.TaskCompletedTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContent { }
+ setContent {
+ TaskCompletedTheme() {
+ TaskCompletedScreen()
+ }
+ }
}
}
@Composable
fun TaskCompletedScreen() {
- Column( ) { }
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .fillMaxHeight(),
+ horizontalAlignment = CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+
+ ) {
+ Image(
+ painter = painterResource(id = R.drawable.ic_task_completed),
+ contentDescription = null
+ )
+ Text(
+ text = stringResource(id = R.string.all_task_completed),
+ fontSize = 24.sp,
+ modifier = Modifier
+ .padding(top = 24.dp, bottom = 8.dp)
+ )
+ Text(
+ text = stringResource(id = R.string.nice_work),
+ fontSize = 16.sp
+ )
+
+ }
}
@Preview(showBackground = true)
@Composable
-fun DefaultPreview() { }
\ No newline at end of file
+fun DefaultPreview() {
+ TaskCompletedTheme() {
+ Surface {
+ TaskCompletedScreen()
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/Unit2/Pathway2/DiceRoller/app/.gitignore b/Unit2/Pathway2/DiceRoller/app/.gitignore
new file mode 100644
index 000000000..42afabfd2
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/Unit2/Pathway2/DiceRoller/app/build.gradle b/Unit2/Pathway2/DiceRoller/app/build.gradle
new file mode 100644
index 000000000..d7123fd83
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/build.gradle
@@ -0,0 +1,65 @@
+plugins {
+ id 'com.android.application'
+ id 'org.jetbrains.kotlin.android'
+}
+
+android {
+ namespace 'com.example.diceroller'
+ compileSdk 32
+
+ defaultConfig {
+ applicationId "com.example.diceroller"
+ minSdk 21
+ targetSdk 32
+ 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'
+ }
+ buildFeatures {
+ compose true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion compose_version
+ }
+ packagingOptions {
+ resources {
+ excludes += '/META-INF/{AL2.0,LGPL2.1}'
+ }
+ }
+}
+
+dependencies {
+
+
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'com.google.android.material:material:1.5.0'
+ implementation 'androidx.activity:activity-compose:1.4.0'
+ implementation "androidx.compose.ui:ui:$compose_version"
+ implementation "androidx.compose.material:material:$compose_version"
+ implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
+ implementation 'androidx.activity:activity-compose:1.4.0'
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
+ debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
+
+}
\ No newline at end of file
diff --git a/Unit2/Pathway2/DiceRoller/app/proguard-rules.pro b/Unit2/Pathway2/DiceRoller/app/proguard-rules.pro
new file mode 100644
index 000000000..481bb4348
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/Unit2/Pathway2/DiceRoller/app/src/androidTest/java/com/example/diceroller/ExampleInstrumentedTest.kt b/Unit2/Pathway2/DiceRoller/app/src/androidTest/java/com/example/diceroller/ExampleInstrumentedTest.kt
new file mode 100644
index 000000000..56697a23d
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/androidTest/java/com/example/diceroller/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.example.diceroller
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.example.diceroller", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/Unit2/Pathway2/DiceRoller/app/src/main/AndroidManifest.xml b/Unit2/Pathway2/DiceRoller/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..6f64e271f
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/main/AndroidManifest.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway2/DiceRoller/app/src/main/java/com/example/diceroller/MainActivity.kt b/Unit2/Pathway2/DiceRoller/app/src/main/java/com/example/diceroller/MainActivity.kt
new file mode 100644
index 000000000..b358e9ebc
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/main/java/com/example/diceroller/MainActivity.kt
@@ -0,0 +1,70 @@
+package com.example.diceroller
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.*
+import androidx.compose.material.Button
+import androidx.compose.material.Text
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.example.diceroller.ui.theme.DiceRollerTheme
+
+class MainActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ DiceRollerTheme {
+ DiceRollerApp()
+ }
+ }
+ }
+}
+
+@Composable
+fun DiceWithButtonAndImage(modifier: Modifier = Modifier) {
+ var result by remember { mutableStateOf(1) }
+ val imageResource = when(result) {
+ 1 -> R.drawable.dice_1
+ 2 -> R.drawable.dice_2
+ 3 -> R.drawable.dice_3
+ 4 -> R.drawable.dice_4
+ 5 -> R.drawable.dice_5
+ else -> R.drawable.dice_6
+ }
+
+ Column (
+ modifier = modifier,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Image(
+ painter = painterResource(id = imageResource),
+ contentDescription = result.toString()
+ )
+ Spacer(modifier = Modifier.height(16.dp))
+ Button(
+ onClick = {
+ result = (1..6).random()
+ }
+ ) {
+ Text(
+ text = stringResource(id = R.string.roll)
+ )
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+fun DiceRollerApp() {
+ DiceWithButtonAndImage(modifier = Modifier
+ .fillMaxSize()
+ .wrapContentSize(Alignment.Center)
+ )
+}
\ No newline at end of file
diff --git a/Unit2/Pathway2/DiceRoller/app/src/main/java/com/example/diceroller/ui/theme/Color.kt b/Unit2/Pathway2/DiceRoller/app/src/main/java/com/example/diceroller/ui/theme/Color.kt
new file mode 100644
index 000000000..a15709145
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/main/java/com/example/diceroller/ui/theme/Color.kt
@@ -0,0 +1,8 @@
+package com.example.diceroller.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple200 = Color(0xFFBB86FC)
+val Purple500 = Color(0xFF6200EE)
+val Purple700 = Color(0xFF3700B3)
+val Teal200 = Color(0xFF03DAC5)
\ No newline at end of file
diff --git a/Unit2/Pathway2/DiceRoller/app/src/main/java/com/example/diceroller/ui/theme/Shape.kt b/Unit2/Pathway2/DiceRoller/app/src/main/java/com/example/diceroller/ui/theme/Shape.kt
new file mode 100644
index 000000000..fc2b35df7
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/main/java/com/example/diceroller/ui/theme/Shape.kt
@@ -0,0 +1,11 @@
+package com.example.diceroller.ui.theme
+
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Shapes
+import androidx.compose.ui.unit.dp
+
+val Shapes = Shapes(
+ small = RoundedCornerShape(4.dp),
+ medium = RoundedCornerShape(4.dp),
+ large = RoundedCornerShape(0.dp)
+)
\ No newline at end of file
diff --git a/Unit2/Pathway2/DiceRoller/app/src/main/java/com/example/diceroller/ui/theme/Theme.kt b/Unit2/Pathway2/DiceRoller/app/src/main/java/com/example/diceroller/ui/theme/Theme.kt
new file mode 100644
index 000000000..69e7a8021
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/main/java/com/example/diceroller/ui/theme/Theme.kt
@@ -0,0 +1,44 @@
+package com.example.diceroller.ui.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.darkColors
+import androidx.compose.material.lightColors
+import androidx.compose.runtime.Composable
+
+private val DarkColorPalette = darkColors(
+ primary = Purple200,
+ primaryVariant = Purple700,
+ secondary = Teal200
+)
+
+private val LightColorPalette = lightColors(
+ primary = Purple500,
+ primaryVariant = Purple700,
+ secondary = Teal200
+
+ /* Other default colors to override
+ background = Color.White,
+ surface = Color.White,
+ onPrimary = Color.White,
+ onSecondary = Color.Black,
+ onBackground = Color.Black,
+ onSurface = Color.Black,
+ */
+)
+
+@Composable
+fun DiceRollerTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
+ val colors = if (darkTheme) {
+ DarkColorPalette
+ } else {
+ LightColorPalette
+ }
+
+ MaterialTheme(
+ colors = colors,
+ typography = Typography,
+ shapes = Shapes,
+ content = content
+ )
+}
\ No newline at end of file
diff --git a/Unit2/Pathway2/DiceRoller/app/src/main/java/com/example/diceroller/ui/theme/Type.kt b/Unit2/Pathway2/DiceRoller/app/src/main/java/com/example/diceroller/ui/theme/Type.kt
new file mode 100644
index 000000000..9719bbc73
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/main/java/com/example/diceroller/ui/theme/Type.kt
@@ -0,0 +1,28 @@
+package com.example.diceroller.ui.theme
+
+import androidx.compose.material.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+ body1 = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp
+ )
+ /* Other default text styles to override
+ button = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.W500,
+ fontSize = 14.sp
+ ),
+ caption = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 12.sp
+ )
+ */
+)
\ No newline at end of file
diff --git a/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 000000000..2b068d114
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/dice_1.xml b/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/dice_1.xml
new file mode 100644
index 000000000..2bd436bb6
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/dice_1.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/dice_2.xml b/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/dice_2.xml
new file mode 100644
index 000000000..30fb235c0
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/dice_2.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/dice_3.xml b/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/dice_3.xml
new file mode 100644
index 000000000..20bf47221
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/dice_3.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/dice_4.xml b/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/dice_4.xml
new file mode 100644
index 000000000..772737d9c
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/dice_4.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/dice_5.xml b/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/dice_5.xml
new file mode 100644
index 000000000..b1e6afe83
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/dice_5.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/dice_6.xml b/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/dice_6.xml
new file mode 100644
index 000000000..484f92e8a
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/dice_6.xml
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/drawable/ic_launcher_background.xml b/Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/ic_launcher_background.xml
similarity index 100%
rename from Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/drawable/ic_launcher_background.xml
rename to Unit2/Pathway2/DiceRoller/app/src/main/res/drawable/ic_launcher_background.xml
diff --git a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
similarity index 100%
rename from Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
rename to Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
diff --git a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
similarity index 100%
rename from Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
rename to Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
diff --git a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-hdpi/ic_launcher.webp
similarity index 100%
rename from Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-hdpi/ic_launcher.webp
rename to Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-hdpi/ic_launcher.webp
diff --git a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
similarity index 100%
rename from Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
rename to Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
diff --git a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-mdpi/ic_launcher.webp
similarity index 100%
rename from Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-mdpi/ic_launcher.webp
rename to Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-mdpi/ic_launcher.webp
diff --git a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
similarity index 100%
rename from Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
rename to Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
diff --git a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
similarity index 100%
rename from Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
rename to Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
diff --git a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
similarity index 100%
rename from Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
rename to Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
diff --git a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
similarity index 100%
rename from Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
rename to Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
diff --git a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
similarity index 100%
rename from Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
rename to Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
diff --git a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
similarity index 100%
rename from Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
rename to Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
diff --git a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
similarity index 100%
rename from Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
rename to Unit2/Pathway2/DiceRoller/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
diff --git a/Unit2/Pathway2/DiceRoller/app/src/main/res/values/colors.xml b/Unit2/Pathway2/DiceRoller/app/src/main/res/values/colors.xml
new file mode 100644
index 000000000..f8c6127d3
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/Unit2/Pathway2/DiceRoller/app/src/main/res/values/strings.xml b/Unit2/Pathway2/DiceRoller/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..51b1bfd90
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+ Dice Roller
+
+ Roll
+
\ No newline at end of file
diff --git a/Unit2/Pathway2/DiceRoller/app/src/main/res/values/themes.xml b/Unit2/Pathway2/DiceRoller/app/src/main/res/values/themes.xml
new file mode 100644
index 000000000..1d1b2f6c1
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/main/res/values/themes.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway2/DiceRoller/app/src/main/res/xml/backup_rules.xml b/Unit2/Pathway2/DiceRoller/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 000000000..fa0f996d2
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway2/DiceRoller/app/src/main/res/xml/data_extraction_rules.xml b/Unit2/Pathway2/DiceRoller/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 000000000..9ee9997b0
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway2/DiceRoller/app/src/test/java/com/example/diceroller/ExampleUnitTest.kt b/Unit2/Pathway2/DiceRoller/app/src/test/java/com/example/diceroller/ExampleUnitTest.kt
new file mode 100644
index 000000000..9136b8558
--- /dev/null
+++ b/Unit2/Pathway2/DiceRoller/app/src/test/java/com/example/diceroller/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.example.diceroller
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/.gitignore b/Unit2/Pathway2/Lemonade/.gitignore
new file mode 100644
index 000000000..aa724b770
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/Unit2/Pathway2/Lemonade/app/.gitignore b/Unit2/Pathway2/Lemonade/app/.gitignore
new file mode 100644
index 000000000..42afabfd2
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/app/build.gradle b/Unit2/Pathway2/Lemonade/app/build.gradle
new file mode 100644
index 000000000..e36798909
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/build.gradle
@@ -0,0 +1,63 @@
+plugins {
+ id 'com.android.application'
+ id 'org.jetbrains.kotlin.android'
+}
+
+android {
+ namespace 'com.example.lemonade'
+ compileSdk 32
+
+ defaultConfig {
+ applicationId "com.example.lemonade"
+ minSdk 21
+ targetSdk 32
+ 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'
+ }
+ buildFeatures {
+ compose true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion '1.1.1'
+ }
+ packagingOptions {
+ resources {
+ excludes += '/META-INF/{AL2.0,LGPL2.1}'
+ }
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
+ implementation 'androidx.activity:activity-compose:1.3.1'
+ implementation "androidx.compose.ui:ui:$compose_ui_version"
+ implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version"
+ implementation 'androidx.compose.material:material:1.1.1'
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.4'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
+ androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version"
+ debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version"
+ debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version"
+}
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/app/proguard-rules.pro b/Unit2/Pathway2/Lemonade/app/proguard-rules.pro
new file mode 100644
index 000000000..481bb4348
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/app/src/androidTest/java/com/example/lemonade/ExampleInstrumentedTest.kt b/Unit2/Pathway2/Lemonade/app/src/androidTest/java/com/example/lemonade/ExampleInstrumentedTest.kt
new file mode 100644
index 000000000..d1cc255b1
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/androidTest/java/com/example/lemonade/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.example.lemonade
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.example.lemonade", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/AndroidManifest.xml b/Unit2/Pathway2/Lemonade/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..2f14b6ee8
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/main/AndroidManifest.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/java/com/example/lemonade/MainActivity.kt b/Unit2/Pathway2/Lemonade/app/src/main/java/com/example/lemonade/MainActivity.kt
new file mode 100644
index 000000000..b0a877036
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/main/java/com/example/lemonade/MainActivity.kt
@@ -0,0 +1,133 @@
+package com.example.lemonade
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Surface
+import androidx.compose.material.Text
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.example.lemonade.ui.theme.LemonadeTheme
+
+class MainActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ LemonadeTheme {
+ // A surface container using the 'background' color from the theme
+ Surface {
+ MakeLemonade()
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun MakeLemonade(){
+ var step by remember { mutableStateOf(1) }
+ var lemonTapCount by remember { mutableStateOf(0) }
+
+ when(step){
+ 1 -> {
+ showEachStep(
+ painter = painterResource(id = R.drawable.lemon_tree),
+ title = stringResource(id = R.string.step1_title),
+ contentDesc = stringResource(id = R.string.step1_content_description),
+ onClickListener = {
+ step = 2
+ lemonTapCount = (2..4).random()
+ }
+ )
+ }
+ 2 -> {
+ showEachStep(
+ painter = painterResource(id = R.drawable.lemon_squeeze),
+ title = stringResource(id = R.string.step2_title),
+ contentDesc = stringResource(id = R.string.step2_content_description),
+ onClickListener = {
+ lemonTapCount --
+ if(lemonTapCount == 0) {
+ step = 3
+ }
+ }
+ )
+ }
+ 3 -> {
+ showEachStep(
+ painter = painterResource(id = R.drawable.lemon_drink),
+ title = stringResource(id = R.string.step3_title),
+ contentDesc = stringResource(id = R.string.step3_content_description),
+ onClickListener = {
+ step = 4
+ }
+ )
+ }
+ else -> {
+ showEachStep(
+ painter = painterResource(id = R.drawable.lemon_restart),
+ title = stringResource(id = R.string.step4_title),
+ contentDesc = stringResource(id = R.string.step4_content_description),
+ onClickListener = {
+ step = 1
+ }
+ )
+ }
+ }
+}
+
+@Composable
+fun showEachStep(
+ painter : Painter,
+ title : String,
+ contentDesc : String,
+ onClickListener : () -> Unit
+){
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center,
+ modifier = Modifier.fillMaxSize()
+ ) {
+ Text(
+ text = title,
+ fontSize = 18.sp
+ )
+ Spacer(modifier = Modifier.height(16.dp))
+ Image(
+ painter = painter,
+ contentDescription = contentDesc,
+ modifier = Modifier
+ .wrapContentSize()
+ .border(
+ BorderStroke(1.dp, Color(105, 205, 216)),
+ shape = RoundedCornerShape(25.dp)
+ )
+ .padding(16.dp)
+ .clickable(
+ onClick = onClickListener
+ )
+ )
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+fun DefaultPreview() {
+ LemonadeTheme {
+ MakeLemonade()
+ }
+}
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/java/com/example/lemonade/ui/theme/Color.kt b/Unit2/Pathway2/Lemonade/app/src/main/java/com/example/lemonade/ui/theme/Color.kt
new file mode 100644
index 000000000..7d85d84f9
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/main/java/com/example/lemonade/ui/theme/Color.kt
@@ -0,0 +1,8 @@
+package com.example.lemonade.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple200 = Color(0xFFBB86FC)
+val Purple500 = Color(0xFF6200EE)
+val Purple700 = Color(0xFF3700B3)
+val Teal200 = Color(0xFF03DAC5)
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/java/com/example/lemonade/ui/theme/Shape.kt b/Unit2/Pathway2/Lemonade/app/src/main/java/com/example/lemonade/ui/theme/Shape.kt
new file mode 100644
index 000000000..7493aa03f
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/main/java/com/example/lemonade/ui/theme/Shape.kt
@@ -0,0 +1,11 @@
+package com.example.lemonade.ui.theme
+
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Shapes
+import androidx.compose.ui.unit.dp
+
+val Shapes = Shapes(
+ small = RoundedCornerShape(4.dp),
+ medium = RoundedCornerShape(4.dp),
+ large = RoundedCornerShape(0.dp)
+)
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/java/com/example/lemonade/ui/theme/Theme.kt b/Unit2/Pathway2/Lemonade/app/src/main/java/com/example/lemonade/ui/theme/Theme.kt
new file mode 100644
index 000000000..3cbd0358d
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/main/java/com/example/lemonade/ui/theme/Theme.kt
@@ -0,0 +1,44 @@
+package com.example.lemonade.ui.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.darkColors
+import androidx.compose.material.lightColors
+import androidx.compose.runtime.Composable
+
+private val DarkColorPalette = darkColors(
+ primary = Purple200,
+ primaryVariant = Purple700,
+ secondary = Teal200
+)
+
+private val LightColorPalette = lightColors(
+ primary = Purple500,
+ primaryVariant = Purple700,
+ secondary = Teal200
+
+ /* Other default colors to override
+ background = Color.White,
+ surface = Color.White,
+ onPrimary = Color.White,
+ onSecondary = Color.Black,
+ onBackground = Color.Black,
+ onSurface = Color.Black,
+ */
+)
+
+@Composable
+fun LemonadeTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
+ val colors = if (darkTheme) {
+ DarkColorPalette
+ } else {
+ LightColorPalette
+ }
+
+ MaterialTheme(
+ colors = colors,
+ typography = Typography,
+ shapes = Shapes,
+ content = content
+ )
+}
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/java/com/example/lemonade/ui/theme/Type.kt b/Unit2/Pathway2/Lemonade/app/src/main/java/com/example/lemonade/ui/theme/Type.kt
new file mode 100644
index 000000000..7481002c8
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/main/java/com/example/lemonade/ui/theme/Type.kt
@@ -0,0 +1,28 @@
+package com.example.lemonade.ui.theme
+
+import androidx.compose.material.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+ body1 = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp
+ )
+ /* Other default text styles to override
+ button = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.W500,
+ fontSize = 14.sp
+ ),
+ caption = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 12.sp
+ )
+ */
+)
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/Unit2/Pathway2/Lemonade/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 000000000..2b068d114
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/drawable/ic_launcher_background.xml b/Unit2/Pathway2/Lemonade/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 000000000..07d5da9cb
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/drawable/lemon_drink.xml b/Unit2/Pathway2/Lemonade/app/src/main/res/drawable/lemon_drink.xml
new file mode 100644
index 000000000..1675978b2
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/main/res/drawable/lemon_drink.xml
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/drawable/lemon_restart.xml b/Unit2/Pathway2/Lemonade/app/src/main/res/drawable/lemon_restart.xml
new file mode 100644
index 000000000..c5f5379bf
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/main/res/drawable/lemon_restart.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/drawable/lemon_squeeze.xml b/Unit2/Pathway2/Lemonade/app/src/main/res/drawable/lemon_squeeze.xml
new file mode 100644
index 000000000..cc90a9324
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/main/res/drawable/lemon_squeeze.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/drawable/lemon_tree.xml b/Unit2/Pathway2/Lemonade/app/src/main/res/drawable/lemon_tree.xml
new file mode 100644
index 000000000..a307871cd
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/main/res/drawable/lemon_tree.xml
@@ -0,0 +1,156 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..eca70cfe5
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000..eca70cfe5
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 000000000..c209e78ec
Binary files /dev/null and b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..b2dfe3d1b
Binary files /dev/null and b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 000000000..4f0f1d64e
Binary files /dev/null and b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..62b611da0
Binary files /dev/null and b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 000000000..948a3070f
Binary files /dev/null and b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..1b9a6956b
Binary files /dev/null and b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 000000000..28d4b77f9
Binary files /dev/null and b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..9287f5083
Binary files /dev/null and b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 000000000..aa7d6427e
Binary files /dev/null and b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..9126ae37c
Binary files /dev/null and b/Unit2/Pathway2/Lemonade/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/values/colors.xml b/Unit2/Pathway2/Lemonade/app/src/main/res/values/colors.xml
new file mode 100644
index 000000000..f8c6127d3
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/values/strings.xml b/Unit2/Pathway2/Lemonade/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..57293f239
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/main/res/values/strings.xml
@@ -0,0 +1,13 @@
+
+ Lemonade
+
+ Tap the lemon tree to select a lemon
+ Keep tapping the lemon to squeeze it
+ Tap the lemonade to drink it
+ Tap the empty glass to start again
+
+ Lemon tree
+ Lemon
+ Glass of lemonade
+ Empty glass
+
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/values/themes.xml b/Unit2/Pathway2/Lemonade/app/src/main/res/values/themes.xml
new file mode 100644
index 000000000..92e729eed
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/main/res/values/themes.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/xml/backup_rules.xml b/Unit2/Pathway2/Lemonade/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 000000000..fa0f996d2
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/app/src/main/res/xml/data_extraction_rules.xml b/Unit2/Pathway2/Lemonade/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 000000000..9ee9997b0
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/app/src/test/java/com/example/lemonade/ExampleUnitTest.kt b/Unit2/Pathway2/Lemonade/app/src/test/java/com/example/lemonade/ExampleUnitTest.kt
new file mode 100644
index 000000000..1569bcbe4
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/app/src/test/java/com/example/lemonade/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.example.lemonade
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/build.gradle b/Unit2/Pathway2/Lemonade/build.gradle
new file mode 100644
index 000000000..15dff9ef8
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/build.gradle
@@ -0,0 +1,10 @@
+buildscript {
+ ext {
+ compose_ui_version = '1.1.1'
+ }
+}// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ id 'com.android.application' version '7.3.1' apply false
+ id 'com.android.library' version '7.3.1' apply false
+ id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
+}
\ No newline at end of file
diff --git a/Unit2/Pathway2/Lemonade/gradle.properties b/Unit2/Pathway2/Lemonade/gradle.properties
new file mode 100644
index 000000000..3c5031eb7
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/gradle.properties
@@ -0,0 +1,23 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+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
diff --git a/Unit2/Pathway2/Lemonade/gradle/wrapper/gradle-wrapper.jar b/Unit2/Pathway2/Lemonade/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..e708b1c02
Binary files /dev/null and b/Unit2/Pathway2/Lemonade/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Unit2/Pathway2/Lemonade/gradle/wrapper/gradle-wrapper.properties b/Unit2/Pathway2/Lemonade/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..a81395f13
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Dec 08 00:32:19 KST 2022
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/Unit2/Pathway2/Lemonade/gradlew b/Unit2/Pathway2/Lemonade/gradlew
new file mode 100644
index 000000000..4f906e0c8
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# 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.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/Unit2/Pathway2/Lemonade/gradlew.bat b/Unit2/Pathway2/Lemonade/gradlew.bat
new file mode 100644
index 000000000..107acd32c
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Unit2/Pathway2/Lemonade/settings.gradle b/Unit2/Pathway2/Lemonade/settings.gradle
new file mode 100644
index 000000000..64e1f070d
--- /dev/null
+++ b/Unit2/Pathway2/Lemonade/settings.gradle
@@ -0,0 +1,16 @@
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ google()
+ mavenCentral()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+rootProject.name = "Lemonade"
+include ':app'
diff --git a/Unit2/Pathway3/ArtSpace/.gitignore b/Unit2/Pathway3/ArtSpace/.gitignore
new file mode 100644
index 000000000..aa724b770
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/Unit2/Pathway3/ArtSpace/app/.gitignore b/Unit2/Pathway3/ArtSpace/app/.gitignore
new file mode 100644
index 000000000..42afabfd2
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/app/build.gradle b/Unit2/Pathway3/ArtSpace/app/build.gradle
new file mode 100644
index 000000000..7a7b53133
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/build.gradle
@@ -0,0 +1,63 @@
+plugins {
+ id 'com.android.application'
+ id 'org.jetbrains.kotlin.android'
+}
+
+android {
+ namespace 'com.example.artspace'
+ compileSdk 32
+
+ defaultConfig {
+ applicationId "com.example.artspace"
+ minSdk 21
+ targetSdk 32
+ 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'
+ }
+ buildFeatures {
+ compose true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion '1.1.1'
+ }
+ packagingOptions {
+ resources {
+ excludes += '/META-INF/{AL2.0,LGPL2.1}'
+ }
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
+ implementation 'androidx.activity:activity-compose:1.3.1'
+ implementation "androidx.compose.ui:ui:$compose_ui_version"
+ implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version"
+ implementation 'androidx.compose.material:material:1.1.1'
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.4'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
+ androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version"
+ debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version"
+ debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version"
+}
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/app/proguard-rules.pro b/Unit2/Pathway3/ArtSpace/app/proguard-rules.pro
new file mode 100644
index 000000000..481bb4348
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/app/src/androidTest/java/com/example/artspace/ExampleInstrumentedTest.kt b/Unit2/Pathway3/ArtSpace/app/src/androidTest/java/com/example/artspace/ExampleInstrumentedTest.kt
new file mode 100644
index 000000000..d778e2354
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/src/androidTest/java/com/example/artspace/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.example.artspace
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.example.artspace", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/AndroidManifest.xml b/Unit2/Pathway3/ArtSpace/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1769642f7
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/src/main/AndroidManifest.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/java/com/example/artspace/MainActivity.kt b/Unit2/Pathway3/ArtSpace/app/src/main/java/com/example/artspace/MainActivity.kt
new file mode 100644
index 000000000..e2da64531
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/src/main/java/com/example/artspace/MainActivity.kt
@@ -0,0 +1,205 @@
+package com.example.artspace
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.*
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Surface
+import androidx.compose.material.Text
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.example.artspace.ui.theme.ArtSpaceTheme
+
+class MainActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ ArtSpaceTheme {
+ // A surface container using the 'background' color from the theme
+ Surface(
+ modifier = Modifier.fillMaxSize(),
+ color = MaterialTheme.colors.background
+ ) {
+ InitArtSpace()
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun InitArtSpace() {
+
+ var page by remember {
+ mutableStateOf(1)
+ }
+
+ when(page){
+ 1 -> {
+ ShowArtSpace(
+ R.drawable.picture_1,
+ R.string.picture_1_title,
+ R.string.picture_1_artist,
+ R.string.picture_1_content_description,
+ previousOnClickListener = {
+ page = 3
+ },
+ nextOnClickListener = {
+ page = 2
+ }
+ )
+ }
+ 2 -> {
+ ShowArtSpace(
+ R.drawable.picture_2,
+ R.string.picture_2_title,
+ R.string.picture_2_artist,
+ R.string.picture_2_content_description,
+ previousOnClickListener = {
+ page = 1
+ },
+ nextOnClickListener = {
+ page = 3
+ }
+ )
+ }
+ else -> {
+ ShowArtSpace(
+ R.drawable.picture_3,
+ R.string.picture_3_title,
+ R.string.picture_3_artist,
+ R.string.picture_3_content_description,
+ previousOnClickListener = {
+ page = 2
+ },
+ nextOnClickListener = {
+ page = 1
+ }
+ )
+ }
+ }
+}
+
+@Composable
+fun ShowArtSpace(
+ imageId : Int,
+ titleId : Int,
+ artistId : Int,
+ contentDescId : Int,
+ previousOnClickListener: () -> Unit,
+ nextOnClickListener: () -> Unit
+){
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center,
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(16.dp)
+ ){
+ Surface(
+ elevation = 5.dp,
+ border = BorderStroke(1.dp, Color.Gray),
+ modifier = Modifier.wrapContentSize()
+ ) {
+ Image(
+ painter = painterResource(id = imageId),
+ contentDescription = stringResource(id = contentDescId),
+ modifier = Modifier.padding(20.dp),
+ contentScale = ContentScale.Inside
+ )
+ }
+ Spacer(modifier = Modifier.height(30.dp))
+
+ Surface(
+ elevation = 5.dp,
+ border = BorderStroke(1.dp, Color.Gray),
+ modifier = Modifier.wrapContentSize()
+ ){
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = Modifier
+ .wrapContentSize()
+ .padding(top = 5.dp, bottom = 5.dp, start = 10.dp, end = 10.dp)
+ ) {
+ Text(
+ text = stringResource(id = titleId),
+ fontSize = 24.sp,
+ fontWeight = FontWeight.Bold,
+ textAlign = TextAlign.Center
+ )
+ Spacer(modifier = Modifier.height(10.dp))
+
+ Text(
+ text = stringResource(id = artistId),
+ textAlign = TextAlign.Center
+ )
+ }
+ }
+
+ Spacer(modifier = Modifier.height(30.dp))
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceAround
+ ) {
+ Text(
+ text = "Previous",
+ fontSize = 20.sp,
+ fontWeight = FontWeight.Bold,
+ textAlign = TextAlign.Center,
+ modifier = Modifier
+ .weight(1f)
+ .background(color = Color.Cyan, shape = RoundedCornerShape(5.dp))
+ .border(
+ BorderStroke(1.dp, color = Color.Black),
+ shape = RoundedCornerShape(5.dp)
+ )
+ .padding(top = 5.dp, bottom = 5.dp, start = 10.dp, end = 10.dp)
+ .clickable(
+ onClick = previousOnClickListener
+ )
+ )
+
+ Spacer(modifier = Modifier.weight(0.5f))
+ Text(
+ text = "Next",
+ fontSize = 20.sp,
+ fontWeight = FontWeight.Bold,
+ textAlign = TextAlign.Center,
+ modifier = Modifier
+ .weight(1f)
+ .background(color = Color.Cyan, shape = RoundedCornerShape(5.dp))
+ .border(
+ BorderStroke(1.dp, color = Color.Black),
+ shape = RoundedCornerShape(5.dp)
+ )
+ .padding(top = 5.dp, bottom = 5.dp, start = 10.dp, end = 10.dp)
+ .clickable(
+ onClick = nextOnClickListener
+ )
+ )
+ }
+
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+fun DefaultPreview() {
+ ArtSpaceTheme {
+ InitArtSpace()
+ }
+}
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/java/com/example/artspace/ui/theme/Color.kt b/Unit2/Pathway3/ArtSpace/app/src/main/java/com/example/artspace/ui/theme/Color.kt
new file mode 100644
index 000000000..51a27509f
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/src/main/java/com/example/artspace/ui/theme/Color.kt
@@ -0,0 +1,8 @@
+package com.example.artspace.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple200 = Color(0xFFBB86FC)
+val Purple500 = Color(0xFF6200EE)
+val Purple700 = Color(0xFF3700B3)
+val Teal200 = Color(0xFF03DAC5)
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/java/com/example/artspace/ui/theme/Shape.kt b/Unit2/Pathway3/ArtSpace/app/src/main/java/com/example/artspace/ui/theme/Shape.kt
new file mode 100644
index 000000000..95b14b077
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/src/main/java/com/example/artspace/ui/theme/Shape.kt
@@ -0,0 +1,11 @@
+package com.example.artspace.ui.theme
+
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Shapes
+import androidx.compose.ui.unit.dp
+
+val Shapes = Shapes(
+ small = RoundedCornerShape(4.dp),
+ medium = RoundedCornerShape(4.dp),
+ large = RoundedCornerShape(0.dp)
+)
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/java/com/example/artspace/ui/theme/Theme.kt b/Unit2/Pathway3/ArtSpace/app/src/main/java/com/example/artspace/ui/theme/Theme.kt
new file mode 100644
index 000000000..0d48ce512
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/src/main/java/com/example/artspace/ui/theme/Theme.kt
@@ -0,0 +1,44 @@
+package com.example.artspace.ui.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.darkColors
+import androidx.compose.material.lightColors
+import androidx.compose.runtime.Composable
+
+private val DarkColorPalette = darkColors(
+ primary = Purple200,
+ primaryVariant = Purple700,
+ secondary = Teal200
+)
+
+private val LightColorPalette = lightColors(
+ primary = Purple500,
+ primaryVariant = Purple700,
+ secondary = Teal200
+
+ /* Other default colors to override
+ background = Color.White,
+ surface = Color.White,
+ onPrimary = Color.White,
+ onSecondary = Color.Black,
+ onBackground = Color.Black,
+ onSurface = Color.Black,
+ */
+)
+
+@Composable
+fun ArtSpaceTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
+ val colors = if (darkTheme) {
+ DarkColorPalette
+ } else {
+ LightColorPalette
+ }
+
+ MaterialTheme(
+ colors = colors,
+ typography = Typography,
+ shapes = Shapes,
+ content = content
+ )
+}
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/java/com/example/artspace/ui/theme/Type.kt b/Unit2/Pathway3/ArtSpace/app/src/main/java/com/example/artspace/ui/theme/Type.kt
new file mode 100644
index 000000000..1ada8704e
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/src/main/java/com/example/artspace/ui/theme/Type.kt
@@ -0,0 +1,28 @@
+package com.example.artspace.ui.theme
+
+import androidx.compose.material.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+ body1 = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp
+ )
+ /* Other default text styles to override
+ button = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.W500,
+ fontSize = 14.sp
+ ),
+ caption = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 12.sp
+ )
+ */
+)
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/Unit2/Pathway3/ArtSpace/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 000000000..2b068d114
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/drawable/ic_launcher_background.xml b/Unit2/Pathway3/ArtSpace/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 000000000..07d5da9cb
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/drawable/picture_1.jpg b/Unit2/Pathway3/ArtSpace/app/src/main/res/drawable/picture_1.jpg
new file mode 100644
index 000000000..c4b4e319c
Binary files /dev/null and b/Unit2/Pathway3/ArtSpace/app/src/main/res/drawable/picture_1.jpg differ
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/drawable/picture_2.jpg b/Unit2/Pathway3/ArtSpace/app/src/main/res/drawable/picture_2.jpg
new file mode 100644
index 000000000..e16796a2a
Binary files /dev/null and b/Unit2/Pathway3/ArtSpace/app/src/main/res/drawable/picture_2.jpg differ
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/drawable/picture_3.jpg b/Unit2/Pathway3/ArtSpace/app/src/main/res/drawable/picture_3.jpg
new file mode 100644
index 000000000..a39ec48af
Binary files /dev/null and b/Unit2/Pathway3/ArtSpace/app/src/main/res/drawable/picture_3.jpg differ
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..eca70cfe5
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000..eca70cfe5
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 000000000..c209e78ec
Binary files /dev/null and b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..b2dfe3d1b
Binary files /dev/null and b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 000000000..4f0f1d64e
Binary files /dev/null and b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..62b611da0
Binary files /dev/null and b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 000000000..948a3070f
Binary files /dev/null and b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..1b9a6956b
Binary files /dev/null and b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 000000000..28d4b77f9
Binary files /dev/null and b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..9287f5083
Binary files /dev/null and b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 000000000..aa7d6427e
Binary files /dev/null and b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..9126ae37c
Binary files /dev/null and b/Unit2/Pathway3/ArtSpace/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/values/colors.xml b/Unit2/Pathway3/ArtSpace/app/src/main/res/values/colors.xml
new file mode 100644
index 000000000..f8c6127d3
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/values/strings.xml b/Unit2/Pathway3/ArtSpace/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..a4a76729f
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/src/main/res/values/strings.xml
@@ -0,0 +1,15 @@
+
+ Art Space
+
+ Grilling Beef
+ DE Hahn(2022.11)
+ Grilling Beef
+
+ Bottles Of Wine
+ DE Hahn(2022.11)
+ Bottles Of Wine
+
+ Fireworks
+ DE Hahn(2022.10)
+ Fireworks
+
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/values/themes.xml b/Unit2/Pathway3/ArtSpace/app/src/main/res/values/themes.xml
new file mode 100644
index 000000000..23a10bedd
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/src/main/res/values/themes.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/xml/backup_rules.xml b/Unit2/Pathway3/ArtSpace/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 000000000..fa0f996d2
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/app/src/main/res/xml/data_extraction_rules.xml b/Unit2/Pathway3/ArtSpace/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 000000000..9ee9997b0
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/app/src/test/java/com/example/artspace/ExampleUnitTest.kt b/Unit2/Pathway3/ArtSpace/app/src/test/java/com/example/artspace/ExampleUnitTest.kt
new file mode 100644
index 000000000..fde12fe0b
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/app/src/test/java/com/example/artspace/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.example.artspace
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/build.gradle b/Unit2/Pathway3/ArtSpace/build.gradle
new file mode 100644
index 000000000..15dff9ef8
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/build.gradle
@@ -0,0 +1,10 @@
+buildscript {
+ ext {
+ compose_ui_version = '1.1.1'
+ }
+}// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ id 'com.android.application' version '7.3.1' apply false
+ id 'com.android.library' version '7.3.1' apply false
+ id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
+}
\ No newline at end of file
diff --git a/Unit2/Pathway3/ArtSpace/gradle.properties b/Unit2/Pathway3/ArtSpace/gradle.properties
new file mode 100644
index 000000000..3c5031eb7
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/gradle.properties
@@ -0,0 +1,23 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+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
diff --git a/Unit2/Pathway3/ArtSpace/gradle/wrapper/gradle-wrapper.jar b/Unit2/Pathway3/ArtSpace/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..e708b1c02
Binary files /dev/null and b/Unit2/Pathway3/ArtSpace/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Unit2/Pathway3/ArtSpace/gradle/wrapper/gradle-wrapper.properties b/Unit2/Pathway3/ArtSpace/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..4c0553fe4
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Dec 09 00:05:58 KST 2022
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/Unit2/Pathway3/ArtSpace/gradlew b/Unit2/Pathway3/ArtSpace/gradlew
new file mode 100644
index 000000000..4f906e0c8
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# 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.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/Unit2/Pathway3/ArtSpace/gradlew.bat b/Unit2/Pathway3/ArtSpace/gradlew.bat
new file mode 100644
index 000000000..107acd32c
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Unit2/Pathway3/ArtSpace/settings.gradle b/Unit2/Pathway3/ArtSpace/settings.gradle
new file mode 100644
index 000000000..a7ef376e9
--- /dev/null
+++ b/Unit2/Pathway3/ArtSpace/settings.gradle
@@ -0,0 +1,16 @@
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ google()
+ mavenCentral()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+rootProject.name = "Art Space"
+include ':app'
diff --git a/Unit2/Pathway3/TipTime/.gitignore b/Unit2/Pathway3/TipTime/.gitignore
new file mode 100644
index 000000000..aa724b770
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/Unit2/Pathway3/TipTime/app/.gitignore b/Unit2/Pathway3/TipTime/app/.gitignore
new file mode 100644
index 000000000..42afabfd2
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/app/build.gradle b/Unit2/Pathway3/TipTime/app/build.gradle
new file mode 100644
index 000000000..8bf1e4563
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/build.gradle
@@ -0,0 +1,63 @@
+plugins {
+ id 'com.android.application'
+ id 'org.jetbrains.kotlin.android'
+}
+
+android {
+ namespace 'com.example.tiptime'
+ compileSdk 32
+
+ defaultConfig {
+ applicationId "com.example.tiptime"
+ minSdk 21
+ targetSdk 32
+ 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'
+ }
+ buildFeatures {
+ compose true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion '1.2.0'
+ }
+ packagingOptions {
+ resources {
+ excludes += '/META-INF/{AL2.0,LGPL2.1}'
+ }
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
+ implementation 'androidx.activity:activity-compose:1.5.1'
+ implementation "androidx.compose.ui:ui:$compose_ui_version"
+ implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version"
+ implementation "androidx.compose.material:material:$compose_ui_version"
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.4'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
+ androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version"
+ debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version"
+ debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version"
+}
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/app/proguard-rules.pro b/Unit2/Pathway3/TipTime/app/proguard-rules.pro
new file mode 100644
index 000000000..481bb4348
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/app/src/androidTest/java/com/example/tiptime/ExampleInstrumentedTest.kt b/Unit2/Pathway3/TipTime/app/src/androidTest/java/com/example/tiptime/ExampleInstrumentedTest.kt
new file mode 100644
index 000000000..f21106b85
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/src/androidTest/java/com/example/tiptime/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.example.tiptime
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.example.tiptime", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/app/src/main/AndroidManifest.xml b/Unit2/Pathway3/TipTime/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..66c593744
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/src/main/AndroidManifest.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/app/src/main/java/com/example/tiptime/MainActivity.kt b/Unit2/Pathway3/TipTime/app/src/main/java/com/example/tiptime/MainActivity.kt
new file mode 100644
index 000000000..793446f90
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/src/main/java/com/example/tiptime/MainActivity.kt
@@ -0,0 +1,99 @@
+package com.example.tiptime
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.Text
+import androidx.compose.material.TextField
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.modifier.modifierLocalConsumer
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.core.content.pm.ShortcutInfoCompat.Surface
+import com.example.tiptime.ui.theme.TipTimeTheme
+import java.text.NumberFormat
+
+class MainActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ TipTimeTheme {
+ // A surface container using the 'background' color from the theme
+ TipTimeScreen()
+ }
+ }
+ }
+}
+
+@Composable
+fun TipTimeScreen() {
+ var amountInput by remember {
+ mutableStateOf("")
+ }
+ val amount = amountInput.toDoubleOrNull() ?: 0.0
+ val tip = calculateTip(amount)
+
+ Column(
+ modifier = Modifier.padding(32.dp),
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ Text(
+ text = stringResource(R.string.calculate_tip),
+ fontSize = 24.sp,
+ modifier = Modifier.align(Alignment.CenterHorizontally)
+ )
+ Spacer(Modifier.height(16.dp))
+ EditNumberField(
+ value = amountInput,
+ onValueChange = {amountInput = it}
+ )
+ Spacer(Modifier.height(24.dp))
+ Text(
+ text = stringResource(R.string.tip_amount, tip),
+ textAlign = TextAlign.Center,
+ fontSize = 20.sp,
+ fontWeight = FontWeight.Bold
+ )
+
+ }
+}
+
+@Composable
+fun EditNumberField(
+ value : String,
+ onValueChange : (String) -> Unit
+) {
+ TextField(
+ value = value,
+ onValueChange = onValueChange,
+ label = { Text(stringResource(R.string.cost_of_service)) },
+ modifier = Modifier.fillMaxWidth(),
+ singleLine = true,
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
+ )
+}
+
+private fun calculateTip(
+ amount: Double,
+ tipPercent: Double = 15.0
+) : String {
+ val tip = tipPercent / 100 * amount
+ return NumberFormat.getCurrencyInstance().format(tip)
+}
+
+@Preview(showBackground = true)
+@Composable
+fun DefaultPreview() {
+ TipTimeTheme {
+ TipTimeScreen()
+ }
+}
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/app/src/main/java/com/example/tiptime/ui/theme/Color.kt b/Unit2/Pathway3/TipTime/app/src/main/java/com/example/tiptime/ui/theme/Color.kt
new file mode 100644
index 000000000..d5aa49053
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/src/main/java/com/example/tiptime/ui/theme/Color.kt
@@ -0,0 +1,8 @@
+package com.example.tiptime.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple200 = Color(0xFFBB86FC)
+val Purple500 = Color(0xFF6200EE)
+val Purple700 = Color(0xFF3700B3)
+val Teal200 = Color(0xFF03DAC5)
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/app/src/main/java/com/example/tiptime/ui/theme/Shape.kt b/Unit2/Pathway3/TipTime/app/src/main/java/com/example/tiptime/ui/theme/Shape.kt
new file mode 100644
index 000000000..764529ae6
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/src/main/java/com/example/tiptime/ui/theme/Shape.kt
@@ -0,0 +1,11 @@
+package com.example.tiptime.ui.theme
+
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Shapes
+import androidx.compose.ui.unit.dp
+
+val Shapes = Shapes(
+ small = RoundedCornerShape(4.dp),
+ medium = RoundedCornerShape(4.dp),
+ large = RoundedCornerShape(0.dp)
+)
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/app/src/main/java/com/example/tiptime/ui/theme/Theme.kt b/Unit2/Pathway3/TipTime/app/src/main/java/com/example/tiptime/ui/theme/Theme.kt
new file mode 100644
index 000000000..27d8d98c8
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/src/main/java/com/example/tiptime/ui/theme/Theme.kt
@@ -0,0 +1,44 @@
+package com.example.tiptime.ui.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.darkColors
+import androidx.compose.material.lightColors
+import androidx.compose.runtime.Composable
+
+private val DarkColorPalette = darkColors(
+ primary = Purple200,
+ primaryVariant = Purple700,
+ secondary = Teal200
+)
+
+private val LightColorPalette = lightColors(
+ primary = Purple500,
+ primaryVariant = Purple700,
+ secondary = Teal200
+
+ /* Other default colors to override
+ background = Color.White,
+ surface = Color.White,
+ onPrimary = Color.White,
+ onSecondary = Color.Black,
+ onBackground = Color.Black,
+ onSurface = Color.Black,
+ */
+)
+
+@Composable
+fun TipTimeTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
+ val colors = if (darkTheme) {
+ DarkColorPalette
+ } else {
+ LightColorPalette
+ }
+
+ MaterialTheme(
+ colors = colors,
+ typography = Typography,
+ shapes = Shapes,
+ content = content
+ )
+}
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/app/src/main/java/com/example/tiptime/ui/theme/Type.kt b/Unit2/Pathway3/TipTime/app/src/main/java/com/example/tiptime/ui/theme/Type.kt
new file mode 100644
index 000000000..c3259af69
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/src/main/java/com/example/tiptime/ui/theme/Type.kt
@@ -0,0 +1,28 @@
+package com.example.tiptime.ui.theme
+
+import androidx.compose.material.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+ body1 = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp
+ )
+ /* Other default text styles to override
+ button = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.W500,
+ fontSize = 14.sp
+ ),
+ caption = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 12.sp
+ )
+ */
+)
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/Unit2/Pathway3/TipTime/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 000000000..2b068d114
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/app/src/main/res/drawable/ic_launcher_background.xml b/Unit2/Pathway3/TipTime/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 000000000..07d5da9cb
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..eca70cfe5
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000..eca70cfe5
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 000000000..c209e78ec
Binary files /dev/null and b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..b2dfe3d1b
Binary files /dev/null and b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 000000000..4f0f1d64e
Binary files /dev/null and b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..62b611da0
Binary files /dev/null and b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 000000000..948a3070f
Binary files /dev/null and b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..1b9a6956b
Binary files /dev/null and b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 000000000..28d4b77f9
Binary files /dev/null and b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..9287f5083
Binary files /dev/null and b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 000000000..aa7d6427e
Binary files /dev/null and b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..9126ae37c
Binary files /dev/null and b/Unit2/Pathway3/TipTime/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/Unit2/Pathway3/TipTime/app/src/main/res/values/colors.xml b/Unit2/Pathway3/TipTime/app/src/main/res/values/colors.xml
new file mode 100644
index 000000000..f8c6127d3
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/app/src/main/res/values/strings.xml b/Unit2/Pathway3/TipTime/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..f0decabbc
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+ Tip Time
+ Calculate Tip
+ Cost of Service
+ Tip amount: %s
+
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/app/src/main/res/values/themes.xml b/Unit2/Pathway3/TipTime/app/src/main/res/values/themes.xml
new file mode 100644
index 000000000..941c637ec
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/src/main/res/values/themes.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/app/src/main/res/xml/backup_rules.xml b/Unit2/Pathway3/TipTime/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 000000000..fa0f996d2
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/app/src/main/res/xml/data_extraction_rules.xml b/Unit2/Pathway3/TipTime/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 000000000..9ee9997b0
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/app/src/test/java/com/example/tiptime/ExampleUnitTest.kt b/Unit2/Pathway3/TipTime/app/src/test/java/com/example/tiptime/ExampleUnitTest.kt
new file mode 100644
index 000000000..f3c5af88d
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/app/src/test/java/com/example/tiptime/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.example.tiptime
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/build.gradle b/Unit2/Pathway3/TipTime/build.gradle
new file mode 100644
index 000000000..c7cf3d983
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/build.gradle
@@ -0,0 +1,10 @@
+buildscript {
+ ext {
+ compose_ui_version = '1.2.0'
+ }
+}// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ id 'com.android.application' version '7.3.1' apply false
+ id 'com.android.library' version '7.3.1' apply false
+ id 'org.jetbrains.kotlin.android' version '1.7.0' apply false
+}
\ No newline at end of file
diff --git a/Unit2/Pathway3/TipTime/gradle.properties b/Unit2/Pathway3/TipTime/gradle.properties
new file mode 100644
index 000000000..3c5031eb7
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/gradle.properties
@@ -0,0 +1,23 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+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
diff --git a/Unit2/Pathway3/TipTime/gradle/wrapper/gradle-wrapper.jar b/Unit2/Pathway3/TipTime/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..e708b1c02
Binary files /dev/null and b/Unit2/Pathway3/TipTime/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Unit2/Pathway3/TipTime/gradle/wrapper/gradle-wrapper.properties b/Unit2/Pathway3/TipTime/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..0e3fd8ce4
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Dec 08 01:59:06 KST 2022
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/Unit2/Pathway3/TipTime/gradlew b/Unit2/Pathway3/TipTime/gradlew
new file mode 100644
index 000000000..4f906e0c8
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# 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.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/Unit2/Pathway3/TipTime/gradlew.bat b/Unit2/Pathway3/TipTime/gradlew.bat
new file mode 100644
index 000000000..107acd32c
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Unit2/Pathway3/TipTime/settings.gradle b/Unit2/Pathway3/TipTime/settings.gradle
new file mode 100644
index 000000000..09edcfc33
--- /dev/null
+++ b/Unit2/Pathway3/TipTime/settings.gradle
@@ -0,0 +1,16 @@
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ google()
+ mavenCentral()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+rootProject.name = "Tip Time"
+include ':app'
diff --git a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/ic_launcher-playstore.png b/Unit3/Pathway2/AffirmationsCodelab/app/src/main/ic_launcher-playstore.png
new file mode 100644
index 000000000..6b5b3a213
Binary files /dev/null and b/Unit3/Pathway2/AffirmationsCodelab/app/src/main/ic_launcher-playstore.png differ
diff --git a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/java/com/example/affirmationscodelab/MainActivity.kt b/Unit3/Pathway2/AffirmationsCodelab/app/src/main/java/com/example/affirmationscodelab/MainActivity.kt
index dc2023b3b..7ce972bfb 100644
--- a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/java/com/example/affirmationscodelab/MainActivity.kt
+++ b/Unit3/Pathway2/AffirmationsCodelab/app/src/main/java/com/example/affirmationscodelab/MainActivity.kt
@@ -17,8 +17,25 @@ package com.example.affirmationscodelab
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
-import androidx.compose.material.Scaffold
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material.Card
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.example.affirmationscodelab.data.Datasource
+import com.example.affirmationscodelab.model.Affirmation
import com.example.affirmationscodelab.ui.theme.AffirmationsTheme
class MainActivity : ComponentActivity() {
@@ -33,5 +50,73 @@ class MainActivity : ComponentActivity() {
@Composable
fun AffirmationApp() {
AffirmationsTheme {
+ AffirmationList(affirmationList = Datasource().loadAffirmations())
}
}
+
+@Composable
+fun AffirmationCard(
+ affirmation: Affirmation,
+ modifier: Modifier = Modifier
+){
+ Card(
+ modifier = modifier.padding(8.dp),
+ elevation = 4.dp
+ ) {
+ Column() {
+ Image(
+ painter = painterResource(id = affirmation.imageResourceId),
+ contentDescription = stringResource(id = affirmation.stringResourceId),
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(194.dp),
+ contentScale = ContentScale.Crop
+ )
+ Text(
+ text = stringResource(affirmation.stringResourceId),
+ modifier = Modifier.padding(16.dp),
+ style = MaterialTheme.typography.h6
+ )
+ }
+ }
+
+}
+
+@Composable
+private fun AffirmationList(
+ affirmationList: List,
+ modifier: Modifier = Modifier) {
+ LazyColumn {
+ items(affirmationList){
+ item: Affirmation -> AffirmationCard(affirmation = item)
+ }
+ }
+}
+
+
+
+@Preview
+@Composable
+private fun AffirmationCardPreview(){
+ AffirmationCard(Affirmation(R.string.affirmation1, R.drawable.image1))
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/java/com/example/affirmationscodelab/data/Datasource.kt b/Unit3/Pathway2/AffirmationsCodelab/app/src/main/java/com/example/affirmationscodelab/data/Datasource.kt
index 7d99d4916..932017066 100644
--- a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/java/com/example/affirmationscodelab/data/Datasource.kt
+++ b/Unit3/Pathway2/AffirmationsCodelab/app/src/main/java/com/example/affirmationscodelab/data/Datasource.kt
@@ -15,12 +15,13 @@
*/
package com.example.affirmationscodelab.data
import com.example.affirmationscodelab.R
+import com.example.affirmationscodelab.model.Affirmation
/**
* [Datasource] generates a list of [Affirmation]
*/
class Datasource() {
- /*fun loadAffirmations(): List {
+ fun loadAffirmations(): List {
return listOf(
Affirmation(R.string.affirmation1, R.drawable.image1),
Affirmation(R.string.affirmation2, R.drawable.image2),
@@ -32,5 +33,5 @@ class Datasource() {
Affirmation(R.string.affirmation8, R.drawable.image8),
Affirmation(R.string.affirmation9, R.drawable.image9),
Affirmation(R.string.affirmation10, R.drawable.image10))
- }*/
+ }
}
diff --git a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/java/com/example/affirmationscodelab/model/Affirmation.kt b/Unit3/Pathway2/AffirmationsCodelab/app/src/main/java/com/example/affirmationscodelab/model/Affirmation.kt
new file mode 100644
index 000000000..8d02936cc
--- /dev/null
+++ b/Unit3/Pathway2/AffirmationsCodelab/app/src/main/java/com/example/affirmationscodelab/model/Affirmation.kt
@@ -0,0 +1,9 @@
+package com.example.affirmationscodelab.model
+
+import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
+
+data class Affirmation(
+ @StringRes val stringResourceId: Int,
+ @DrawableRes val imageResourceId: Int
+)
diff --git a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/drawable-v24/ic_launcher_background.xml b/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/drawable-v24/ic_launcher_background.xml
index 88a65b4b7..ddbaedc2d 100644
--- a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/drawable-v24/ic_launcher_background.xml
+++ b/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/drawable-v24/ic_launcher_background.xml
@@ -21,18 +21,23 @@
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
index 2b068d114..7f6440ef3 100644
--- a/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ b/Unit3/Pathway2/AffirmationsCodelab/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -1,30 +1,14 @@
-
-
-
-
-
-
-
-
+
+ android:pathData="M38.19,65.92L69.32,65.92a1.2,1.2 0,0 0,1.2 -1.2,0.89 0.89,0 0,0 0,-0.23 17,17 0,0 0,-33.25 0,1.18 1.18,0 0,0 0.9,1.42ZM74.53,69.05a0.85,0.85 0,0 0,-0.78 -0.84L34,68.21a0.85,0.85 0,0 0,-0.77 0.84v2.82a0.84,0.84 0,0 0,0.77 0.86L73.78,72.73a0.85,0.85 0,0 0,0.77 -0.86L74.55,70.65C74.55,70.24 74.56,69.58 74.53,69.05ZM52.08,49h3.59a1.86,1.86 0,0 0,0 -3.72L52.08,45.28a1.86,1.86 0,0 0,0 3.72ZM53.87,39.81a1.19,1.19 0,0 0,1.19 -1.19L55.06,32.87a1.19,1.19 0,0 0,-2.38 0v5.71a1.19,1.19 0,0 0,1.19 1.19h0ZM61.69,41l4.62,-3.35a1.19,1.19 0,1 0,-1.4 -1.93L60.29,39A1.2,1.2 0,0 0,60 40.67a1.18,1.18 0,0 0,1.66 0.26ZM41.69,37.65L46.31,41a1.2,1.2 0,0 0,1.35 -2L43,35.66a1.19,1.19 0,0 0,-1.66 0.26,1.2 1.2,0 0,0 0.3,1.67Z"
+ android:fillColor="#FFFFFF"/>
+
\ No newline at end of file
diff --git a/Unit3/Pathway3/WoofCodelab/app/build.gradle b/Unit3/Pathway3/WoofCodelab/app/build.gradle
index 854a8f86b..4d0e207cc 100644
--- a/Unit3/Pathway3/WoofCodelab/app/build.gradle
+++ b/Unit3/Pathway3/WoofCodelab/app/build.gradle
@@ -71,4 +71,6 @@ dependencies {
implementation "androidx.compose.material:material:$compose_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
+
+ implementation "androidx.compose.material:material-icons-extended:$compose_version"
}
\ No newline at end of file
diff --git a/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/MainActivity.kt b/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/MainActivity.kt
index 26d4256d7..237f49958 100644
--- a/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/MainActivity.kt
+++ b/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/MainActivity.kt
@@ -20,23 +20,32 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
+import androidx.compose.animation.animateColorAsState
+import androidx.compose.animation.animateContentSize
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.spring
import androidx.compose.foundation.Image
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
-import androidx.compose.material.Text
-import androidx.compose.runtime.Composable
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.*
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ExpandLess
+import androidx.compose.material.icons.filled.ExpandMore
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.woofcodelab.data.Dog
import com.example.woofcodelab.data.dogs
+import com.example.woofcodelab.ui.theme.Green100
import com.example.woofcodelab.ui.theme.WoofTheme
class MainActivity : ComponentActivity() {
@@ -55,13 +64,41 @@ class MainActivity : ComponentActivity() {
*/
@Composable
fun WoofApp() {
- LazyColumn {
- items(dogs) {
- DogItem(dog = it)
+ Scaffold(
+ topBar = {
+ WoofTopAppBar()
+ }
+ ) {
+ LazyColumn(modifier = Modifier.background(MaterialTheme.colors.background)) {
+ items(dogs) {
+ DogItem(dog = it)
+ }
}
}
}
+@Composable
+fun WoofTopAppBar(modifier: Modifier = Modifier) {
+ Row(modifier = modifier
+ .fillMaxWidth()
+ .background(color = MaterialTheme.colors.primary),
+ verticalAlignment = Alignment.CenterVertically
+
+ ) {
+ Image(
+ modifier = Modifier
+ .size(64.dp)
+ .padding(8.dp),
+ painter = painterResource(R.drawable.ic_woof_logo),
+ contentDescription = null
+ )
+ Text(
+ text = stringResource(R.string.app_name),
+ style = MaterialTheme.typography.h1
+ )
+ }
+}
+
/**
* Composable that displays a list item containing a dog icon and their information.
*
@@ -70,13 +107,43 @@ fun WoofApp() {
*/
@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(8.dp)
+ var expanded by remember { mutableStateOf(false) }
+
+ val color by animateColorAsState(
+ targetValue = if(expanded) Green100 else MaterialTheme.colors.surface
+ )
+
+ Card(
+ modifier = modifier.padding(8.dp),
+ elevation = 4.dp
) {
- DogIcon(dog.imageResourceId)
- DogInformation(dog.name, dog.age)
+ Column(
+ modifier = Modifier
+ .animateContentSize(
+ animationSpec = spring(
+ dampingRatio = Spring.DampingRatioMediumBouncy,
+ stiffness = Spring.StiffnessLow
+ )
+ )
+ .background(color = color)
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(8.dp)
+ ) {
+ DogIcon(dog.imageResourceId)
+ DogInformation(dog.name, dog.age)
+ Spacer(Modifier.weight(1f))
+ DogItemButton(
+ expanded = expanded,
+ onClick = { expanded = !expanded }
+ )
+ }
+ if(expanded){
+ DogHobby(dogHobby = dog.hobbies)
+ }
+ }
}
}
@@ -91,7 +158,9 @@ fun DogIcon(@DrawableRes dogIcon: Int, modifier: Modifier = Modifier) {
Image(
modifier = modifier
.size(64.dp)
- .padding(8.dp),
+ .padding(8.dp)
+ .clip(RoundedCornerShape(50.dp)),
+ contentScale = ContentScale.Crop,
painter = painterResource(dogIcon),
/*
* Content Description is not needed here - image is decorative, and setting a null content
@@ -113,10 +182,51 @@ fun DogInformation(@StringRes dogName: Int, dogAge: Int, modifier: Modifier = Mo
Column {
Text(
text = stringResource(dogName),
+ style = MaterialTheme.typography.h2,
modifier = modifier.padding(top = 8.dp)
)
Text(
- text = stringResource(R.string.years_old, dogAge)
+ text = stringResource(R.string.years_old, dogAge),
+ style = MaterialTheme.typography.body1,
+
+ )
+ }
+}
+
+@Composable
+private fun DogItemButton(
+ expanded: Boolean,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier
+) {
+ IconButton(onClick = onClick) {
+ Icon(
+ imageVector = if(expanded) Icons.Filled.ExpandLess
+ else Icons.Filled.ExpandMore,
+ tint = MaterialTheme.colors.secondary,
+ contentDescription = stringResource(R.string.expand_button_content_description)
+ )
+ }
+}
+
+
+@Composable
+fun DogHobby(@StringRes dogHobby: Int, modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier.padding(
+ start = 16.dp,
+ top = 8.dp,
+ bottom = 16.dp,
+ end = 16.dp
+ )
+ ) {
+ Text(
+ text = stringResource(R.string.about),
+ style = MaterialTheme.typography.h3,
+ )
+ Text(
+ text = stringResource(dogHobby),
+ style = MaterialTheme.typography.body1,
)
}
}
@@ -131,3 +241,11 @@ fun WoofPreview() {
WoofApp()
}
}
+
+@Preview
+@Composable
+fun DarkThemePreview() {
+ WoofTheme(darkTheme = true) {
+ WoofApp()
+ }
+}
\ No newline at end of file
diff --git a/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/ui/theme/Color.kt b/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/ui/theme/Color.kt
index 2bb986719..7ae56f734 100644
--- a/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/ui/theme/Color.kt
+++ b/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/ui/theme/Color.kt
@@ -21,3 +21,16 @@ val Purple200 = Color(0xFFBB86FC)
val Purple500 = Color(0xFF6200EE)
val Purple700 = Color(0xFF3700B3)
val Teal200 = Color(0xFF03DAC5)
+
+//Light Theme
+val Grey50 = Color(0xFFF8F9FA)
+val Grey900 = Color(0xFF202124)
+val Grey700 = Color(0xFF5F6368)
+val Green50 = Color(0xFFE6F4EA)
+val Green100 = Color(0xFFCEEAD6)
+
+//Dark Theme
+val White = Color(0xFFFFFFFF)
+val Grey100 = Color(0xFFF1F3F4)
+val Cyan900 = Color(0xFF007B83)
+val Cyan700 = Color(0xFF129EAF)
\ No newline at end of file
diff --git a/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/ui/theme/Shape.kt b/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/ui/theme/Shape.kt
index a04343591..63168dd29 100644
--- a/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/ui/theme/Shape.kt
+++ b/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/ui/theme/Shape.kt
@@ -21,6 +21,6 @@ import androidx.compose.ui.unit.dp
val Shapes = Shapes(
small = RoundedCornerShape(4.dp),
- medium = RoundedCornerShape(4.dp),
+ medium = RoundedCornerShape(16.dp),
large = RoundedCornerShape(0.dp)
-)
+)
\ No newline at end of file
diff --git a/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/ui/theme/Theme.kt b/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/ui/theme/Theme.kt
index dd94c3134..30fcc55ba 100644
--- a/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/ui/theme/Theme.kt
+++ b/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/ui/theme/Theme.kt
@@ -22,24 +22,21 @@ import androidx.compose.material.lightColors
import androidx.compose.runtime.Composable
private val DarkColorPalette = darkColors(
- primary = Purple200,
- primaryVariant = Purple700,
- secondary = Teal200
+ background = Cyan900,
+ surface = Cyan700,
+ onSurface = White,
+ primary = Grey900,
+ onPrimary = White,
+ secondary = Grey100
)
private val LightColorPalette = lightColors(
- primary = Purple500,
- primaryVariant = Purple700,
- secondary = Teal200
-
- /* Other default colors to override
- background = Color.White,
- surface = Color.White,
- onPrimary = Color.White,
- onSecondary = Color.Black,
- onBackground = Color.Black,
- onSurface = Color.Black,
- */
+ background = Green100,
+ surface = Green50,
+ onSurface = Grey900,
+ primary = Grey50,
+ onPrimary = Grey900,
+ secondary = Grey700
)
@Composable
diff --git a/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/ui/theme/Type.kt b/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/ui/theme/Type.kt
index 4ad3e60c2..5eba39b36 100644
--- a/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/ui/theme/Type.kt
+++ b/Unit3/Pathway3/WoofCodelab/app/src/main/java/com/example/woofcodelab/ui/theme/Type.kt
@@ -17,27 +17,43 @@ package com.example.woofcodelab.ui.theme
import androidx.compose.material.Typography
import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
+import com.example.woofcodelab.R
// Set of Material typography styles to start with
+
+val AbrilFatface = FontFamily(
+ Font(R.font.abril_fatface_regular)
+)
+
+val Montserrat = FontFamily(
+ Font(R.font.montserrat_regular),
+ Font(R.font.montserrat_bold, FontWeight.Bold)
+)
+
val Typography = Typography(
- body1 = TextStyle(
- fontFamily = FontFamily.Default,
+ h1 = TextStyle(
+ fontFamily = AbrilFatface,
fontWeight = FontWeight.Normal,
- fontSize = 16.sp
- )
- /* Other default text styles to override
- button = TextStyle(
- fontFamily = FontFamily.Default,
- fontWeight = FontWeight.W500,
+ fontSize = 30.sp
+ ),
+ h2 = TextStyle(
+ fontFamily = Montserrat,
+ fontWeight = FontWeight.Bold,
+ fontSize = 20.sp
+ ),
+ h3 = TextStyle(
+ fontFamily = Montserrat,
+ fontWeight = FontWeight.Bold,
fontSize = 14.sp
),
- caption = TextStyle(
- fontFamily = FontFamily.Default,
+ body1 = TextStyle(
+ fontFamily = Montserrat,
fontWeight = FontWeight.Normal,
- fontSize = 12.sp
+ fontSize = 14.sp
)
- */
)
+
diff --git a/Unit3/Pathway3/WoofCodelab/app/src/main/res/font/abril_fatface_regular.ttf b/Unit3/Pathway3/WoofCodelab/app/src/main/res/font/abril_fatface_regular.ttf
new file mode 100644
index 000000000..a29171148
Binary files /dev/null and b/Unit3/Pathway3/WoofCodelab/app/src/main/res/font/abril_fatface_regular.ttf differ
diff --git a/Unit3/Pathway3/WoofCodelab/app/src/main/res/font/montserrat_bold.ttf b/Unit3/Pathway3/WoofCodelab/app/src/main/res/font/montserrat_bold.ttf
new file mode 100644
index 000000000..efddc8341
Binary files /dev/null and b/Unit3/Pathway3/WoofCodelab/app/src/main/res/font/montserrat_bold.ttf differ
diff --git a/Unit3/Pathway3/WoofCodelab/app/src/main/res/font/montserrat_regular.ttf b/Unit3/Pathway3/WoofCodelab/app/src/main/res/font/montserrat_regular.ttf
new file mode 100644
index 000000000..aa9033a80
Binary files /dev/null and b/Unit3/Pathway3/WoofCodelab/app/src/main/res/font/montserrat_regular.ttf differ
diff --git a/Unit3/Pathway3/WoofCodelab/app/src/main/res/values-night/themes.xml b/Unit3/Pathway3/WoofCodelab/app/src/main/res/values-night/themes.xml
new file mode 100644
index 000000000..09d184553
--- /dev/null
+++ b/Unit3/Pathway3/WoofCodelab/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Unit3/Pathway3/WoofCodelab/app/src/main/res/values/colors.xml b/Unit3/Pathway3/WoofCodelab/app/src/main/res/values/colors.xml
index de3366f83..0f5357354 100644
--- a/Unit3/Pathway3/WoofCodelab/app/src/main/res/values/colors.xml
+++ b/Unit3/Pathway3/WoofCodelab/app/src/main/res/values/colors.xml
@@ -22,4 +22,7 @@
#FF018786
#FF000000
#FFFFFFFF
+
+ #FFF8F9FA
+ #FF202124
\ No newline at end of file
diff --git a/Unit3/Pathway3/WoofCodelab/app/src/main/res/values/themes.xml b/Unit3/Pathway3/WoofCodelab/app/src/main/res/values/themes.xml
index 4ef11abc2..6c84eb06e 100644
--- a/Unit3/Pathway3/WoofCodelab/app/src/main/res/values/themes.xml
+++ b/Unit3/Pathway3/WoofCodelab/app/src/main/res/values/themes.xml
@@ -17,6 +17,7 @@
\ No newline at end of file
diff --git a/Unit4/Pathway1/basic-android-kotlin-compose-training-dessert-clicker/app/build.gradle b/Unit4/Pathway1/basic-android-kotlin-compose-training-dessert-clicker/app/build.gradle
index 8feecee4a..90ed6bd00 100644
--- a/Unit4/Pathway1/basic-android-kotlin-compose-training-dessert-clicker/app/build.gradle
+++ b/Unit4/Pathway1/basic-android-kotlin-compose-training-dessert-clicker/app/build.gradle
@@ -47,6 +47,8 @@ android {
dependencies {
+ implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"
+
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
implementation 'androidx.activity:activity-compose:1.5.1'
diff --git a/Unit4/Pathway1/basic-android-kotlin-compose-training-dessert-clicker/app/src/main/java/com/example/dessertclicker/MainActivity.kt b/Unit4/Pathway1/basic-android-kotlin-compose-training-dessert-clicker/app/src/main/java/com/example/dessertclicker/MainActivity.kt
index e267a3264..7b741c5e7 100644
--- a/Unit4/Pathway1/basic-android-kotlin-compose-training-dessert-clicker/app/src/main/java/com/example/dessertclicker/MainActivity.kt
+++ b/Unit4/Pathway1/basic-android-kotlin-compose-training-dessert-clicker/app/src/main/java/com/example/dessertclicker/MainActivity.kt
@@ -19,6 +19,7 @@ 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
@@ -26,26 +27,15 @@ import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.material.Scaffold
-import androidx.compose.material.Text
-import androidx.compose.material.Icon
-import androidx.compose.material.IconButton
-import androidx.compose.material.MaterialTheme
+import androidx.compose.foundation.layout.*
+import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Share
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.setValue
import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
+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.graphics.Color
@@ -58,19 +48,51 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat.startActivity
import com.example.dessertclicker.data.Datasource.dessertList
-import com.example.dessertclicker.ui.theme.DessertClickerTheme
import com.example.dessertclicker.model.Dessert
+import com.example.dessertclicker.ui.theme.DessertClickerTheme
-class MainActivity : ComponentActivity() {
+private const val TAG = "MainActivity"
+class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ Log.d(TAG, "onCreate Called")
setContent {
DessertClickerTheme {
DessertClickerApp(desserts = 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")
+ }
}
/**
@@ -127,15 +149,15 @@ private fun DessertClickerApp(
desserts: List
) {
- var revenue by remember { mutableStateOf(0) }
- var dessertsSold by remember { mutableStateOf(0) }
+ var revenue by rememberSaveable { mutableStateOf(0) }
+ var dessertsSold by rememberSaveable { mutableStateOf(0) }
- val currentDessertIndex by remember { mutableStateOf(0) }
+ val currentDessertIndex by rememberSaveable { mutableStateOf(0) }
- var currentDessertPrice by remember {
+ var currentDessertPrice by rememberSaveable {
mutableStateOf(desserts[currentDessertIndex].price)
}
- var currentDessertImageId by remember {
+ var currentDessertImageId by rememberSaveable {
mutableStateOf(desserts[currentDessertIndex].imageId)
}
diff --git a/Unit4/Pathway1/basic-android-kotlin-compose-training-dessert-clicker/build.gradle b/Unit4/Pathway1/basic-android-kotlin-compose-training-dessert-clicker/build.gradle
index 1deeec4e4..17e03dbcb 100644
--- a/Unit4/Pathway1/basic-android-kotlin-compose-training-dessert-clicker/build.gradle
+++ b/Unit4/Pathway1/basic-android-kotlin-compose-training-dessert-clicker/build.gradle
@@ -2,6 +2,7 @@ buildscript {
ext {
compose_version = '1.2.0'
compose_compiler_version = '1.2.0'
+ lifecycle_version = '2.5.1'
}
}// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
diff --git a/Unit4/Pathway1/basic-android-kotlin-compose-training-unscramble/app/build.gradle b/Unit4/Pathway1/basic-android-kotlin-compose-training-unscramble/app/build.gradle
index 5079735d4..b11f26b78 100644
--- a/Unit4/Pathway1/basic-android-kotlin-compose-training-unscramble/app/build.gradle
+++ b/Unit4/Pathway1/basic-android-kotlin-compose-training-unscramble/app/build.gradle
@@ -63,6 +63,8 @@ android {
dependencies {
+ implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1"
+
implementation 'androidx.activity:activity-compose:1.5.1'
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
diff --git a/Unit4/Pathway1/basic-android-kotlin-compose-training-unscramble/app/src/main/java/com/example/android/unscramble/ui/GameScreen.kt b/Unit4/Pathway1/basic-android-kotlin-compose-training-unscramble/app/src/main/java/com/example/android/unscramble/ui/GameScreen.kt
index 0e14ff1ee..81b387907 100644
--- a/Unit4/Pathway1/basic-android-kotlin-compose-training-unscramble/app/src/main/java/com/example/android/unscramble/ui/GameScreen.kt
+++ b/Unit4/Pathway1/basic-android-kotlin-compose-training-unscramble/app/src/main/java/com/example/android/unscramble/ui/GameScreen.kt
@@ -34,6 +34,8 @@ import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@@ -42,12 +44,17 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.android.unscramble.R
import com.example.android.unscramble.ui.theme.UnscrambleTheme
@Composable
-fun GameScreen(modifier: Modifier = Modifier) {
+fun GameScreen(
+ gameViewModel: GameViewModel = viewModel(),
+ modifier: Modifier = Modifier
+) {
+ val gameUiState by gameViewModel.uiState.collectAsState()
Column(
modifier = modifier
.verticalScroll(rememberScrollState())
@@ -55,8 +62,18 @@ fun GameScreen(modifier: Modifier = Modifier) {
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
- GameStatus()
- GameLayout()
+ GameStatus(
+ wordCount = gameUiState.currentWordCount,
+ score = gameUiState.score
+ )
+ GameLayout(
+ currentScrambledWord = gameUiState.currentScrambledWord,
+ isGuessWrong = gameUiState.isGuessedWordWrong,
+ userGuess = gameViewModel.userGuess,
+ onUserGuessChanged = { gameViewModel.updateUserGuess(it) },
+ onKeyboardDone = { gameViewModel.checkUserGuess() },
+ )
+
Row(
modifier = modifier
.fillMaxWidth()
@@ -64,7 +81,7 @@ fun GameScreen(modifier: Modifier = Modifier) {
horizontalArrangement = Arrangement.SpaceAround
) {
OutlinedButton(
- onClick = { },
+ onClick = { gameViewModel.skipWord() },
modifier = Modifier
.weight(1f)
.padding(end = 8.dp)
@@ -77,16 +94,26 @@ fun GameScreen(modifier: Modifier = Modifier) {
.fillMaxWidth()
.weight(1f)
.padding(start = 8.dp),
- onClick = { }
+ onClick = { gameViewModel.checkUserGuess() }
) {
Text(stringResource(R.string.submit))
}
}
+ if (gameUiState.isGameOver) {
+ FinalScoreDialog(
+ score = gameUiState.score,
+ onPlayAgain = { gameViewModel.resetGame() }
+ )
+ }
}
}
@Composable
-fun GameStatus(modifier: Modifier = Modifier) {
+fun GameStatus(
+ wordCount: Int,
+ score: Int,
+ modifier: Modifier = Modifier
+) {
Row(
modifier = modifier
.fillMaxWidth()
@@ -94,27 +121,34 @@ fun GameStatus(modifier: Modifier = Modifier) {
.size(48.dp),
) {
Text(
- text = stringResource(R.string.word_count, 0),
+ text = stringResource(R.string.word_count, wordCount),
fontSize = 18.sp,
)
Text(
modifier = Modifier
.fillMaxWidth()
.wrapContentWidth(Alignment.End),
- text = stringResource(R.string.score, 0),
+ text = stringResource(R.string.score, score),
fontSize = 18.sp,
)
}
}
+
@Composable
-fun GameLayout(modifier: Modifier = Modifier) {
+fun GameLayout(
+ currentScrambledWord: String,
+ isGuessWrong: Boolean,
+ userGuess: String,
+ onUserGuessChanged: (String) -> Unit,
+ onKeyboardDone: () -> Unit,
+ modifier: Modifier = Modifier) {
Column(
verticalArrangement = Arrangement.spacedBy(24.dp),
) {
Text(
- text = "scrambleun",
+ text = currentScrambledWord,
fontSize = 45.sp,
modifier = modifier.align(Alignment.CenterHorizontally)
)
@@ -124,17 +158,23 @@ fun GameLayout(modifier: Modifier = Modifier) {
modifier = Modifier.align(Alignment.CenterHorizontally)
)
OutlinedTextField(
- value = "",
+ value = userGuess,
singleLine = true,
modifier = Modifier.fillMaxWidth(),
- onValueChange = { },
- label = { Text(stringResource(R.string.enter_your_word)) },
- isError = false,
+ onValueChange = onUserGuessChanged,
+ label = {
+ if (isGuessWrong) {
+ Text(stringResource(R.string.wrong_guess))
+ } else {
+ Text(stringResource(R.string.enter_your_word))
+ }
+ },
+ isError = isGuessWrong,
keyboardOptions = KeyboardOptions.Default.copy(
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(
- onDone = { }
+ onDone = { onKeyboardDone() }
),
)
}
@@ -145,6 +185,7 @@ fun GameLayout(modifier: Modifier = Modifier) {
*/
@Composable
private fun FinalScoreDialog(
+ score: Int,
onPlayAgain: () -> Unit,
modifier: Modifier = Modifier
) {
@@ -157,7 +198,7 @@ private fun FinalScoreDialog(
// onCloseRequest.
},
title = { Text(stringResource(R.string.congratulations)) },
- text = { Text(stringResource(R.string.you_scored, 0)) },
+ text = { Text(stringResource(R.string.you_scored, score)) },
modifier = modifier,
dismissButton = {
TextButton(
diff --git a/Unit4/Pathway1/basic-android-kotlin-compose-training-unscramble/app/src/main/java/com/example/android/unscramble/ui/GameUiState.kt b/Unit4/Pathway1/basic-android-kotlin-compose-training-unscramble/app/src/main/java/com/example/android/unscramble/ui/GameUiState.kt
new file mode 100644
index 000000000..998627ff8
--- /dev/null
+++ b/Unit4/Pathway1/basic-android-kotlin-compose-training-unscramble/app/src/main/java/com/example/android/unscramble/ui/GameUiState.kt
@@ -0,0 +1,9 @@
+package com.example.android.unscramble.ui
+
+data class GameUiState(
+ val currentScrambledWord: String = "",
+ val currentWordCount: Int = 1,
+ val score: Int = 0,
+ val isGuessedWordWrong: Boolean = false,
+ val isGameOver: Boolean = false
+)
\ No newline at end of file
diff --git a/Unit4/Pathway1/basic-android-kotlin-compose-training-unscramble/app/src/main/java/com/example/android/unscramble/ui/GameViewModel.kt b/Unit4/Pathway1/basic-android-kotlin-compose-training-unscramble/app/src/main/java/com/example/android/unscramble/ui/GameViewModel.kt
new file mode 100644
index 000000000..ae9c3b044
--- /dev/null
+++ b/Unit4/Pathway1/basic-android-kotlin-compose-training-unscramble/app/src/main/java/com/example/android/unscramble/ui/GameViewModel.kt
@@ -0,0 +1,110 @@
+package com.example.android.unscramble.ui
+
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.ViewModel
+import com.example.android.unscramble.data.MAX_NO_OF_WORDS
+import com.example.android.unscramble.data.SCORE_INCREASE
+import com.example.android.unscramble.data.allWords
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+
+class GameViewModel : ViewModel() {
+
+ // Game UI state
+ private val _uiState = MutableStateFlow(GameUiState())
+ // Backing property to avoid state updates from other classes
+ val uiState: StateFlow = _uiState.asStateFlow()
+
+ private lateinit var currentWord: String
+
+ // Set of words used in the game
+ private var usedWords: MutableSet = mutableSetOf()
+
+ var userGuess by mutableStateOf("")
+
+ init {
+ resetGame()
+ }
+
+ private fun pickRandomWordAndShuffle(): String {
+ // Continue picking up a new random word until you get one that hasn't been used before
+ currentWord = allWords.random()
+ if (usedWords.contains(currentWord)) {
+ return pickRandomWordAndShuffle()
+ } else {
+ usedWords.add(currentWord)
+ return shuffleCurrentWord(currentWord)
+ }
+ }
+
+ private fun shuffleCurrentWord(word: String): String {
+ val tempWord = word.toCharArray()
+ // Scramble the word
+ tempWord.shuffle()
+ while (String(tempWord).equals(word)) {
+ tempWord.shuffle()
+ }
+ return String(tempWord)
+ }
+
+ fun resetGame() {
+ usedWords.clear()
+ _uiState.value = GameUiState(currentScrambledWord = pickRandomWordAndShuffle())
+ }
+
+
+ fun updateUserGuess(guessedWord: String){
+ userGuess = guessedWord
+ }
+
+
+ fun checkUserGuess() {
+ if (userGuess.equals(currentWord, ignoreCase = true)) {
+ // User's guess is correct, increase the score
+ // and call updateGameState() to prepare the game for next round
+ val updatedScore = _uiState.value.score.plus(SCORE_INCREASE)
+ updateGameState(updatedScore)
+ updateUserGuess("")
+ } else {
+ // User's guess is wrong, show an error
+ _uiState.update { currentState ->
+ currentState.copy(isGuessedWordWrong = true)
+ }
+ }
+ }
+
+ private fun updateGameState(updatedScore: Int) {
+
+ if (usedWords.size == MAX_NO_OF_WORDS){
+ //Last round in the game, update isGameOver to true, don't pick a new word
+ _uiState.update { currentState ->
+ currentState.copy(
+ isGuessedWordWrong = false,
+ score = updatedScore,
+ currentWordCount = currentState.currentWordCount,
+ isGameOver = true
+ )
+ }
+ } else {
+ // Normal round in the game
+ _uiState.update { currentState ->
+ currentState.copy(
+ isGuessedWordWrong = false,
+ currentWordCount = currentState.currentWordCount.inc(),
+ currentScrambledWord = pickRandomWordAndShuffle(),
+ score = updatedScore
+ )
+ }
+ }
+ }
+
+ fun skipWord() {
+ updateGameState(_uiState.value.score)
+ // Reset user guess
+ updateUserGuess("")
+ }
+}
diff --git a/Unit4/Pathway1/basic-android-kotlin-compose-training-unscramble/app/src/test/java/com/example/android/unscramble/ui/test/GameViewModelTest.kt b/Unit4/Pathway1/basic-android-kotlin-compose-training-unscramble/app/src/test/java/com/example/android/unscramble/ui/test/GameViewModelTest.kt
new file mode 100644
index 000000000..417846157
--- /dev/null
+++ b/Unit4/Pathway1/basic-android-kotlin-compose-training-unscramble/app/src/test/java/com/example/android/unscramble/ui/test/GameViewModelTest.kt
@@ -0,0 +1,111 @@
+package com.example.android.unscramble.ui.test
+
+import com.example.android.unscramble.data.MAX_NO_OF_WORDS
+import com.example.android.unscramble.data.SCORE_INCREASE
+import com.example.android.unscramble.data.getUnscrambledWord
+import com.example.android.unscramble.ui.GameViewModel
+import junit.framework.TestCase.*
+import org.junit.Assert.assertNotEquals
+import org.junit.Test
+
+class GameViewModelTest {
+
+ private val viewModel = GameViewModel()
+
+
+ /*
+ 참고: thingUnderTest_TriggerOfTest_ResultOfTest 형식을 사용하여 테스트 함수 이름을 지정합니다.
+ thingUnderTest = gameViewModel
+ TriggerOfTest = CorrectWordGuessed
+ ResultOfTest = ScoreUpdatedAndErrorFlagUnset
+ */
+ @Test
+ fun gameViewModel_CorrectWordGuessed_ScoreUpdatedAndErrorFlagUnset(){
+ var currentGameUiState = viewModel.uiState.value
+ val correctPlayerWord = getUnscrambledWord(currentGameUiState.currentScrambledWord)
+
+ viewModel.updateUserGuess(correctPlayerWord)
+ viewModel.checkUserGuess()
+
+ currentGameUiState = viewModel.uiState.value
+
+ assertFalse(currentGameUiState.isGuessedWordWrong)
+ assertEquals(SCORE_AFTER_FIRST_CORRECT_ANSWER, currentGameUiState.score)
+ }
+
+ companion object {
+ private const val SCORE_AFTER_FIRST_CORRECT_ANSWER = SCORE_INCREASE
+
+ }
+
+
+ @Test
+ fun gameViewModel_IncorrectGuess_ErrorFlagSet() {
+ // Given an incorrect word as input
+ val incorrectPlayerWord = "and"
+
+ viewModel.updateUserGuess(incorrectPlayerWord)
+ viewModel.checkUserGuess()
+
+ val currentGameUiState = viewModel.uiState.value
+ // Assert that score is unchanged
+ assertEquals(0, currentGameUiState.score)
+ // Assert that checkUserGuess() method updates isGuessedWordWrong correctly
+ assertTrue(currentGameUiState.isGuessedWordWrong)
+ }
+
+
+ @Test
+ fun gameViewModel_Initialization_FirstWordLoaded() {
+ val gameUiState = viewModel.uiState.value
+ val unScrambledWord = getUnscrambledWord(gameUiState.currentScrambledWord)
+
+ // Assert that current word is scrambled.
+ assertNotEquals(unScrambledWord, gameUiState.currentScrambledWord)
+ // Assert that current word count is set to 1.
+ assertTrue(gameUiState.currentWordCount == 1)
+ // Assert that initially the score is 0.
+ assertTrue(gameUiState.score == 0)
+ // Assert that the wrong word guessed is false.
+ assertFalse(gameUiState.isGuessedWordWrong)
+ // Assert that game is not over.
+ assertFalse(gameUiState.isGameOver)
+ }
+
+ @Test
+ fun gameViewModel_AllWordsGuessed_UiStateUpdatedCorrectly() {
+ var expectedScore = 0
+ var currentGameUiState = viewModel.uiState.value
+ var correctPlayerWord = getUnscrambledWord(currentGameUiState.currentScrambledWord)
+ repeat(MAX_NO_OF_WORDS){
+ expectedScore += SCORE_INCREASE
+ viewModel.updateUserGuess(correctPlayerWord)
+ viewModel.checkUserGuess()
+ currentGameUiState = viewModel.uiState.value
+ correctPlayerWord = getUnscrambledWord(currentGameUiState.currentScrambledWord)
+ // Assert that after each correct answer, score is updated correctly.
+ assertEquals(expectedScore, currentGameUiState.score)
+ }
+ // Assert that after all questions are answered, the current word count is up-to-date.
+ assertEquals(MAX_NO_OF_WORDS, currentGameUiState.currentWordCount)
+ // Assert that after 10 questions are answered, the game is over.
+ assertTrue(currentGameUiState.isGameOver)
+ }
+
+ @Test
+ fun gameViewModel_WordSkipped_ScoreUnchangedAndWordCountIncreased() {
+ var currentGameUiState = viewModel.uiState.value
+ val correctPlayerWord = getUnscrambledWord(currentGameUiState.currentScrambledWord)
+ viewModel.updateUserGuess(correctPlayerWord)
+ viewModel.checkUserGuess()
+
+ currentGameUiState = viewModel.uiState.value
+ val lastWordCount = currentGameUiState.currentWordCount
+ viewModel.skipWord()
+ currentGameUiState = viewModel.uiState.value
+ // Assert that score remains unchanged after word is skipped.
+ assertEquals(SCORE_AFTER_FIRST_CORRECT_ANSWER, currentGameUiState.score)
+ // Assert that word count is increased by 1 after word is skipped.
+ assertEquals(lastWordCount + 1, currentGameUiState.currentWordCount)
+ }
+}
\ No newline at end of file
diff --git a/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/build.gradle b/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/build.gradle
index 170740623..0b8d4c0ef 100644
--- a/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/build.gradle
+++ b/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/build.gradle
@@ -59,9 +59,11 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.1'
implementation "androidx.navigation:navigation-compose:2.5.0"
+ implementation 'androidx.navigation:navigation-testing:2.5.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
+ androidTestImplementation 'androidx.test.espresso:espresso-intents:3.5.0'
}
diff --git a/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/androidTest/java/com/example/cupcake/test/ComposeRuleExtensions.kt b/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/androidTest/java/com/example/cupcake/test/ComposeRuleExtensions.kt
new file mode 100644
index 000000000..0c8441fda
--- /dev/null
+++ b/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/androidTest/java/com/example/cupcake/test/ComposeRuleExtensions.kt
@@ -0,0 +1,12 @@
+package com.example.cupcake.test
+
+import androidx.activity.ComponentActivity
+import androidx.annotation.StringRes
+import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.junit4.AndroidComposeTestRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+
+fun AndroidComposeTestRule, A>.onNodeWithStringId(
+ @StringRes id: Int
+): SemanticsNodeInteraction = onNodeWithText(activity.getString(id))
diff --git a/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/androidTest/java/com/example/cupcake/test/CupcakeOrderScreenTest.kt b/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/androidTest/java/com/example/cupcake/test/CupcakeOrderScreenTest.kt
new file mode 100644
index 000000000..296b0370d
--- /dev/null
+++ b/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/androidTest/java/com/example/cupcake/test/CupcakeOrderScreenTest.kt
@@ -0,0 +1,106 @@
+package com.example.cupcake.test
+
+import androidx.activity.ComponentActivity
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import com.example.cupcake.R
+import com.example.cupcake.data.DataSource
+import com.example.cupcake.data.OrderUiState
+import com.example.cupcake.ui.OrderSummaryScreen
+import com.example.cupcake.ui.SelectOptionScreen
+import com.example.cupcake.ui.StartOrderScreen
+import org.junit.Rule
+import org.junit.Test
+
+class CupcakeOrderScreenTest {
+
+ @get:Rule
+ val composeTestRule = createAndroidComposeRule()
+
+ private val fakeOrderUiState = OrderUiState(
+ quantity = 6,
+ flavor = "Vanilla",
+ date = "Wed Jul 21",
+ price = "$100",
+ pickupOptions = listOf()
+ )
+
+ @Test
+ fun startOrderScreen_verifyContent() {
+
+ composeTestRule.setContent {
+ StartOrderScreen(
+ quantityOptions = DataSource.quantityOptions,
+ onNextButtonClicked = {}
+ )
+ }
+
+ DataSource.quantityOptions.forEach {
+ composeTestRule.onNodeWithStringId(it.first).assertIsDisplayed()
+ }
+ }
+
+
+ @Test
+ fun selectOptionScreen_verifyContent() {
+ val flavours = listOf("Vanilla", "Chocolate", "Hazelnut", "Cookie", "Mango")
+ val subTotal = "$100"
+
+ composeTestRule.setContent {
+ SelectOptionScreen(subtotal = subTotal, options = flavours)
+ }
+
+ flavours.forEach { flavour ->
+ composeTestRule.onNodeWithText(flavour).assertIsDisplayed()
+ }
+
+ composeTestRule.onNodeWithText(
+ composeTestRule.activity.getString(
+ R.string.subtotal_price,
+ subTotal
+ )
+ ).assertIsDisplayed()
+
+ composeTestRule.onNodeWithStringId(R.string.next).assertIsNotEnabled()
+ }
+
+
+ @Test
+ fun selectOptionScreen_optionSelected_NextButtonEnabled() {
+ val flavours = listOf("Vanilla", "Chocolate", "Hazelnut", "Cookie", "Mango")
+ val subTotal = "$100"
+
+ composeTestRule.setContent {
+ SelectOptionScreen(subtotal = subTotal, options = flavours)
+ }
+
+ composeTestRule.onNodeWithText("Vanilla").performClick()
+ composeTestRule.onNodeWithStringId(R.string.next).assertIsEnabled()
+ }
+
+
+ @Test
+ fun summaryScreen_verifyContentDisplay() {
+ // When Summary Screen is loaded
+ composeTestRule.setContent {
+ OrderSummaryScreen(
+ orderUiState = fakeOrderUiState,
+ onCancelButtonClicked = {},
+ onSendButtonClicked = { _, _ -> },
+ )
+ }
+
+ composeTestRule.onNodeWithText(fakeOrderUiState.flavor).assertIsDisplayed()
+ composeTestRule.onNodeWithText(fakeOrderUiState.date).assertIsDisplayed()
+ composeTestRule.onNodeWithText(
+ composeTestRule.activity.getString(
+ R.string.subtotal_price,
+ fakeOrderUiState.price
+ )
+ )
+ }
+}
\ No newline at end of file
diff --git a/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/androidTest/java/com/example/cupcake/test/CupcakeScreenNavigationTest.kt b/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/androidTest/java/com/example/cupcake/test/CupcakeScreenNavigationTest.kt
new file mode 100644
index 000000000..a9542ddc1
--- /dev/null
+++ b/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/androidTest/java/com/example/cupcake/test/CupcakeScreenNavigationTest.kt
@@ -0,0 +1,144 @@
+package com.example.cupcake.test
+
+import android.icu.util.Calendar
+import androidx.activity.ComponentActivity
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.test.onNodeWithContentDescription
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.navigation.compose.ComposeNavigator
+import androidx.navigation.testing.TestNavHostController
+import com.example.cupcake.CupcakeApp
+import com.example.cupcake.CupcakeScreen
+import com.example.cupcake.R
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import java.text.SimpleDateFormat
+import java.util.Locale
+
+class CupcakeScreenNavigationTest {
+
+ @get:Rule
+ val composeTestRule = createAndroidComposeRule()
+
+ private lateinit var navController: TestNavHostController
+
+ @Before
+ fun setupCupcakeNavHost() {
+ composeTestRule.setContent {
+ navController = TestNavHostController(LocalContext.current).apply {
+ navigatorProvider.addNavigator(ComposeNavigator())
+ }
+ CupcakeApp(navController = navController)
+ }
+ }
+
+ @Test
+ fun cupcakeNavHost_verifyStartDestination() {
+ navController.assertCurrentRouteName(CupcakeScreen.Start.name)
+ }
+
+ @Test
+ fun cupcakeNavHost_verifyBackNavigationNotShownOnStartOrderScreen() {
+ val backText = composeTestRule.activity.getString(R.string.back_button)
+ composeTestRule.onNodeWithContentDescription(backText).assertDoesNotExist()
+ }
+
+ @Test
+ fun cupcakeNavHost_clickOneCupcake_navigatesToSelectFlavorScreen() {
+ composeTestRule.onNodeWithStringId(R.string.one_cupcake)
+ .performClick()
+ navController.assertCurrentRouteName(CupcakeScreen.Flavor.name)
+ }
+
+ @Test
+ fun cupcakeNavHost_clickNextOnFlavorScreen_navigatesToPickupScreen() {
+ navigateToFlavorScreen()
+ composeTestRule.onNodeWithStringId(R.string.next)
+ .performClick()
+ navController.assertCurrentRouteName(CupcakeScreen.Pickup.name)
+ }
+
+ @Test
+ fun cupcakeNavHost_clickBackOnFlavorScreen_navigatesToStartOrderScreen() {
+ navigateToFlavorScreen()
+ performNavigateUp()
+ navController.assertCurrentRouteName(CupcakeScreen.Start.name)
+ }
+
+ @Test
+ fun cupcakeNavHost_clickCancelOnFlavorScreen_navigatesToStartOrderScreen() {
+ navigateToFlavorScreen()
+ composeTestRule.onNodeWithStringId(R.string.cancel)
+ .performClick()
+ navController.assertCurrentRouteName(CupcakeScreen.Start.name)
+ }
+
+ @Test
+ fun cupcakeNavHost_clickNextOnPickupScreen_navigatesToSummaryScreen() {
+ navigateToPickupScreen()
+ composeTestRule.onNodeWithText(getFormattedDate())
+ .performClick()
+ composeTestRule.onNodeWithStringId(R.string.next)
+ .performClick()
+ navController.assertCurrentRouteName(CupcakeScreen.Summary.name)
+ }
+
+ @Test
+ fun cupcakeNavHost_clickBackOnPickupScreen_navigatesToFlavorScreen() {
+ navigateToPickupScreen()
+ performNavigateUp()
+ navController.assertCurrentRouteName(CupcakeScreen.Flavor.name)
+ }
+
+ @Test
+ fun cupcakeNavHost_clickCancelOnPickupScreen_navigatesToStartOrderScreen() {
+ navigateToPickupScreen()
+ composeTestRule.onNodeWithStringId(R.string.cancel)
+ .performClick()
+ navController.assertCurrentRouteName(CupcakeScreen.Start.name)
+ }
+
+ @Test
+ fun cupcakeNavHost_clickCancelOnSummaryScreen_navigatesToStartOrderScreen() {
+ navigateToSummaryScreen()
+ composeTestRule.onNodeWithStringId(R.string.cancel)
+ .performClick()
+ navController.assertCurrentRouteName(CupcakeScreen.Start.name)
+ }
+
+ private fun navigateToFlavorScreen() {
+ composeTestRule.onNodeWithStringId(R.string.one_cupcake)
+ .performClick()
+ composeTestRule.onNodeWithStringId(R.string.chocolate)
+ .performClick()
+ }
+
+ private fun navigateToPickupScreen() {
+ navigateToFlavorScreen()
+ composeTestRule.onNodeWithStringId(R.string.next)
+ .performClick()
+ }
+
+ private fun navigateToSummaryScreen() {
+ navigateToPickupScreen()
+ composeTestRule.onNodeWithText(getFormattedDate())
+ .performClick()
+ composeTestRule.onNodeWithStringId(R.string.next)
+ .performClick()
+ }
+
+ private fun performNavigateUp() {
+ val backText = composeTestRule.activity.getString(R.string.back_button)
+ composeTestRule.onNodeWithContentDescription(backText).performClick()
+ }
+
+ private fun getFormattedDate(): String {
+ val calendar = Calendar.getInstance()
+ calendar.add(java.util.Calendar.DATE, 1)
+ val formatter = SimpleDateFormat("E MMM d", Locale.getDefault())
+ return formatter.format(calendar.time)
+ }
+}
\ No newline at end of file
diff --git a/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/androidTest/java/com/example/cupcake/test/ScreenAssertions.kt b/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/androidTest/java/com/example/cupcake/test/ScreenAssertions.kt
new file mode 100644
index 000000000..701b5bd2e
--- /dev/null
+++ b/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/androidTest/java/com/example/cupcake/test/ScreenAssertions.kt
@@ -0,0 +1,8 @@
+package com.example.cupcake.test
+
+import androidx.navigation.NavController
+import org.junit.Assert
+
+fun NavController.assertCurrentRouteName(expectedRouteName: String) {
+ Assert.assertEquals(expectedRouteName, currentBackStackEntry?.destination?.route)
+}
\ No newline at end of file
diff --git a/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/main/java/com/example/cupcake/CupcakeScreen.kt b/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/main/java/com/example/cupcake/CupcakeScreen.kt
index 89dd4e885..110603de0 100644
--- a/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/main/java/com/example/cupcake/CupcakeScreen.kt
+++ b/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/main/java/com/example/cupcake/CupcakeScreen.kt
@@ -15,6 +15,10 @@
*/
package com.example.cupcake
+import android.content.Context
+import android.content.Intent
+import androidx.annotation.StringRes
+import androidx.compose.foundation.layout.padding
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Scaffold
@@ -26,21 +30,38 @@ 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.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
-import com.example.cupcake.ui.OrderViewModel
+import androidx.navigation.NavController
+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.flavors
+import com.example.cupcake.data.DataSource.quantityOptions
+import com.example.cupcake.ui.*
+
+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(id = R.string.app_name)) },
+ title = { Text(stringResource(id = currentScreen.title)) },
modifier = modifier,
navigationIcon = {
if (canNavigateBack) {
@@ -56,24 +77,125 @@ fun CupcakeAppBar(
}
@Composable
-fun CupcakeApp(modifier: Modifier = Modifier, viewModel: OrderViewModel = viewModel()){
- // TODO: Create NavController
-
- // TODO: Get current back stack entry
+fun CupcakeApp(
+ modifier: Modifier = Modifier,
+ viewModel: OrderViewModel = viewModel(),
+ navController: NavHostController = rememberNavController()
+){
+// val navController = rememberNavController()
- // TODO: Get the name of the current screen
+ val backStackEntry by navController.currentBackStackEntryAsState()
+ val currentScreen = CupcakeScreen.valueOf(
+ backStackEntry?.destination?.route ?: CupcakeScreen.Start.name
+ )
Scaffold(
topBar = {
CupcakeAppBar(
- canNavigateBack = false,
- navigateUp = { /* TODO: implement back navigation */ }
+ 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.padding(innerPadding)
+ ){
+ composable(route = CupcakeScreen.Start.name){
+ StartOrderScreen(
+ quantityOptions = quantityOptions,
+ onNextButtonClicked = {
+ viewModel.setQuantity(it)
+ navController.navigate(CupcakeScreen.Flavor.name)
+ }
+ )
+ }
+ composable(route = CupcakeScreen.Flavor.name){
+ val context = LocalContext.current
+ SelectOptionScreen(
+ subtotal = uiState.price,
+ onNextButtonClicked = {
+ navController.navigate(CupcakeScreen.Pickup.name)
+ },
+ onCancelButtonClicked = {
+ cancelOrderAndNavigateToStart(viewModel, navController)
+ },
+ options = flavors.map { id -> stringResource(id = id)},
+ onSelectionChanged = { viewModel.setFlavor(it) }
+ )
+ }
+ 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) }
+ )
+ }
+ composable(route = CupcakeScreen.Summary.name){
+ val context = LocalContext.current
+ OrderSummaryScreen(
+ orderUiState = uiState,
+ onCancelButtonClicked = {
+ cancelOrderAndNavigateToStart(viewModel, navController)
+ },
+ onSendButtonClicked = { subject: String, summary: String ->
+ shareOrder(context = context, subject = subject, summary = summary)
+ }
+ )
+ }
+
+ }
+ }
+}
- // TODO: add NavHost
+
+private fun cancelOrderAndNavigateToStart(
+ viewModel: OrderViewModel,
+ navController: NavController
+) {
+ viewModel.resetOrder()
+ navController.popBackStack(CupcakeScreen.Start.name, inclusive = false)
+}
+
+
+private fun shareOrder(context: Context, subject: String, summary: String) {
+ 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)
+ )
+ )
+
}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/main/java/com/example/cupcake/ui/SelectOptionScreen.kt b/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/main/java/com/example/cupcake/ui/SelectOptionScreen.kt
index 16e7eea89..6ee786408 100644
--- a/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/main/java/com/example/cupcake/ui/SelectOptionScreen.kt
+++ b/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/main/java/com/example/cupcake/ui/SelectOptionScreen.kt
@@ -50,8 +50,8 @@ fun SelectOptionScreen(
subtotal: String,
options: List,
onSelectionChanged: (String) -> Unit = {},
- // TODO: add onCancelButtonClicked
- // TODO: add onNextButtonClicked
+ onCancelButtonClicked: () -> Unit = {},
+ onNextButtonClicked: () -> Unit = {},
modifier: Modifier = Modifier
){
var selectedValue by rememberSaveable { mutableStateOf("") }
@@ -89,14 +89,14 @@ fun SelectOptionScreen(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp)
){
- OutlinedButton(modifier = Modifier.weight(1f), onClick = { /* TODO: handle cancel button */ }) {
+ OutlinedButton(modifier = Modifier.weight(1f), onClick = onCancelButtonClicked) {
Text(stringResource(R.string.cancel))
}
Button(
modifier = Modifier.weight(1f),
// the button is enabled when the user makes a selection
enabled = selectedValue.isNotEmpty(),
- onClick = { /* TODO: handle next button */ }
+ onClick = onNextButtonClicked
) {
Text(stringResource(R.string.next))
}
diff --git a/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/main/java/com/example/cupcake/ui/StartOrderScreen.kt b/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/main/java/com/example/cupcake/ui/StartOrderScreen.kt
index 0f41e7925..c7ee7e2a4 100644
--- a/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/main/java/com/example/cupcake/ui/StartOrderScreen.kt
+++ b/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/main/java/com/example/cupcake/ui/StartOrderScreen.kt
@@ -45,7 +45,7 @@ import com.example.cupcake.data.DataSource.quantityOptions
@Composable
fun StartOrderScreen(
quantityOptions: List>,
- // TODO: add onNextButtonClicked
+ onNextButtonClicked: (Int) -> Unit,
modifier: Modifier = Modifier
){
Column(
@@ -65,7 +65,7 @@ fun StartOrderScreen(
quantityOptions.forEach { item ->
SelectQuantityButton(
labelResourceId = item.first,
- onClick = { /* TODO: handle next button */ }
+ onClick = { onNextButtonClicked(item.second) }
)
}
}
@@ -92,5 +92,8 @@ fun SelectQuantityButton(
@Preview
@Composable
fun StartOrderPreview(){
- StartOrderScreen(quantityOptions = quantityOptions)
+ StartOrderScreen(
+ quantityOptions = quantityOptions,
+ onNextButtonClicked = {}
+ )
}
diff --git a/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/main/java/com/example/cupcake/ui/SummaryScreen.kt b/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/main/java/com/example/cupcake/ui/SummaryScreen.kt
index ff63a972e..5d4c07342 100644
--- a/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/main/java/com/example/cupcake/ui/SummaryScreen.kt
+++ b/Unit4/Pathway2/basic-android-kotlin-compose-training-cupcake/app/src/main/java/com/example/cupcake/ui/SummaryScreen.kt
@@ -44,8 +44,8 @@ import com.example.cupcake.ui.components.FormattedPriceLabel
@Composable
fun OrderSummaryScreen(
orderUiState: OrderUiState,
- // TODO: add onCancelButtonClicked
- // TODO: add onSendButtonClicked
+ onCancelButtonClicked: () -> Unit,
+ onSendButtonClicked: (String, String) -> Unit,
modifier: Modifier = Modifier
){
val resources = LocalContext.current.resources
@@ -90,13 +90,13 @@ fun OrderSummaryScreen(
)
Button(
modifier = Modifier.fillMaxWidth(),
- onClick = { /* TODO: handle send button */ }
+ onClick = { onSendButtonClicked(newOrder, orderSummary) }
) {
Text(stringResource(R.string.send))
}
OutlinedButton(
modifier = Modifier.fillMaxWidth(),
- onClick = { /* TODO: handle cancel button */ }
+ onClick = onCancelButtonClicked
) {
Text(stringResource(R.string.cancel))
}
@@ -108,5 +108,9 @@ fun OrderSummaryScreen(
fun OrderSummaryPreview(){
OrderSummaryScreen(
orderUiState = OrderUiState(0, "Test", "Test", "$300.00"),
+ onCancelButtonClicked = {},
+ onSendButtonClicked = { subject: String, summary: String ->
+
+ }
)
}