Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ dependencies {
implementation(libs.hilt.android)
ksp(libs.hilt.android.compiler)

// Splash screen
implementation(libs.androidx.core.splashscreen)

implementation(project(":common"))
implementation(project(":scaffold"))
implementation(project(":localData"))
Expand Down
8 changes: 4 additions & 4 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/mvp_icon"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/mvp_icon"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.SayItAgain"
android:theme="@style/Theme.Sia.Start"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:theme="@style/Theme.SayItAgain">
android:theme="@style/Theme.Sia.Start">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>

Expand Down
Binary file added app/src/main/ic_launcher-playstore.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions app/src/main/java/eu/project/sayitagain/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
Expand Down Expand Up @@ -53,6 +54,7 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)
installSplashScreen()
enableEdgeToEdge()

setContent {
Expand Down
12 changes: 12 additions & 0 deletions app/src/main/res/drawable/sia_icon.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="512dp"
android:height="512dp"
android:viewportWidth="512"
android:viewportHeight="512">
<group
android:name="animation">
<path
android:pathData="M186.78,319.64C178.4,319.64 170.77,318.7 163.89,316.83C157.02,314.95 150.24,311.7 143.58,307.06V279.56H161.23L163.66,295.73C166,297.71 169.23,299.33 173.34,300.58C177.46,301.78 181.94,302.38 186.78,302.38C190.9,302.38 194.36,301.8 197.17,300.66C200.04,299.51 202.2,297.9 203.66,295.81C205.17,293.68 205.92,291.18 205.92,288.31C205.92,285.6 205.24,283.21 203.89,281.13C202.59,278.99 200.4,277.06 197.33,275.34C194.31,273.57 190.19,271.88 184.98,270.27C175.97,267.61 168.5,264.72 162.56,261.59C156.68,258.42 152.25,254.64 149.28,250.27C146.37,245.84 144.91,240.53 144.91,234.33C144.91,228.18 146.6,222.77 149.98,218.08C153.42,213.34 158.16,209.62 164.2,206.91C170.24,204.2 177.22,202.79 185.14,202.69C193.89,202.58 201.63,203.63 208.34,205.81C215.12,208 220.95,211.05 225.84,214.95V240.58H208.73L205.77,224.95C203.79,223.7 201.1,222.61 197.72,221.67C194.38,220.73 190.58,220.27 186.31,220.27C182.67,220.27 179.44,220.81 176.63,221.91C173.87,223 171.68,224.59 170.06,226.67C168.45,228.76 167.64,231.28 167.64,234.25C167.64,236.8 168.34,239.04 169.75,240.97C171.16,242.84 173.5,244.64 176.78,246.36C180.12,248.03 184.7,249.82 190.53,251.75C203.08,255.24 212.59,259.85 219.05,265.58C225.56,271.31 228.81,278.83 228.81,288.16C228.81,294.61 227.07,300.21 223.58,304.95C220.09,309.64 215.19,313.26 208.89,315.81C202.59,318.36 195.22,319.64 186.78,319.64ZM237.72,318V304.33L248.73,301.98V249.56L236.55,247.22V233.47H271.55V301.98L282.48,304.33V318H237.72ZM247.95,215.58V196.13H271.55V215.58H247.95ZM318.42,319.64C310.04,319.64 303.37,317.4 298.42,312.92C293.47,308.39 291,302.24 291,294.48C291,286.52 294.23,280.06 300.69,275.11C307.15,270.11 316.6,267.61 329.05,267.61H340.22V261.36C340.22,257.3 339.02,254.02 336.63,251.52C334.28,248.96 330.74,247.69 326,247.69C323.55,247.69 321.36,248 319.44,248.63C317.51,249.2 315.74,250.03 314.13,251.13L312.33,262.3H295.92L295.61,240.34C300.04,237.84 304.83,235.81 309.98,234.25C315.14,232.69 320.9,231.91 327.25,231.91C338.29,231.91 347.02,234.48 353.42,239.64C359.83,244.74 363.03,252.06 363.03,261.59V296.44C363.03,297.58 363.03,298.73 363.03,299.88C363.08,300.97 363.19,302.04 363.34,303.08L372.17,304.33V318H343.89C343.32,316.54 342.74,314.98 342.17,313.31C341.6,311.65 341.18,309.98 340.92,308.31C338.21,311.75 335.01,314.51 331.31,316.59C327.67,318.63 323.37,319.64 318.42,319.64ZM324.52,302.77C327.69,302.77 330.71,302.06 333.58,300.66C336.44,299.2 338.66,297.3 340.22,294.95V281.59H328.97C323.97,281.59 320.17,282.74 317.56,285.03C315.01,287.32 313.73,290.08 313.73,293.31C313.73,296.33 314.67,298.68 316.55,300.34C318.47,301.96 321.13,302.77 324.52,302.77Z"
android:fillColor="#00DBDBDB"/>
</group>
</vector>
6 changes: 6 additions & 0 deletions app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<monochrome android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
6 changes: 6 additions & 0 deletions app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<monochrome android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
Binary file added app/src/main/res/mipmap-hdpi/ic_launcher.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed app/src/main/res/mipmap-hdpi/mvp_icon.png
Binary file not shown.
Binary file added app/src/main/res/mipmap-mdpi/ic_launcher.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions app/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="background">#FF1E1E1E</color>
</resources>
4 changes: 4 additions & 0 deletions app/src/main/res/values/ic_launcher_background.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#1E1E1E</color>
</resources>
2 changes: 1 addition & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<resources>
<string name="app_name">Say It Again</string>
<string name="app_name">Sia</string>
</resources>
13 changes: 12 additions & 1 deletion app/src/main/res/values/themes.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.SayItAgain" parent="android:Theme.Material.Light.NoActionBar"/>
<style name="Theme.Sia" parent="android:Theme.Material.Light.NoActionBar" />

<style name="Theme.Sia.Start" parent="Theme.SplashScreen">
<!-- Set the splash screen background, animated icon, and animation duration. -->
<item name="windowSplashScreenBackground">@color/background</item>

<!-- Use windowSplashScreenAnimatedIcon to add a drawable or an animated drawable. One of these is required. -->
<item name="windowSplashScreenAnimatedIcon">@drawable/sia_icon</item>

<!-- Set the theme of the Activity that directly follows your splash screen. This is required. -->
<item name="postSplashScreenTheme">@style/Theme.Sia</item>
</style>
</resources>
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[versions]
converterGsonVersion = "3.0.0"
coreSplashscreenVersion = "1.2.0"
hiltNavigationComposeVersion = "1.2.0"
activityComposeVersion = "1.10.1"
composeBom = "2026.03.00"
Expand Down Expand Up @@ -36,6 +37,7 @@ ksp = "2.2.21-2.0.4"
[libraries]
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityComposeVersion" }
androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "composeBom" }
androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreenVersion" }
androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espressoCore" }
androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigationComposeVersion" }
androidx-junit = { module = "androidx.test.ext:junit", version.ref = "junitVersion" }
Expand Down
4 changes: 4 additions & 0 deletions ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,8 @@ dependencies {
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(libs.androidx.ui.test.junit4)

// Compose UI test
androidTestImplementation(libs.ui.test.junit4)
debugImplementation(libs.ui.test.manifest)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package eu.project.design_system.component

import android.annotation.SuppressLint
import androidx.compose.material3.Text
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipeDown
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test

class BottomSheetTest {

@get:Rule
val composeTestRule = createComposeRule()

private val testTag = "test_bottom_sheet"
private val testContentTag = "test_bottom_sheet_content"
private val testData = "Test Content"



//- Visibility -------------------------------------------------------------------------------------

@Test
fun whenStateIsShown_contentIsDisplayed() {
composeTestRule.setContent {
BottomSheet(
state = BottomSheetState.Shown(testData),
onDismissRequest = {},
testTag = testTag
) { data ->
Text(
text = data,
modifier = Modifier.testTag(testContentTag)
)
}
}

composeTestRule.onNodeWithTag(testContentTag).assertIsDisplayed()
composeTestRule.onNodeWithText(testData).assertIsDisplayed()
}



//- Dismiss behavior -------------------------------------------------------------------------------

@SuppressLint("CheckResult")
@Test
fun whenDismissed_onDismissRequestIsInvoked() {
var dismissed = false

composeTestRule.setContent {
BottomSheet(
state = BottomSheetState.Shown(testData),
onDismissRequest = { dismissed = true },
testTag = testTag
) { data ->
Text(text = data)
}
}

composeTestRule.onNodeWithTag(testTag).performTouchInput { this.swipeDown() }

composeTestRule.runOnIdle {
assertTrue(dismissed)
}
}



//- Data passing -----------------------------------------------------------------------------------

@Test
fun whenStateIsShownWithData_dataIsPassedToContent() {
val specificData = "Specific Test Data"
var receivedData: String? = null

composeTestRule.setContent {
BottomSheet(
state = BottomSheetState.Shown(specificData),
onDismissRequest = {},
testTag = testTag
) { data ->
receivedData = data
Text(text = data)
}
}

composeTestRule.runOnIdle {
assertEquals(specificData, receivedData)
}
}



//- State transitions ------------------------------------------------------------------------------

@Test
fun whenStateChangesFromHiddenToShown_contentBecomesVisible() {
var state: BottomSheetState<String> by mutableStateOf(BottomSheetState.Hidden)

composeTestRule.setContent {
BottomSheet(
state = state,
onDismissRequest = {},
testTag = testTag
) { data ->
Text(
text = data,
modifier = Modifier.testTag(testContentTag)
)
}
}

composeTestRule.onNodeWithTag(testContentTag).assertDoesNotExist()

state = BottomSheetState.Shown(testData)

composeTestRule.onNodeWithTag(testContentTag).assertIsDisplayed()
}

@Test
fun whenStateChangesFromShownToHidden_contentBecomesInvisible() {
var state: BottomSheetState<String> by mutableStateOf(BottomSheetState.Shown(testData))

composeTestRule.setContent {
BottomSheet(
state = state,
onDismissRequest = {},
testTag = testTag
) { data ->
Text(
text = data,
modifier = Modifier.testTag(testContentTag)
)
}
}

composeTestRule.onNodeWithTag(testContentTag).assertIsDisplayed()

state = BottomSheetState.Hidden

composeTestRule.onNodeWithTag(testTag).assertDoesNotExist()
composeTestRule.onNodeWithTag(testContentTag).assertDoesNotExist()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package eu.project.design_system.component

import androidx.compose.ui.test.assertIsOff
import androidx.compose.ui.test.assertIsOn
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test

class CheckboxTest {

@get:Rule
val composeTestRule = createComposeRule()

private val testTag = "test_checkbox"



//- State renders correct content ------------------------------------------------------------------

@Test
fun whenStateIsSelected_checkboxIsOn() {
composeTestRule.setContent {
Checkbox(
state = CheckboxState.Selected,
onToggle = {},
testTag = testTag
)
}

composeTestRule
.onNodeWithTag(testTag)
.assertIsOn()
}

@Test
fun whenStateIsUnselected_checkboxIsOff() {
composeTestRule.setContent {
Checkbox(
state = CheckboxState.Unselected,
onToggle = {},
testTag = testTag
)
}

composeTestRule
.onNodeWithTag(testTag)
.assertIsOff()
}



//- Click behavior ---------------------------------------------------------------------------------

@Test
fun whenClicked_onToggleIsInvoked() {
var toggled = false

composeTestRule.setContent {
Checkbox(
state = CheckboxState.Unselected,
onToggle = { toggled = true },
testTag = testTag
)
}

composeTestRule
.onNodeWithTag(testTag)
.performClick()

assertTrue(toggled)
}
}
Loading
Loading