diff --git a/README.md b/README.md index 21eba5272..31f61f7db 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,14 @@ # PeriodPals -Our app is an inclusive cycle tracking tool designed to **support menstruating individuals** thanks to **innovative community support tools**, along with period tracking and symptom logging. -These new community features constitute a key innovation: users can discreetly request menstrual products from nearby users through geolocation-based notifications, ensuring privacy and encouraging real-time support. This feature not only helps users in need but also fosters a supportive community, making our app a comprehensive solution for menstrual health management. +Our app is an inclusive cycle tracking tool designed to **support menstruating individuals** thanks +to **innovative community support tools**, along with period tracking and symptom logging. +These new community features constitute a key innovation: users can discreetly request menstrual +products from nearby users through geolocation-based notifications, ensuring privacy and encouraging +real-time support. This feature not only helps users in need but also fosters a supportive +community, making our app a comprehensive solution for menstrual health management. ## Links + [Figma](https://www.figma.com/team_invite/redeem/MnyBeEvw4fKH4aV5aVBpPb) [Google Drive](https://docs.google.com/document/d/1-qGE7yrF2O_BGeR_vdvgo5ePdevHa0nPuL4w-9gv3MQ/edit?usp=sharing) [Architecture Diagram on Excalidraw](https://excalidraw.com/#json=lnlBs2IsbkRrnxmz0vlL1,VML2k7MzPW-Jo9j7Nq6rmQ) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9dbb9a1a6..f423141e6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,271 +1,273 @@ -import java.io.FileInputStream import java.util.Properties plugins { - // supabase setup - kotlin("plugin.serialization") version "2.0.0-RC1" + // supabase setup + kotlin("plugin.serialization") version "2.0.0-RC1" - alias(libs.plugins.androidApplication) - alias(libs.plugins.jetbrainsKotlinAndroid) - alias(libs.plugins.ktfmt) - //alias(libs.plugins.sonar) - alias(libs.plugins.compose.compiler) - id("jacoco") + alias(libs.plugins.androidApplication) + alias(libs.plugins.jetbrainsKotlinAndroid) + alias(libs.plugins.ktfmt) + //alias(libs.plugins.sonar) + alias(libs.plugins.compose.compiler) + id("jacoco") - id("org.sonarqube") version "5.1.0.4882" + id("org.sonarqube") version "5.1.0.4882" } android { - namespace = "com.android.periodpals" - compileSdk = 34 + namespace = "com.android.periodpals" + compileSdk = 34 - defaultConfig { - applicationId = "com.android.periodpals" - minSdk = 28 - targetSdk = 34 - versionCode = 1 - versionName = "1.0" + defaultConfig { + applicationId = "com.android.periodpals" + minSdk = 28 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - vectorDrawables { useSupportLibrary = true } + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { useSupportLibrary = true } - val properties = Properties() - properties.load(project.rootProject.file("local.properties").inputStream()) - buildConfigField("String", "SUPABASE_KEY", properties.getProperty("SUPABASE_KEY")) - buildConfigField("String", "SUPABASE_URL", properties.getProperty("SUPABASE_URL")) + val properties = Properties() + properties.load(project.rootProject.file("local.properties").inputStream()) + buildConfigField("String", "SUPABASE_KEY", properties.getProperty("SUPABASE_KEY")) + buildConfigField("String", "SUPABASE_URL", properties.getProperty("SUPABASE_URL")) - } + } - buildTypes { - release { - isMinifyEnabled = false - proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + + debug { + enableUnitTestCoverage = true + enableAndroidTestCoverage = true + } } - debug { - enableUnitTestCoverage = true - enableAndroidTestCoverage = true + testCoverage { jacocoVersion = "0.8.8" } + + buildFeatures { + compose = true + buildConfig = true } - } - - testCoverage { jacocoVersion = "0.8.8" } - - buildFeatures { - compose = true - buildConfig = true - } - - composeOptions { kotlinCompilerExtensionVersion = "1.5.1" } - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } - - kotlinOptions { jvmTarget = "11" } - - packaging { - resources { - excludes += "/META-INF/{AL2.0,LGPL2.1}" - merges += "META-INF/LICENSE.md" - merges += "META-INF/LICENSE-notice.md" - excludes += "META-INF/LICENSE-notice.md" - excludes += "META-INF/LICENSE.md" - excludes += "META-INF/LICENSE" - excludes += "META-INF/LICENSE.txt" - excludes += "META-INF/NOTICE" - excludes += "META-INF/NOTICE.txt" + + composeOptions { kotlinCompilerExtensionVersion = "1.5.1" } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } - } - testOptions { - unitTests { - isIncludeAndroidResources = true - isReturnDefaultValues = true + kotlinOptions { jvmTarget = "11" } + + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + merges += "META-INF/LICENSE.md" + merges += "META-INF/LICENSE-notice.md" + excludes += "META-INF/LICENSE-notice.md" + excludes += "META-INF/LICENSE.md" + excludes += "META-INF/LICENSE" + excludes += "META-INF/LICENSE.txt" + excludes += "META-INF/NOTICE" + excludes += "META-INF/NOTICE.txt" + } + } + + testOptions { + unitTests { + isIncludeAndroidResources = true + isReturnDefaultValues = true + } + packagingOptions { jniLibs { useLegacyPackaging = true } } + } + + // Robolectric needs to be run only in debug. But its tests are placed in the shared source set + // (test) + // The next lines transfers the src/test/* from shared to the testDebug one + // + // This prevent errors from occurring during unit tests + sourceSets.getByName("testDebug") { + val test = sourceSets.getByName("test") + + java.setSrcDirs(test.java.srcDirs) + res.setSrcDirs(test.res.srcDirs) + resources.setSrcDirs(test.resources.srcDirs) + } + + sourceSets.getByName("test") { + java.setSrcDirs(emptyList()) + res.setSrcDirs(emptyList()) + resources.setSrcDirs(emptyList()) } - packagingOptions { jniLibs { useLegacyPackaging = true } } - } - - // Robolectric needs to be run only in debug. But its tests are placed in the shared source set - // (test) - // The next lines transfers the src/test/* from shared to the testDebug one - // - // This prevent errors from occurring during unit tests - sourceSets.getByName("testDebug") { - val test = sourceSets.getByName("test") - - java.setSrcDirs(test.java.srcDirs) - res.setSrcDirs(test.res.srcDirs) - resources.setSrcDirs(test.resources.srcDirs) - } - - sourceSets.getByName("test") { - java.setSrcDirs(emptyList()) - res.setSrcDirs(emptyList()) - resources.setSrcDirs(emptyList()) - } } sonar { - properties { - property("sonar.projectKey", "periodpals_periodpals") - property("sonar.organization", "periodpals") - property("sonar.host.url", "https://sonarcloud.io") - // Comma-separated paths to the various directories containing the *.xml JUnit report files. - // Each path may be absolute or relative to the project base directory. - property( - "sonar.junit.reportPaths", - "${project.layout.buildDirectory.get()}/test-results/testDebugunitTest/" - ) - // Paths to xml files with Android Lint issues. If the main flavor is changed, this file will - // have to be changed too. - property( - "sonar.androidLint.reportPaths", - "${project.layout.buildDirectory.get()}/reports/lint-results-debug.xml" - ) - // Paths to JaCoCo XML coverage report files. - property( - "sonar.coverage.jacoco.xmlReportPaths", - "${project.layout.buildDirectory.get()}/reports/jacoco/jacocoTestReport/jacocoTestReport.xml" - ) - } + properties { + property("sonar.projectKey", "periodpals_periodpals") + property("sonar.organization", "periodpals") + property("sonar.host.url", "https://sonarcloud.io") + // Comma-separated paths to the various directories containing the *.xml JUnit report files. + // Each path may be absolute or relative to the project base directory. + property( + "sonar.junit.reportPaths", + "${project.layout.buildDirectory.get()}/test-results/testDebugunitTest/" + ) + // Paths to xml files with Android Lint issues. If the main flavor is changed, this file will + // have to be changed too. + property( + "sonar.androidLint.reportPaths", + "${project.layout.buildDirectory.get()}/reports/lint-results-debug.xml" + ) + // Paths to JaCoCo XML coverage report files. + property( + "sonar.coverage.jacoco.xmlReportPaths", + "${project.layout.buildDirectory.get()}/reports/jacoco/jacocoTestReport/jacocoTestReport.xml" + ) + } } // When a library is used both by robolectric and connected tests, use this function fun DependencyHandlerScope.globalTestImplementation(dep: Any) { - androidTestImplementation(dep) - testImplementation(dep) - testImplementation(libs.robolectric) - testImplementation("org.mockito:mockito-core:4.0.0") + androidTestImplementation(dep) + testImplementation(dep) + testImplementation(libs.robolectric) + testImplementation("org.mockito:mockito-core:4.0.0") } dependencies { - implementation(libs.play.services.auth) - //// Core - // implementation(libs.core.ktx) - // implementation(libs.androidx.core.ktx) - // implementation(libs.androidx.lifecycle.runtime.ktx) - // implementation(libs.androidx.activity.compose) - // implementation(libs.androidx.appcompat) - // implementation(libs.androidx.constraintlayout) - // implementation(libs.androidx.fragment.ktx) - // implementation(libs.kotlinx.serialization.json) - - implementation(libs.compose) - implementation(libs.mockk.v1120) - implementation(libs.androidx.ui.test.junit4.v105) - implementation(libs.androidx.ui.test.manifest.v105) - - configurations.all { - resolutionStrategy { - force("androidx.test.ext:junit:1.1.5") - force("androidx.test.espresso:espresso-core:3.5.0") + implementation(libs.play.services.auth) + //// Core + // implementation(libs.core.ktx) + // implementation(libs.androidx.core.ktx) + // implementation(libs.androidx.lifecycle.runtime.ktx) + // implementation(libs.androidx.activity.compose) + // implementation(libs.androidx.appcompat) + // implementation(libs.androidx.constraintlayout) + // implementation(libs.androidx.fragment.ktx) + // implementation(libs.kotlinx.serialization.json) + + implementation(libs.compose) + implementation(libs.mockk.v1120) + implementation(libs.androidx.ui.test.junit4.v105) + implementation(libs.androidx.ui.test.manifest.v105) + + configurations.all { + resolutionStrategy { + force("androidx.test.ext:junit:1.1.5") + force("androidx.test.espresso:espresso-core:3.5.0") + } } - } - // supabase setup - implementation(platform("io.github.jan-tennert.supabase:bom:3.0.0")) - implementation(libs.github.postgrest.kt) - implementation(libs.ktor.client.android.v300rc1) - implementation(libs.supabase.postgrest.kt) - implementation(libs.auth.kt) - implementation(libs.realtime.kt) - implementation(libs.ktor.client.android.v300rc1) - implementation(libs.kotlinx.serialization.json.v162) - - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.appcompat) - implementation(libs.material) - implementation(libs.androidx.lifecycle.runtime.ktx) - implementation(platform(libs.compose.bom)) - implementation(libs.androidx.navigation.compose.v282) - implementation(libs.androidx.espresso.intents) - implementation(libs.androidx.espresso.core) - - testImplementation(libs.junit) - testImplementation(libs.mockito.kotlin) - globalTestImplementation(libs.androidx.junit) - globalTestImplementation(libs.androidx.espresso.core) - - // ------------- Jetpack Compose ------------------ - val composeBom = platform(libs.compose.bom) - implementation(composeBom) - globalTestImplementation(composeBom) - - implementation(libs.compose.ui) - implementation(libs.compose.ui.graphics) - // Material Design 3 - implementation(libs.compose.material3) - // Integration with activities - implementation(libs.compose.activity) - // Integration with ViewModels - implementation(libs.compose.viewmodel) - // Android Studio Preview support - implementation(libs.compose.preview) - debugImplementation(libs.compose.tooling) - // UI Tests - globalTestImplementation(libs.compose.test.junit) - debugImplementation(libs.compose.test.manifest) - - // --------- Kaspresso test framework ---------- - globalTestImplementation(libs.kaspresso) - globalTestImplementation(libs.kaspresso.compose) - - // ---------- Robolectric ------------ - testImplementation(libs.robolectric) - - // Material Icons - implementation(libs.androidx.material.icons.extended) - // Mockito for unit testing - testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.core.v540) - - testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.1") - - // OpenStreetMap (osmdroid) dependency - implementation("org.osmdroid:osmdroid-android:6.1.13") - // Location Services - implementation("com.google.android.gms:play-services-location:21.0.1") + // supabase setup + implementation(platform("io.github.jan-tennert.supabase:bom:3.0.0")) + implementation(libs.github.postgrest.kt) + implementation(libs.ktor.client.android.v300rc1) + implementation(libs.supabase.postgrest.kt) + implementation(libs.auth.kt) + implementation(libs.realtime.kt) + implementation(libs.ktor.client.android.v300rc1) + implementation(libs.kotlinx.serialization.json.v162) + + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.material) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(platform(libs.compose.bom)) + implementation(libs.androidx.navigation.compose.v282) + implementation(libs.androidx.espresso.intents) + implementation(libs.androidx.espresso.core) + + testImplementation(libs.junit) + testImplementation(libs.mockito.kotlin) + globalTestImplementation(libs.androidx.junit) + globalTestImplementation(libs.androidx.espresso.core) + + // ------------- Jetpack Compose ------------------ + val composeBom = platform(libs.compose.bom) + implementation(composeBom) + globalTestImplementation(composeBom) + + implementation(libs.compose.ui) + implementation(libs.compose.ui.graphics) + // Material Design 3 + implementation(libs.compose.material3) + // Integration with activities + implementation(libs.compose.activity) + // Integration with ViewModels + implementation(libs.compose.viewmodel) + // Android Studio Preview support + implementation(libs.compose.preview) + debugImplementation(libs.compose.tooling) + // UI Tests + globalTestImplementation(libs.compose.test.junit) + debugImplementation(libs.compose.test.manifest) + + // --------- Kaspresso test framework ---------- + globalTestImplementation(libs.kaspresso) + globalTestImplementation(libs.kaspresso.compose) + + // ---------- Robolectric ------------ + testImplementation(libs.robolectric) + + // Material Icons + implementation(libs.androidx.material.icons.extended) + // Mockito for unit testing + testImplementation(libs.mockito.kotlin) + testImplementation(libs.mockito.core.v540) + + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.1") + + // OpenStreetMap (osmdroid) dependency + implementation("org.osmdroid:osmdroid-android:6.1.13") + // Location Services + implementation("com.google.android.gms:play-services-location:21.0.1") } tasks.withType { - // Configure Jacoco for each tests - configure { - isIncludeNoLocationClasses = true - excludes = listOf("jdk.internal.*") - } + // Configure Jacoco for each tests + configure { + isIncludeNoLocationClasses = true + excludes = listOf("jdk.internal.*") + } } tasks.register("jacocoTestReport", JacocoReport::class) { - mustRunAfter("testDebugUnitTest", "connectedDebugAndroidTest") - - reports { - xml.required = true - html.required = true - } - - val fileFilter = - listOf( - "**/R.class", - "**/R$*.class", - "**/BuildConfig.*", - "**/Manifest*.*", - "**/*Test*.*", - "android/**/*.*", - ) - - val debugTree = - fileTree("${project.layout.buildDirectory.get()}/tmp/kotlin-classes/debug") { - exclude(fileFilter) + mustRunAfter("testDebugUnitTest", "connectedDebugAndroidTest") + + reports { + xml.required = true + html.required = true } - val mainSrc = "${project.layout.projectDirectory}/src/main/java" - sourceDirectories.setFrom(files(mainSrc)) - classDirectories.setFrom(files(debugTree)) - executionData.setFrom( - fileTree(project.layout.buildDirectory.get()) { - include("outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec") - include("outputs/code_coverage/debugAndroidTest/connected/*/coverage.ec") - }) + val fileFilter = + listOf( + "**/R.class", + "**/R$*.class", + "**/BuildConfig.*", + "**/Manifest*.*", + "**/*Test*.*", + "android/**/*.*", + ) + + val debugTree = + fileTree("${project.layout.buildDirectory.get()}/tmp/kotlin-classes/debug") { + exclude(fileFilter) + } + + val mainSrc = "${project.layout.projectDirectory}/src/main/java" + sourceDirectories.setFrom(files(mainSrc)) + classDirectories.setFrom(files(debugTree)) + executionData.setFrom( + fileTree(project.layout.buildDirectory.get()) { + include("outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec") + include("outputs/code_coverage/debugAndroidTest/connected/*/coverage.ec") + }) } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d95499b59..0d6577212 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ + @@ -12,10 +13,10 @@ android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" - android:usesCleartextTraffic="false" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.PeriodPals" + android:usesCleartextTraffic="false" tools:targetApi="31"> - - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 036d09bc5..c9ad5f98f 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index 3e10e95a7..486a07347 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -1,4 +1,4 @@ - +