From 4d783090d3f41911f1bc3a892103af1f115d9441 Mon Sep 17 00:00:00 2001 From: omer358 Date: Wed, 12 Jun 2024 23:08:23 +0400 Subject: [PATCH 01/18] impl the peopleList screen --- .idea/deploymentTargetSelector.xml | 3 + app/build.gradle.kts | 2 + .../com/example/rememberme/RememberMeApp.kt | 23 ++---- .../example/rememberme/data/FakeDataSource.kt | 72 +++++++++++++++++++ .../rememberme/data/database/People.kt | 2 +- .../composable/PeopleListItem.kt} | 32 +++++++-- .../presentation/people/PeopleListScreen.kt | 46 ++++++++++++ .../presentation/people/PeopleViewModel.kt | 4 ++ 8 files changed, 161 insertions(+), 23 deletions(-) create mode 100644 app/src/main/java/com/example/rememberme/data/FakeDataSource.kt rename app/src/main/java/com/example/rememberme/{common/composable/ListItem.kt => presentation/composable/PeopleListItem.kt} (62%) create mode 100644 app/src/main/java/com/example/rememberme/presentation/people/PeopleListScreen.kt create mode 100644 app/src/main/java/com/example/rememberme/presentation/people/PeopleViewModel.kt diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index 5f6e9e3..b8b08a8 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -13,6 +13,9 @@ + + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6d2872e..fad0584 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -77,6 +77,8 @@ dependencies { // ViewModel utilities for Compose implementation(libs.androidx.lifecycle.viewmodel.compose) + + // Splash screen implementation(libs.androidx.core.splashscreen) diff --git a/app/src/main/java/com/example/rememberme/RememberMeApp.kt b/app/src/main/java/com/example/rememberme/RememberMeApp.kt index c0abf3d..6d28fb7 100644 --- a/app/src/main/java/com/example/rememberme/RememberMeApp.kt +++ b/app/src/main/java/com/example/rememberme/RememberMeApp.kt @@ -1,6 +1,5 @@ package com.example.rememberme -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api @@ -12,16 +11,16 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import com.example.rememberme.common.composable.ListItem +import com.example.rememberme.presentation.people.PeopleScreen import com.example.rememberme.ui.theme.RememberMeTheme @OptIn(ExperimentalMaterial3Api::class) @Composable -fun RememberMeApp(){ - RememberMeTheme{ +fun RememberMeApp() { + RememberMeTheme { Surface( modifier = Modifier.fillMaxSize(), - ){ + ) { Scaffold( topBar = { TopAppBar( @@ -30,16 +29,8 @@ fun RememberMeApp(){ } ) } - ){ - Column( - modifier = Modifier.padding(it) - ) { - ListItem(name = "Otba", meetPlace = "Abu Dhabi",{ println("Clicked!")}) - ListItem(name = "Otba", meetPlace = "Abu Dhabi",{println("Clicked!")}) - ListItem(name = "Otba", meetPlace = "Abu Dhabi",{println("Clicked!")}) - ListItem(name = "Otba", meetPlace = "Abu Dhabi",{println("Clicked!")}) - - } + ) { + PeopleScreen(modifier = Modifier.padding(it)) } } } @@ -48,6 +39,6 @@ fun RememberMeApp(){ @Preview(showBackground = true) @Composable -fun RememberMeAppPreview(){ +fun RememberMeAppPreview() { RememberMeApp() } diff --git a/app/src/main/java/com/example/rememberme/data/FakeDataSource.kt b/app/src/main/java/com/example/rememberme/data/FakeDataSource.kt new file mode 100644 index 0000000..06b617d --- /dev/null +++ b/app/src/main/java/com/example/rememberme/data/FakeDataSource.kt @@ -0,0 +1,72 @@ +package com.example.rememberme.data + +import com.example.rememberme.R +import com.example.rememberme.data.database.People + +object FakeDataSource { + fun getPeopleList(): List { + val firstNames = listOf( + "John", "Jane", "Michael", "Emily", "Robert", "Linda", "David", "Sarah", "James", "Jessica", + "William", "Amanda", "Joseph", "Ashley", "Charles", "Melissa", "Thomas", "Stephanie", "Daniel", "Laura", + "Matthew", "Rachel", "Andrew", "Megan", "Joshua", "Jennifer", "Anthony", "Samantha", "Mark", "Elizabeth" + ) + val lastNames = listOf( + "Doe", "Smith", "Johnson", "Davis", "Brown", "Taylor", "Wilson", "Moore", "Jackson", "White", + "Harris", "Martin", "Thompson", "Garcia", "Martinez", "Robinson", "Clark", "Rodriguez", "Lewis", "Lee", + "Walker", "Hall", "Allen", "Young", "King", "Wright", "Scott", "Torres", "Nguyen", "Hill" + ) + val places = listOf( + "Central Park", "Starbucks", "Office", "Gym", "Conference Hall", "Library", "Mall", "Restaurant", "Beach", "Museum", + "Cinema", "Zoo", "Amusement Park", "Aquarium", "Sports Arena", "Theater", "Cafe", "Bookstore", "Airport", "Train Station", + "Bus Stop", "Hotel", "Hospital", "Clinic", "School", "University", "Park", "Playground", "Concert Hall", "Night Club" + ) + val times = listOf( + "12:00 PM", "9:00 AM", "10:00 AM", "6:00 PM", "3:00 PM", "2:00 PM", "11:00 AM", "1:00 PM", "4:00 PM", "5:00 PM", + "7:00 PM", "8:00 PM", "6:30 PM", "9:30 AM", "10:30 AM", "11:30 AM", "12:30 PM", "1:30 PM", "2:30 PM", "3:30 PM", + "4:30 PM", "5:30 PM", "6:00 AM", "7:00 AM", "8:00 AM", "8:30 AM", "7:30 AM", "6:00 AM", "9:00 PM", "10:00 PM" + ) + val notes = listOf( + "Met during lunch break", "Had a coffee together", "Discussed project details", "Met during workout", "Attended a seminar", "Studied together", + "Shopped for clothes", "Had dinner together", "Relaxed on the beach", "Visited an exhibition", "Watched a movie", "Saw animals", "Enjoyed rides", + "Saw marine life", "Watched a game", "Watched a play", "Had a snack", "Bought books", "Caught a flight", "Caught a train", "Waited for the bus", + "Stayed overnight", "Had a checkup", "Received treatment", "Attended classes", "Studied together", "Walked in the park", "Played together", + "Attended a concert", "Danced at the club" + ) + + return List(30) { index -> + People( + firstName = firstNames[index % firstNames.size], + secondName = lastNames[index % lastNames.size], + place = places[index % places.size], + time = times[index % times.size], + note = notes[index % notes.size], + gender = if (index % 2 == 0) "Male" else "Female", + avatar = if (index % 2 == 0) { + getMaleAvatar(index) + } else { + getFemaleAvatar(index) + } + ) + } + } + + private fun getMaleAvatar(index: Int): Int { + return when (index % 5) { + 0 -> R.drawable.ic_m1 + 1 -> R.drawable.ic_m2 + 2 -> R.drawable.ic_m3 + 3 -> R.drawable.ic_m4 + else -> R.drawable.ic_m5 + } + } + + private fun getFemaleAvatar(index: Int): Int { + return when (index % 5) { + 0 -> R.drawable.ic_f1 + 1 -> R.drawable.ic_f2 + 2 -> R.drawable.ic_f3 + 3 -> R.drawable.ic_f4 + else -> R.drawable.ic_f5 + } + } +} diff --git a/app/src/main/java/com/example/rememberme/data/database/People.kt b/app/src/main/java/com/example/rememberme/data/database/People.kt index 827d698..f0a9615 100644 --- a/app/src/main/java/com/example/rememberme/data/database/People.kt +++ b/app/src/main/java/com/example/rememberme/data/database/People.kt @@ -12,7 +12,7 @@ data class People ( @ColumnInfo(name = "second_name") var secondName: String, @ColumnInfo(name = "meeting_place") var place: String, @ColumnInfo(name = "meeting_time") var time: String, - @ColumnInfo(name ="note")var note:String?, + @ColumnInfo(name ="note")var note:String? = null, @ColumnInfo(name = "registration_time")val registrationTime: Long = System.currentTimeMillis(), var gender:String, var avatar: Int) \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/common/composable/ListItem.kt b/app/src/main/java/com/example/rememberme/presentation/composable/PeopleListItem.kt similarity index 62% rename from app/src/main/java/com/example/rememberme/common/composable/ListItem.kt rename to app/src/main/java/com/example/rememberme/presentation/composable/PeopleListItem.kt index e36d384..077330c 100644 --- a/app/src/main/java/com/example/rememberme/common/composable/ListItem.kt +++ b/app/src/main/java/com/example/rememberme/presentation/composable/PeopleListItem.kt @@ -1,5 +1,6 @@ -package com.example.rememberme.common.composable +package com.example.rememberme.presentation.composable +import android.content.res.Configuration import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.padding @@ -14,16 +15,21 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.example.rememberme.R +import com.example.rememberme.data.database.People import com.example.rememberme.ui.theme.RememberMeTheme @Composable -fun ListItem(name: String, meetPlace: String,onClickListener: () -> Unit, modifier: Modifier = Modifier) { +fun PeopleListItem( + people: People, + onClickListener: () -> Unit, + modifier: Modifier = Modifier +) { ListItem( modifier = modifier .padding(horizontal = 8.dp, vertical = 4.dp) .clip(shape = RoundedCornerShape(8.dp)) .clickable { - onClickListener() + onClickListener() }, leadingContent = { Image( @@ -33,10 +39,13 @@ fun ListItem(name: String, meetPlace: String,onClickListener: () -> Unit, modifi ) }, headlineContent = { - Text(name) + Text(people.firstName +" "+ people.secondName) }, supportingContent = { - Text(meetPlace) + Text(people.place) + }, + trailingContent = { + Text(people.time) }, tonalElevation = 2.dp, // Adjust tonal elevation shadowElevation = 4.dp // Adjust shadow elevation, @@ -44,9 +53,20 @@ fun ListItem(name: String, meetPlace: String,onClickListener: () -> Unit, modifi } @Preview() +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable fun ListItemPreview() { RememberMeTheme { - ListItem("Omer", "Abu Dhabi",{}) + PeopleListItem( + people = People( + firstName = "Sara", + secondName = "Mustafa", + place = "London", + id = 1L, + avatar = R.drawable.ic_f4, + gender = "Female", + time = "10:10 AM" + ) + , {}) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/presentation/people/PeopleListScreen.kt b/app/src/main/java/com/example/rememberme/presentation/people/PeopleListScreen.kt new file mode 100644 index 0000000..83667d6 --- /dev/null +++ b/app/src/main/java/com/example/rememberme/presentation/people/PeopleListScreen.kt @@ -0,0 +1,46 @@ +package com.example.rememberme.presentation.people + +import android.content.res.Configuration +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.example.rememberme.data.FakeDataSource +import com.example.rememberme.presentation.composable.PeopleListItem +import com.example.rememberme.ui.theme.RememberMeTheme + +@Composable +fun PeopleScreen( + modifier: Modifier = Modifier, + viewModel: PeopleViewModel = PeopleViewModel() +) { + PeopleScreenContent(modifier) +} + +@Composable +fun PeopleScreenContent(modifier: Modifier = Modifier) { + + val peopleList = remember { FakeDataSource.getPeopleList() } + LazyColumn( + modifier = modifier + .fillMaxSize() + ) { + items(count = peopleList.size) { index -> + PeopleListItem(peopleList[index], {}) + } + } + +} + +@Preview(showBackground = true) +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +fun PeopleScreenContentPreview() { + RememberMeTheme { + PeopleScreenContent() + } +} + + diff --git a/app/src/main/java/com/example/rememberme/presentation/people/PeopleViewModel.kt b/app/src/main/java/com/example/rememberme/presentation/people/PeopleViewModel.kt new file mode 100644 index 0000000..e88ebcd --- /dev/null +++ b/app/src/main/java/com/example/rememberme/presentation/people/PeopleViewModel.kt @@ -0,0 +1,4 @@ +package com.example.rememberme.presentation.people + +class PeopleViewModel { +} \ No newline at end of file From ff831fb4a959dc0798a2e52eb4901723445dd557 Mon Sep 17 00:00:00 2001 From: omer358 Date: Thu, 13 Jun 2024 00:17:18 +0400 Subject: [PATCH 02/18] update java version to 17 --- app/build.gradle.kts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index fad0584..08cbff3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -30,11 +30,11 @@ android { } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = "1.8" + jvmTarget = "17" } buildFeatures { compose = true From ff153fc7dce40e0902b67590433df3043a52693f Mon Sep 17 00:00:00 2001 From: omer358 Date: Thu, 13 Jun 2024 00:18:01 +0400 Subject: [PATCH 03/18] add Github Actions to build the apk --- .github/workflows/android.yml | 43 +++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/android.yml diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 0000000..f376cde --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,43 @@ +name: Android CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'zulu' + cache: gradle + + - name: Cache Gradle packages + uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build with Gradle + run: ./gradlew build + + - name: Upload build artifacts + uses: actions/upload-artifact@v2 + with: + name: build + path: app/build/outputs/apk/release/*.apk + From 338da0b1d396aeccb2524a956c895a50bb380561 Mon Sep 17 00:00:00 2001 From: omer358 Date: Thu, 13 Jun 2024 02:14:52 +0400 Subject: [PATCH 04/18] change the app Theme --- app/build.gradle.kts | 4 + .../com/example/rememberme/ui/theme/Color.kt | 228 +++++++++++++++- .../com/example/rememberme/ui/theme/Theme.kt | 258 +++++++++++++++--- .../com/example/rememberme/ui/theme/Type.kt | 69 +++-- app/src/main/res/values/font_certs.xml | 32 +++ gradle/libs.versions.toml | 4 + 6 files changed, 528 insertions(+), 67 deletions(-) create mode 100644 app/src/main/res/values/font_certs.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 08cbff3..29b57e1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -50,6 +50,7 @@ android { } dependencies { + implementation(libs.androidx.ui.text.google.fonts) val lifecycle_version = "2.8.1" val arch_version = "2.2.0" val room_version = "2.6.1" @@ -81,5 +82,8 @@ dependencies { // Splash screen implementation(libs.androidx.core.splashscreen) + // Navigation Compose + implementation(libs.androidx.navigation.compose) + } \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/ui/theme/Color.kt b/app/src/main/java/com/example/rememberme/ui/theme/Color.kt index 0857a07..c929f49 100644 --- a/app/src/main/java/com/example/rememberme/ui/theme/Color.kt +++ b/app/src/main/java/com/example/rememberme/ui/theme/Color.kt @@ -1,11 +1,225 @@ package com.example.rememberme.ui.theme - import androidx.compose.ui.graphics.Color -val Purple80 = Color(0xFFD0BCFF) -val PurpleGrey80 = Color(0xFFCCC2DC) -val Pink80 = Color(0xFFEFB8C8) +val primaryLight = Color(0xFF396846) +val onPrimaryLight = Color(0xFFFFFFFF) +val primaryContainerLight = Color(0xFF87BA91) +val onPrimaryContainerLight = Color(0xFF002A11) +val secondaryLight = Color(0xFF506352) +val onSecondaryLight = Color(0xFFFFFFFF) +val secondaryContainerLight = Color(0xFFD4E9D4) +val onSecondaryContainerLight = Color(0xFF3B4D3E) +val tertiaryLight = Color(0xFF3C6186) +val onTertiaryLight = Color(0xFFFFFFFF) +val tertiaryContainerLight = Color(0xFF8CB1DA) +val onTertiaryContainerLight = Color(0xFF00253F) +val errorLight = Color(0xFFBA1A1A) +val onErrorLight = Color(0xFFFFFFFF) +val errorContainerLight = Color(0xFFFFDAD6) +val onErrorContainerLight = Color(0xFF410002) +val backgroundLight = Color(0xFFF9FAF5) +val onBackgroundLight = Color(0xFF191C19) +val surfaceLight = Color(0xFFF9FAF5) +val onSurfaceLight = Color(0xFF191C19) +val surfaceVariantLight = Color(0xFFDDE5DA) +val onSurfaceVariantLight = Color(0xFF414941) +val outlineLight = Color(0xFF717971) +val outlineVariantLight = Color(0xFFC1C9BF) +val scrimLight = Color(0xFF000000) +val inverseSurfaceLight = Color(0xFF2E312E) +val inverseOnSurfaceLight = Color(0xFFF0F1EC) +val inversePrimaryLight = Color(0xFF9FD3A8) +val surfaceDimLight = Color(0xFFD9DBD5) +val surfaceBrightLight = Color(0xFFF9FAF5) +val surfaceContainerLowestLight = Color(0xFFFFFFFF) +val surfaceContainerLowLight = Color(0xFFF3F4EF) +val surfaceContainerLight = Color(0xFFEDEEE9) +val surfaceContainerHighLight = Color(0xFFE7E9E3) +val surfaceContainerHighestLight = Color(0xFFE2E3DE) + +val primaryLightMediumContrast = Color(0xFF1C4C2C) +val onPrimaryLightMediumContrast = Color(0xFFFFFFFF) +val primaryContainerLightMediumContrast = Color(0xFF4F7F5A) +val onPrimaryContainerLightMediumContrast = Color(0xFFFFFFFF) +val secondaryLightMediumContrast = Color(0xFF354738) +val onSecondaryLightMediumContrast = Color(0xFFFFFFFF) +val secondaryContainerLightMediumContrast = Color(0xFF667968) +val onSecondaryContainerLightMediumContrast = Color(0xFFFFFFFF) +val tertiaryLightMediumContrast = Color(0xFF1D4568) +val onTertiaryLightMediumContrast = Color(0xFFFFFFFF) +val tertiaryContainerLightMediumContrast = Color(0xFF53789D) +val onTertiaryContainerLightMediumContrast = Color(0xFFFFFFFF) +val errorLightMediumContrast = Color(0xFF8C0009) +val onErrorLightMediumContrast = Color(0xFFFFFFFF) +val errorContainerLightMediumContrast = Color(0xFFDA342E) +val onErrorContainerLightMediumContrast = Color(0xFFFFFFFF) +val backgroundLightMediumContrast = Color(0xFFF9FAF5) +val onBackgroundLightMediumContrast = Color(0xFF191C19) +val surfaceLightMediumContrast = Color(0xFFF9FAF5) +val onSurfaceLightMediumContrast = Color(0xFF191C19) +val surfaceVariantLightMediumContrast = Color(0xFFDDE5DA) +val onSurfaceVariantLightMediumContrast = Color(0xFF3D453D) +val outlineLightMediumContrast = Color(0xFF596159) +val outlineVariantLightMediumContrast = Color(0xFF757D74) +val scrimLightMediumContrast = Color(0xFF000000) +val inverseSurfaceLightMediumContrast = Color(0xFF2E312E) +val inverseOnSurfaceLightMediumContrast = Color(0xFFF0F1EC) +val inversePrimaryLightMediumContrast = Color(0xFF9FD3A8) +val surfaceDimLightMediumContrast = Color(0xFFD9DBD5) +val surfaceBrightLightMediumContrast = Color(0xFFF9FAF5) +val surfaceContainerLowestLightMediumContrast = Color(0xFFFFFFFF) +val surfaceContainerLowLightMediumContrast = Color(0xFFF3F4EF) +val surfaceContainerLightMediumContrast = Color(0xFFEDEEE9) +val surfaceContainerHighLightMediumContrast = Color(0xFFE7E9E3) +val surfaceContainerHighestLightMediumContrast = Color(0xFFE2E3DE) + +val primaryLightHighContrast = Color(0xFF002911) +val onPrimaryLightHighContrast = Color(0xFFFFFFFF) +val primaryContainerLightHighContrast = Color(0xFF1C4C2C) +val onPrimaryContainerLightHighContrast = Color(0xFFFFFFFF) +val secondaryLightHighContrast = Color(0xFF152619) +val onSecondaryLightHighContrast = Color(0xFFFFFFFF) +val secondaryContainerLightHighContrast = Color(0xFF354738) +val onSecondaryContainerLightHighContrast = Color(0xFFFFFFFF) +val tertiaryLightHighContrast = Color(0xFF00243E) +val onTertiaryLightHighContrast = Color(0xFFFFFFFF) +val tertiaryContainerLightHighContrast = Color(0xFF1D4568) +val onTertiaryContainerLightHighContrast = Color(0xFFFFFFFF) +val errorLightHighContrast = Color(0xFF4E0002) +val onErrorLightHighContrast = Color(0xFFFFFFFF) +val errorContainerLightHighContrast = Color(0xFF8C0009) +val onErrorContainerLightHighContrast = Color(0xFFFFFFFF) +val backgroundLightHighContrast = Color(0xFFF9FAF5) +val onBackgroundLightHighContrast = Color(0xFF191C19) +val surfaceLightHighContrast = Color(0xFFF9FAF5) +val onSurfaceLightHighContrast = Color(0xFF000000) +val surfaceVariantLightHighContrast = Color(0xFFDDE5DA) +val onSurfaceVariantLightHighContrast = Color(0xFF1E261F) +val outlineLightHighContrast = Color(0xFF3D453D) +val outlineVariantLightHighContrast = Color(0xFF3D453D) +val scrimLightHighContrast = Color(0xFF000000) +val inverseSurfaceLightHighContrast = Color(0xFF2E312E) +val inverseOnSurfaceLightHighContrast = Color(0xFFFFFFFF) +val inversePrimaryLightHighContrast = Color(0xFFC4F9CC) +val surfaceDimLightHighContrast = Color(0xFFD9DBD5) +val surfaceBrightLightHighContrast = Color(0xFFF9FAF5) +val surfaceContainerLowestLightHighContrast = Color(0xFFFFFFFF) +val surfaceContainerLowLightHighContrast = Color(0xFFF3F4EF) +val surfaceContainerLightHighContrast = Color(0xFFEDEEE9) +val surfaceContainerHighLightHighContrast = Color(0xFFE7E9E3) +val surfaceContainerHighestLightHighContrast = Color(0xFFE2E3DE) + +val primaryDark = Color(0xFF9FD3A8) +val onPrimaryDark = Color(0xFF04391B) +val primaryContainerDark = Color(0xFF74A67F) +val onPrimaryContainerDark = Color(0xFF000E04) +val secondaryDark = Color(0xFFB7CCB8) +val onSecondaryDark = Color(0xFF233426) +val secondaryContainerDark = Color(0xFF314435) +val onSecondaryContainerDark = Color(0xFFC4DAC5) +val tertiaryDark = Color(0xFFA5CAF4) +val onTertiaryDark = Color(0xFF013355) +val tertiaryContainerDark = Color(0xFF799EC6) +val onTertiaryContainerDark = Color(0xFF000C19) +val errorDark = Color(0xFFFFB4AB) +val onErrorDark = Color(0xFF690005) +val errorContainerDark = Color(0xFF93000A) +val onErrorContainerDark = Color(0xFFFFDAD6) +val backgroundDark = Color(0xFF111411) +val onBackgroundDark = Color(0xFFE2E3DE) +val surfaceDark = Color(0xFF111411) +val onSurfaceDark = Color(0xFFE2E3DE) +val surfaceVariantDark = Color(0xFF414941) +val onSurfaceVariantDark = Color(0xFFC1C9BF) +val outlineDark = Color(0xFF8B938A) +val outlineVariantDark = Color(0xFF414941) +val scrimDark = Color(0xFF000000) +val inverseSurfaceDark = Color(0xFFE2E3DE) +val inverseOnSurfaceDark = Color(0xFF2E312E) +val inversePrimaryDark = Color(0xFF396846) +val surfaceDimDark = Color(0xFF111411) +val surfaceBrightDark = Color(0xFF373A36) +val surfaceContainerLowestDark = Color(0xFF0C0F0C) +val surfaceContainerLowDark = Color(0xFF191C19) +val surfaceContainerDark = Color(0xFF1D201D) +val surfaceContainerHighDark = Color(0xFF282B27) +val surfaceContainerHighestDark = Color(0xFF333532) + +val primaryDarkMediumContrast = Color(0xFFA3D7AC) +val onPrimaryDarkMediumContrast = Color(0xFF001B09) +val primaryContainerDarkMediumContrast = Color(0xFF74A67F) +val onPrimaryContainerDarkMediumContrast = Color(0xFF000000) +val secondaryDarkMediumContrast = Color(0xFFBBD0BC) +val onSecondaryDarkMediumContrast = Color(0xFF081A0D) +val secondaryContainerDarkMediumContrast = Color(0xFF829683) +val onSecondaryContainerDarkMediumContrast = Color(0xFF000000) +val tertiaryDarkMediumContrast = Color(0xFFA9CEF8) +val onTertiaryDarkMediumContrast = Color(0xFF00182C) +val tertiaryContainerDarkMediumContrast = Color(0xFF799EC6) +val onTertiaryContainerDarkMediumContrast = Color(0xFF000000) +val errorDarkMediumContrast = Color(0xFFFFBAB1) +val onErrorDarkMediumContrast = Color(0xFF370001) +val errorContainerDarkMediumContrast = Color(0xFFFF5449) +val onErrorContainerDarkMediumContrast = Color(0xFF000000) +val backgroundDarkMediumContrast = Color(0xFF111411) +val onBackgroundDarkMediumContrast = Color(0xFFE2E3DE) +val surfaceDarkMediumContrast = Color(0xFF111411) +val onSurfaceDarkMediumContrast = Color(0xFFFAFBF6) +val surfaceVariantDarkMediumContrast = Color(0xFF414941) +val onSurfaceVariantDarkMediumContrast = Color(0xFFC5CDC3) +val outlineDarkMediumContrast = Color(0xFF9DA59C) +val outlineVariantDarkMediumContrast = Color(0xFF7D857C) +val scrimDarkMediumContrast = Color(0xFF000000) +val inverseSurfaceDarkMediumContrast = Color(0xFFE2E3DE) +val inverseOnSurfaceDarkMediumContrast = Color(0xFF282B28) +val inversePrimaryDarkMediumContrast = Color(0xFF225131) +val surfaceDimDarkMediumContrast = Color(0xFF111411) +val surfaceBrightDarkMediumContrast = Color(0xFF373A36) +val surfaceContainerLowestDarkMediumContrast = Color(0xFF0C0F0C) +val surfaceContainerLowDarkMediumContrast = Color(0xFF191C19) +val surfaceContainerDarkMediumContrast = Color(0xFF1D201D) +val surfaceContainerHighDarkMediumContrast = Color(0xFF282B27) +val surfaceContainerHighestDarkMediumContrast = Color(0xFF333532) + +val primaryDarkHighContrast = Color(0xFFEFFFEE) +val onPrimaryDarkHighContrast = Color(0xFF000000) +val primaryContainerDarkHighContrast = Color(0xFFA3D7AC) +val onPrimaryContainerDarkHighContrast = Color(0xFF000000) +val secondaryDarkHighContrast = Color(0xFFEFFFEE) +val onSecondaryDarkHighContrast = Color(0xFF000000) +val secondaryContainerDarkHighContrast = Color(0xFFBBD0BC) +val onSecondaryContainerDarkHighContrast = Color(0xFF000000) +val tertiaryDarkHighContrast = Color(0xFFFAFAFF) +val onTertiaryDarkHighContrast = Color(0xFF000000) +val tertiaryContainerDarkHighContrast = Color(0xFFA9CEF8) +val onTertiaryContainerDarkHighContrast = Color(0xFF000000) +val errorDarkHighContrast = Color(0xFFFFF9F9) +val onErrorDarkHighContrast = Color(0xFF000000) +val errorContainerDarkHighContrast = Color(0xFFFFBAB1) +val onErrorContainerDarkHighContrast = Color(0xFF000000) +val backgroundDarkHighContrast = Color(0xFF111411) +val onBackgroundDarkHighContrast = Color(0xFFE2E3DE) +val surfaceDarkHighContrast = Color(0xFF111411) +val onSurfaceDarkHighContrast = Color(0xFFFFFFFF) +val surfaceVariantDarkHighContrast = Color(0xFF414941) +val onSurfaceVariantDarkHighContrast = Color(0xFFF5FDF2) +val outlineDarkHighContrast = Color(0xFFC5CDC3) +val outlineVariantDarkHighContrast = Color(0xFFC5CDC3) +val scrimDarkHighContrast = Color(0xFF000000) +val inverseSurfaceDarkHighContrast = Color(0xFFE2E3DE) +val inverseOnSurfaceDarkHighContrast = Color(0xFF000000) +val inversePrimaryDarkHighContrast = Color(0xFF003216) +val surfaceDimDarkHighContrast = Color(0xFF111411) +val surfaceBrightDarkHighContrast = Color(0xFF373A36) +val surfaceContainerLowestDarkHighContrast = Color(0xFF0C0F0C) +val surfaceContainerLowDarkHighContrast = Color(0xFF191C19) +val surfaceContainerDarkHighContrast = Color(0xFF1D201D) +val surfaceContainerHighDarkHighContrast = Color(0xFF282B27) +val surfaceContainerHighestDarkHighContrast = Color(0xFF333532) + + + + + + -val Purple40 = Color(0xFF6650a4) -val PurpleGrey40 = Color(0xFF625b71) -val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/ui/theme/Theme.kt b/app/src/main/java/com/example/rememberme/ui/theme/Theme.kt index d13b7fd..da32c5d 100644 --- a/app/src/main/java/com/example/rememberme/ui/theme/Theme.kt +++ b/app/src/main/java/com/example/rememberme/ui/theme/Theme.kt @@ -1,5 +1,6 @@ package com.example.rememberme.ui.theme +import android.app.Activity import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme @@ -8,50 +9,241 @@ import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.core.view.WindowCompat -private val DarkColorScheme = darkColorScheme( - primary = Purple80, - secondary = PurpleGrey80, - tertiary = Pink80 +private val lightScheme = lightColorScheme( + primary = primaryLight, + onPrimary = onPrimaryLight, + primaryContainer = primaryContainerLight, + onPrimaryContainer = onPrimaryContainerLight, + secondary = secondaryLight, + onSecondary = onSecondaryLight, + secondaryContainer = secondaryContainerLight, + onSecondaryContainer = onSecondaryContainerLight, + tertiary = tertiaryLight, + onTertiary = onTertiaryLight, + tertiaryContainer = tertiaryContainerLight, + onTertiaryContainer = onTertiaryContainerLight, + error = errorLight, + onError = onErrorLight, + errorContainer = errorContainerLight, + onErrorContainer = onErrorContainerLight, + background = backgroundLight, + onBackground = onBackgroundLight, + surface = surfaceLight, + onSurface = onSurfaceLight, + surfaceVariant = surfaceVariantLight, + onSurfaceVariant = onSurfaceVariantLight, + outline = outlineLight, + outlineVariant = outlineVariantLight, + scrim = scrimLight, + inverseSurface = inverseSurfaceLight, + inverseOnSurface = inverseOnSurfaceLight, + inversePrimary = inversePrimaryLight, ) -private val LightColorScheme = lightColorScheme( - primary = Purple40, - secondary = PurpleGrey40, - tertiary = Pink40 +private val darkScheme = darkColorScheme( + primary = primaryDark, + onPrimary = onPrimaryDark, + primaryContainer = primaryContainerDark, + onPrimaryContainer = onPrimaryContainerDark, + secondary = secondaryDark, + onSecondary = onSecondaryDark, + secondaryContainer = secondaryContainerDark, + onSecondaryContainer = onSecondaryContainerDark, + tertiary = tertiaryDark, + onTertiary = onTertiaryDark, + tertiaryContainer = tertiaryContainerDark, + onTertiaryContainer = onTertiaryContainerDark, + error = errorDark, + onError = onErrorDark, + errorContainer = errorContainerDark, + onErrorContainer = onErrorContainerDark, + background = backgroundDark, + onBackground = onBackgroundDark, + surface = surfaceDark, + onSurface = onSurfaceDark, + surfaceVariant = surfaceVariantDark, + onSurfaceVariant = onSurfaceVariantDark, + outline = outlineDark, + outlineVariant = outlineVariantDark, + scrim = scrimDark, + inverseSurface = inverseSurfaceDark, + inverseOnSurface = inverseOnSurfaceDark, + inversePrimary = inversePrimaryDark, +) + +private val mediumContrastLightColorScheme = lightColorScheme( + primary = primaryLightMediumContrast, + onPrimary = onPrimaryLightMediumContrast, + primaryContainer = primaryContainerLightMediumContrast, + onPrimaryContainer = onPrimaryContainerLightMediumContrast, + secondary = secondaryLightMediumContrast, + onSecondary = onSecondaryLightMediumContrast, + secondaryContainer = secondaryContainerLightMediumContrast, + onSecondaryContainer = onSecondaryContainerLightMediumContrast, + tertiary = tertiaryLightMediumContrast, + onTertiary = onTertiaryLightMediumContrast, + tertiaryContainer = tertiaryContainerLightMediumContrast, + onTertiaryContainer = onTertiaryContainerLightMediumContrast, + error = errorLightMediumContrast, + onError = onErrorLightMediumContrast, + errorContainer = errorContainerLightMediumContrast, + onErrorContainer = onErrorContainerLightMediumContrast, + background = backgroundLightMediumContrast, + onBackground = onBackgroundLightMediumContrast, + surface = surfaceLightMediumContrast, + onSurface = onSurfaceLightMediumContrast, + surfaceVariant = surfaceVariantLightMediumContrast, + onSurfaceVariant = onSurfaceVariantLightMediumContrast, + outline = outlineLightMediumContrast, + outlineVariant = outlineVariantLightMediumContrast, + scrim = scrimLightMediumContrast, + inverseSurface = inverseSurfaceLightMediumContrast, + inverseOnSurface = inverseOnSurfaceLightMediumContrast, + inversePrimary = inversePrimaryLightMediumContrast, +) + +private val highContrastLightColorScheme = lightColorScheme( + primary = primaryLightHighContrast, + onPrimary = onPrimaryLightHighContrast, + primaryContainer = primaryContainerLightHighContrast, + onPrimaryContainer = onPrimaryContainerLightHighContrast, + secondary = secondaryLightHighContrast, + onSecondary = onSecondaryLightHighContrast, + secondaryContainer = secondaryContainerLightHighContrast, + onSecondaryContainer = onSecondaryContainerLightHighContrast, + tertiary = tertiaryLightHighContrast, + onTertiary = onTertiaryLightHighContrast, + tertiaryContainer = tertiaryContainerLightHighContrast, + onTertiaryContainer = onTertiaryContainerLightHighContrast, + error = errorLightHighContrast, + onError = onErrorLightHighContrast, + errorContainer = errorContainerLightHighContrast, + onErrorContainer = onErrorContainerLightHighContrast, + background = backgroundLightHighContrast, + onBackground = onBackgroundLightHighContrast, + surface = surfaceLightHighContrast, + onSurface = onSurfaceLightHighContrast, + surfaceVariant = surfaceVariantLightHighContrast, + onSurfaceVariant = onSurfaceVariantLightHighContrast, + outline = outlineLightHighContrast, + outlineVariant = outlineVariantLightHighContrast, + scrim = scrimLightHighContrast, + inverseSurface = inverseSurfaceLightHighContrast, + inverseOnSurface = inverseOnSurfaceLightHighContrast, + inversePrimary = inversePrimaryLightHighContrast, +) + +private val mediumContrastDarkColorScheme = darkColorScheme( + primary = primaryDarkMediumContrast, + onPrimary = onPrimaryDarkMediumContrast, + primaryContainer = primaryContainerDarkMediumContrast, + onPrimaryContainer = onPrimaryContainerDarkMediumContrast, + secondary = secondaryDarkMediumContrast, + onSecondary = onSecondaryDarkMediumContrast, + secondaryContainer = secondaryContainerDarkMediumContrast, + onSecondaryContainer = onSecondaryContainerDarkMediumContrast, + tertiary = tertiaryDarkMediumContrast, + onTertiary = onTertiaryDarkMediumContrast, + tertiaryContainer = tertiaryContainerDarkMediumContrast, + onTertiaryContainer = onTertiaryContainerDarkMediumContrast, + error = errorDarkMediumContrast, + onError = onErrorDarkMediumContrast, + errorContainer = errorContainerDarkMediumContrast, + onErrorContainer = onErrorContainerDarkMediumContrast, + background = backgroundDarkMediumContrast, + onBackground = onBackgroundDarkMediumContrast, + surface = surfaceDarkMediumContrast, + onSurface = onSurfaceDarkMediumContrast, + surfaceVariant = surfaceVariantDarkMediumContrast, + onSurfaceVariant = onSurfaceVariantDarkMediumContrast, + outline = outlineDarkMediumContrast, + outlineVariant = outlineVariantDarkMediumContrast, + scrim = scrimDarkMediumContrast, + inverseSurface = inverseSurfaceDarkMediumContrast, + inverseOnSurface = inverseOnSurfaceDarkMediumContrast, + inversePrimary = inversePrimaryDarkMediumContrast, +) + +private val highContrastDarkColorScheme = darkColorScheme( + primary = primaryDarkHighContrast, + onPrimary = onPrimaryDarkHighContrast, + primaryContainer = primaryContainerDarkHighContrast, + onPrimaryContainer = onPrimaryContainerDarkHighContrast, + secondary = secondaryDarkHighContrast, + onSecondary = onSecondaryDarkHighContrast, + secondaryContainer = secondaryContainerDarkHighContrast, + onSecondaryContainer = onSecondaryContainerDarkHighContrast, + tertiary = tertiaryDarkHighContrast, + onTertiary = onTertiaryDarkHighContrast, + tertiaryContainer = tertiaryContainerDarkHighContrast, + onTertiaryContainer = onTertiaryContainerDarkHighContrast, + error = errorDarkHighContrast, + onError = onErrorDarkHighContrast, + errorContainer = errorContainerDarkHighContrast, + onErrorContainer = onErrorContainerDarkHighContrast, + background = backgroundDarkHighContrast, + onBackground = onBackgroundDarkHighContrast, + surface = surfaceDarkHighContrast, + onSurface = onSurfaceDarkHighContrast, + surfaceVariant = surfaceVariantDarkHighContrast, + onSurfaceVariant = onSurfaceVariantDarkHighContrast, + outline = outlineDarkHighContrast, + outlineVariant = outlineVariantDarkHighContrast, + scrim = scrimDarkHighContrast, + inverseSurface = inverseSurfaceDarkHighContrast, + inverseOnSurface = inverseOnSurfaceDarkHighContrast, + inversePrimary = inversePrimaryDarkHighContrast, +) - /* Other default colors to override - background = Color(0xFFFFFBFE), - surface = Color(0xFFFFFBFE), - onPrimary = Color.White, - onSecondary = Color.White, - onTertiary = Color.White, - onBackground = Color(0xFF1C1B1F), - onSurface = Color(0xFF1C1B1F), - */ +@Immutable +data class ColorFamily( + val color: Color, + val onColor: Color, + val colorContainer: Color, + val onColorContainer: Color +) + +val unspecified_scheme = ColorFamily( + Color.Unspecified, Color.Unspecified, Color.Unspecified, Color.Unspecified ) @Composable fun RememberMeTheme( darkTheme: Boolean = isSystemInDarkTheme(), // Dynamic color is available on Android 12+ - dynamicColor: Boolean = true, - content: @Composable () -> Unit + dynamicColor: Boolean = false, + content: @Composable() () -> Unit ) { - val colorScheme = when { - dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { - val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) - } - - darkTheme -> DarkColorScheme - else -> LightColorScheme + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> darkScheme + else -> lightScheme + } + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + val window = (view.context as Activity).window + window.statusBarColor = colorScheme.primary.toArgb() + WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme } + } + + MaterialTheme( + colorScheme = colorScheme, + typography = AppTypography, + content = content + ) +} - MaterialTheme( - colorScheme = colorScheme, - typography = Typography, - content = content - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/ui/theme/Type.kt b/app/src/main/java/com/example/rememberme/ui/theme/Type.kt index c7df3b7..89d4a32 100644 --- a/app/src/main/java/com/example/rememberme/ui/theme/Type.kt +++ b/app/src/main/java/com/example/rememberme/ui/theme/Type.kt @@ -1,34 +1,49 @@ package com.example.rememberme.ui.theme import androidx.compose.material3.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 +import androidx.compose.ui.text.googlefonts.Font +import androidx.compose.ui.text.googlefonts.GoogleFont +import com.example.rememberme.R -// Set of Material typography styles to start with -val Typography = Typography( - bodyLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 16.sp, - lineHeight = 24.sp, - letterSpacing = 0.5.sp +val provider = GoogleFont.Provider( + providerAuthority = "com.google.android.gms.fonts", + providerPackage = "com.google.android.gms", + certificates = R.array.com_google_android_gms_fonts_certs +) + +val bodyFontFamily = FontFamily( + Font( + googleFont = GoogleFont("Barlow"), + fontProvider = provider, ) - /* Other default text styles to override - titleLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 22.sp, - lineHeight = 28.sp, - letterSpacing = 0.sp - ), - labelSmall = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Medium, - fontSize = 11.sp, - lineHeight = 16.sp, - letterSpacing = 0.5.sp +) + +val displayFontFamily = FontFamily( + Font( + googleFont = GoogleFont("Barlow"), + fontProvider = provider, ) - */ -) \ No newline at end of file +) + +// Default Material 3 typography values +val baseline = Typography() + +val AppTypography = Typography( + displayLarge = baseline.displayLarge.copy(fontFamily = displayFontFamily), + displayMedium = baseline.displayMedium.copy(fontFamily = displayFontFamily), + displaySmall = baseline.displaySmall.copy(fontFamily = displayFontFamily), + headlineLarge = baseline.headlineLarge.copy(fontFamily = displayFontFamily), + headlineMedium = baseline.headlineMedium.copy(fontFamily = displayFontFamily), + headlineSmall = baseline.headlineSmall.copy(fontFamily = displayFontFamily), + titleLarge = baseline.titleLarge.copy(fontFamily = displayFontFamily), + titleMedium = baseline.titleMedium.copy(fontFamily = displayFontFamily), + titleSmall = baseline.titleSmall.copy(fontFamily = displayFontFamily), + bodyLarge = baseline.bodyLarge.copy(fontFamily = bodyFontFamily), + bodyMedium = baseline.bodyMedium.copy(fontFamily = bodyFontFamily), + bodySmall = baseline.bodySmall.copy(fontFamily = bodyFontFamily), + labelLarge = baseline.labelLarge.copy(fontFamily = bodyFontFamily), + labelMedium = baseline.labelMedium.copy(fontFamily = bodyFontFamily), + labelSmall = baseline.labelSmall.copy(fontFamily = bodyFontFamily), +) + diff --git a/app/src/main/res/values/font_certs.xml b/app/src/main/res/values/font_certs.xml new file mode 100644 index 0000000..207b62f --- /dev/null +++ b/app/src/main/res/values/font_certs.xml @@ -0,0 +1,32 @@ + + + + + @array/com_google_android_gms_fonts_certs_dev + @array/com_google_android_gms_fonts_certs_prod + + + + MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs= + + + + + MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK + + + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b0abfb4..83bf67f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,12 +9,15 @@ espressoCore = "3.5.1" lifecycleRuntimeKtx = "2.8.1" activityCompose = "1.9.0" composeBom = "2023.08.00" +navigationCompose = "2.7.7" roomRuntime = "2.6.1" +uiTextGoogleFonts = "1.6.7" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" } androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" } +androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" } androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomRuntime" } androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomRuntime" } androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" } @@ -31,6 +34,7 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" } +androidx-ui-text-google-fonts = { group = "androidx.compose.ui", name = "ui-text-google-fonts", version.ref = "uiTextGoogleFonts" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } From 90dcd64fd921365a44d745dee915efa1b8de934a Mon Sep 17 00:00:00 2001 From: omer358 Date: Thu, 13 Jun 2024 02:16:23 +0400 Subject: [PATCH 05/18] define the basic of navigation --- .../com/example/rememberme/RememberMeApp.kt | 20 ++++++++++- .../presentation/navgraph/NavGraph.kt | 32 +++++++++++++++++ .../presentation/navgraph/Routes.kt | 13 +++++++ .../onboarding/OnBoardingEvent.kt | 5 +++ .../onboarding/OnBoardingScreen.kt | 35 +++++++++++++++++++ 5 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt create mode 100644 app/src/main/java/com/example/rememberme/presentation/navgraph/Routes.kt create mode 100644 app/src/main/java/com/example/rememberme/presentation/onboarding/OnBoardingEvent.kt create mode 100644 app/src/main/java/com/example/rememberme/presentation/onboarding/OnBoardingScreen.kt diff --git a/app/src/main/java/com/example/rememberme/RememberMeApp.kt b/app/src/main/java/com/example/rememberme/RememberMeApp.kt index 6d28fb7..387e35a 100644 --- a/app/src/main/java/com/example/rememberme/RememberMeApp.kt +++ b/app/src/main/java/com/example/rememberme/RememberMeApp.kt @@ -1,8 +1,13 @@ package com.example.rememberme +import android.content.res.Configuration.UI_MODE_NIGHT_YES import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -26,8 +31,20 @@ fun RememberMeApp() { TopAppBar( title = { Text(stringResource(id = R.string.app_name)) - } + }, ) + }, + floatingActionButton = { + FloatingActionButton( + onClick = { + + } + ) { + Icon( + imageVector = Icons.Default.Add, + contentDescription = "Add a new Person" + ) + } } ) { PeopleScreen(modifier = Modifier.padding(it)) @@ -38,6 +55,7 @@ fun RememberMeApp() { @Preview(showBackground = true) +@Preview(uiMode = UI_MODE_NIGHT_YES) @Composable fun RememberMeAppPreview() { RememberMeApp() diff --git a/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt b/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt new file mode 100644 index 0000000..4d75419 --- /dev/null +++ b/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt @@ -0,0 +1,32 @@ +package com.example.rememberme.presentation.navgraph + +import androidx.compose.runtime.Composable +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import androidx.navigation.navigation +import com.example.rememberme.presentation.onboarding.OnBoardingScreen + +@Composable +fun NavGraph( + startDestination: String +){ + val navController = rememberNavController() + NavHost( + navController = navController, + startDestination = startDestination + ){ + navigation( + route = Routes.OnBoardingScreen.route, + startDestination = Routes.OnBoardingScreen.route, + ){ + composable( + route = Routes.OnBoardingScreen.route + ) { + OnBoardingScreen( + ) + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/presentation/navgraph/Routes.kt b/app/src/main/java/com/example/rememberme/presentation/navgraph/Routes.kt new file mode 100644 index 0000000..b59cfce --- /dev/null +++ b/app/src/main/java/com/example/rememberme/presentation/navgraph/Routes.kt @@ -0,0 +1,13 @@ +package com.example.rememberme.presentation.navgraph + +sealed class Routes( + val route: String +) { + + data object OnBoardingScreen : Routes("onboardingScreen") + data object PeopleListScreen : Routes("peopleListScreen") + data object PersonDetailsScreen : Routes("personDetailScreen") + data object AddPersonScreen : Routes("addPersonScreen") + data object AppStartNavigation : Routes("appStartNavigation") + data object PeopleNavigation : Routes("peopleNavigation") +} \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/presentation/onboarding/OnBoardingEvent.kt b/app/src/main/java/com/example/rememberme/presentation/onboarding/OnBoardingEvent.kt new file mode 100644 index 0000000..5012f37 --- /dev/null +++ b/app/src/main/java/com/example/rememberme/presentation/onboarding/OnBoardingEvent.kt @@ -0,0 +1,5 @@ +package com.example.rememberme.presentation.onboarding + +sealed class OnBoardingEvent { + object SaveAppEntry: OnBoardingEvent() +} \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/presentation/onboarding/OnBoardingScreen.kt b/app/src/main/java/com/example/rememberme/presentation/onboarding/OnBoardingScreen.kt new file mode 100644 index 0000000..8116e67 --- /dev/null +++ b/app/src/main/java/com/example/rememberme/presentation/onboarding/OnBoardingScreen.kt @@ -0,0 +1,35 @@ +package com.example.rememberme.presentation.onboarding + +import android.content.res.Configuration.UI_MODE_NIGHT_YES +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.example.rememberme.ui.theme.RememberMeTheme + +@Composable +fun OnBoardingScreen(modifier: Modifier = Modifier){ + OnBoardingScreenContent(modifier) +} + +@Composable +fun OnBoardingScreenContent(modifier: Modifier = Modifier) { + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text(text = "OnBoarding Screen") + } +} + +@Preview +@Preview(uiMode = UI_MODE_NIGHT_YES) +@Composable +fun OnBoardingScreenPreview() { + RememberMeTheme { + OnBoardingScreenContent() + } +} \ No newline at end of file From 35e20f5583c145df0fda84f044810f53d1ffde2d Mon Sep 17 00:00:00 2001 From: omer358 Date: Thu, 13 Jun 2024 02:16:23 +0400 Subject: [PATCH 06/18] define the basic of navigation --- .idea/androidTestResultsUserPreferences.xml | 36 +++++ .idea/deploymentTargetSelector.xml | 10 +- .idea/kotlinc.xml | 2 +- app/build.gradle.kts | 23 ++- .../rememberme/ExampleInstrumentedTest.kt | 22 --- .../example/rememberme/PeopleDatabaseTest.kt | 131 ++++++++++++++++++ .../com/example/rememberme/RememberMeApp.kt | 36 +---- .../presentation/navgraph/NavGraph.kt | 23 ++- .../presentation/people/PeopleListScreen.kt | 47 ++++++- build.gradle.kts | 3 + gradle/libs.versions.toml | 22 ++- 11 files changed, 279 insertions(+), 76 deletions(-) create mode 100644 .idea/androidTestResultsUserPreferences.xml delete mode 100644 app/src/androidTest/java/com/example/rememberme/ExampleInstrumentedTest.kt create mode 100644 app/src/androidTest/java/com/example/rememberme/PeopleDatabaseTest.kt diff --git a/.idea/androidTestResultsUserPreferences.xml b/.idea/androidTestResultsUserPreferences.xml new file mode 100644 index 0000000..1413b29 --- /dev/null +++ b/.idea/androidTestResultsUserPreferences.xml @@ -0,0 +1,36 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index b8b08a8..ca96088 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -4,10 +4,10 @@ \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index fdf8d99..6d0ee1c 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 29b57e1..a4323a7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,6 +1,10 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.jetbrains.kotlin.android) + alias(libs.plugins.compose.compiler) + + id("com.google.devtools.ksp") + } android { @@ -40,7 +44,7 @@ android { compose = true } composeOptions { - kotlinCompilerExtensionVersion = "1.5.1" + kotlinCompilerExtensionVersion = "1.5.14" } packaging { resources { @@ -50,10 +54,6 @@ android { } dependencies { - implementation(libs.androidx.ui.text.google.fonts) - val lifecycle_version = "2.8.1" - val arch_version = "2.2.0" - val room_version = "2.6.1" implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) @@ -68,13 +68,26 @@ dependencies { androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(platform(libs.androidx.compose.bom)) androidTestImplementation(libs.androidx.ui.test.junit4) + testImplementation(libs.androidx.core.testing) debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) + androidTestImplementation(libs.truth) + androidTestImplementation(libs.core.testing) + implementation(libs.androidx.room.runtime) annotationProcessor(libs.androidx.room.compiler) implementation(libs.androidx.room.ktx) + testImplementation(libs.androidx.room.room.testing) + ksp(libs.androidx.room.compiler) + + + + // Google font + implementation(libs.androidx.ui.text.google.fonts) + + implementation(libs.androidx.junit.ktx) // ViewModel utilities for Compose implementation(libs.androidx.lifecycle.viewmodel.compose) diff --git a/app/src/androidTest/java/com/example/rememberme/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/example/rememberme/ExampleInstrumentedTest.kt deleted file mode 100644 index 0ca325e..0000000 --- a/app/src/androidTest/java/com/example/rememberme/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.example.rememberme - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith - -/** - * 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.rememberme", appContext.packageName) - } -} \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/rememberme/PeopleDatabaseTest.kt b/app/src/androidTest/java/com/example/rememberme/PeopleDatabaseTest.kt new file mode 100644 index 0000000..1947c63 --- /dev/null +++ b/app/src/androidTest/java/com/example/rememberme/PeopleDatabaseTest.kt @@ -0,0 +1,131 @@ +package com.example.rememberme + +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.example.rememberme.data.database.People +import com.example.rememberme.data.database.PeopleDatabase +import com.example.remindme.database.PeopleDao +import kotlinx.coroutines.runBlocking +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SmallTest +class PeopleDatabaseTest { + + private lateinit var database: PeopleDatabase + private lateinit var peopleDao: PeopleDao + + @Before + fun initDb() { + database = Room.inMemoryDatabaseBuilder( + ApplicationProvider.getApplicationContext(), + PeopleDatabase::class.java + ).allowMainThreadQueries().build() + + peopleDao = database.peopleDao + } + + @After + fun closeDb() { + if (this::database.isInitialized) { + database.close() + } + } + + @Test + fun insertAndGetPerson() = runBlocking { + val person = People( + firstName = "John", + secondName = "Doe", + place = "Park", + time = "2023-06-13 10:00", + note = "Met at the park", + gender = "Male", + avatar = 1 + ) + + peopleDao.insert(person) + + val retrievedPerson = peopleDao.getAll()[0] + Assert.assertEquals(retrievedPerson.firstName, "John") + Assert.assertEquals(retrievedPerson.secondName, "Doe") + } + + @Test + fun updatePerson() = runBlocking { + val person = People( + firstName = "John", + secondName = "Doe", + place = "Park", + time = "2023-06-13 10:00", + note = "Met at the park", + gender = "Male", + avatar = 1 + ) + + peopleDao.insert(person) + + val retrievedPerson = peopleDao.getAll()[0] + retrievedPerson.firstName = "Jane" + peopleDao.update(retrievedPerson) + + val updatedPerson = peopleDao.getAll()[0] + Assert.assertEquals(updatedPerson.firstName, "Jane") + } + + @Test + fun deletePerson() = runBlocking { + val person = People( + firstName = "John", + secondName = "Doe", + place = "Park", + time = "2023-06-13 10:00", + note = "Met at the park", + gender = "Male", + avatar = 1 + ) + + peopleDao.insert(person) + + val retrievedPerson = peopleDao.getAll()[0] + peopleDao.removePerson(retrievedPerson.id) + + val people = peopleDao.getAll() + Assert.assertTrue(people.isEmpty()) + } + + @Test + fun clearAllPeople() = runBlocking { + val person1 = People( + firstName = "John", + secondName = "Doe", + place = "Park", + time = "2023-06-13 10:00", + note = "Met at the park", + gender = "Male", + avatar = 1 + ) + + val person2 = People( + firstName = "Jane", + secondName = "Doe", + place = "Mall", + time = "2023-06-13 12:00", + note = "Met at the mall", + gender = "Female", + avatar = 2 + ) + + peopleDao.insertAll(person1, person2) + + peopleDao.clear() + val people = peopleDao.getAll() + Assert.assertTrue(people.isEmpty()) + } +} diff --git a/app/src/main/java/com/example/rememberme/RememberMeApp.kt b/app/src/main/java/com/example/rememberme/RememberMeApp.kt index 387e35a..91e4283 100644 --- a/app/src/main/java/com/example/rememberme/RememberMeApp.kt +++ b/app/src/main/java/com/example/rememberme/RememberMeApp.kt @@ -2,21 +2,13 @@ package com.example.rememberme import android.content.res.Configuration.UI_MODE_NIGHT_YES import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.FloatingActionButton -import androidx.compose.material3.Icon -import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import com.example.rememberme.presentation.people.PeopleScreen +import com.example.rememberme.presentation.navgraph.NavGraph +import com.example.rememberme.presentation.navgraph.Routes import com.example.rememberme.ui.theme.RememberMeTheme @OptIn(ExperimentalMaterial3Api::class) @@ -26,29 +18,7 @@ fun RememberMeApp() { Surface( modifier = Modifier.fillMaxSize(), ) { - Scaffold( - topBar = { - TopAppBar( - title = { - Text(stringResource(id = R.string.app_name)) - }, - ) - }, - floatingActionButton = { - FloatingActionButton( - onClick = { - - } - ) { - Icon( - imageVector = Icons.Default.Add, - contentDescription = "Add a new Person" - ) - } - } - ) { - PeopleScreen(modifier = Modifier.padding(it)) - } + NavGraph(startDestination = Routes.PeopleNavigation.route) } } } diff --git a/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt b/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt index 4d75419..6d1bf81 100644 --- a/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt +++ b/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt @@ -6,6 +6,7 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.navigation import com.example.rememberme.presentation.onboarding.OnBoardingScreen +import com.example.rememberme.presentation.people.PeopleScreen @Composable fun NavGraph( @@ -17,7 +18,7 @@ fun NavGraph( startDestination = startDestination ){ navigation( - route = Routes.OnBoardingScreen.route, + route = Routes.AppStartNavigation.route, startDestination = Routes.OnBoardingScreen.route, ){ composable( @@ -27,6 +28,26 @@ fun NavGraph( ) } } + navigation( + route = Routes.PeopleNavigation.route, + startDestination = Routes.PeopleListScreen.route, + ){ + composable( + route = Routes.PeopleListScreen.route + ){ + PeopleScreen() + } + composable( + route = Routes.AddPersonScreen.route, + ){ + //TODO add person screen + } + composable( + route = Routes.PersonDetailsScreen.route + ){ + //TODO person details screen + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/presentation/people/PeopleListScreen.kt b/app/src/main/java/com/example/rememberme/presentation/people/PeopleListScreen.kt index 83667d6..9eb48cf 100644 --- a/app/src/main/java/com/example/rememberme/presentation/people/PeopleListScreen.kt +++ b/app/src/main/java/com/example/rememberme/presentation/people/PeopleListScreen.kt @@ -2,11 +2,22 @@ package com.example.rememberme.presentation.people import android.content.res.Configuration import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import com.example.rememberme.R import com.example.rememberme.data.FakeDataSource import com.example.rememberme.presentation.composable.PeopleListItem import com.example.rememberme.ui.theme.RememberMeTheme @@ -19,19 +30,41 @@ fun PeopleScreen( PeopleScreenContent(modifier) } +@OptIn(ExperimentalMaterial3Api::class) @Composable fun PeopleScreenContent(modifier: Modifier = Modifier) { val peopleList = remember { FakeDataSource.getPeopleList() } - LazyColumn( - modifier = modifier - .fillMaxSize() - ) { - items(count = peopleList.size) { index -> - PeopleListItem(peopleList[index], {}) + Scaffold( + topBar = { + TopAppBar( + title = { + Text(stringResource(id = R.string.app_name)) + }, + ) + }, + floatingActionButton = { + FloatingActionButton( + onClick = { + + } + ) { + Icon( + imageVector = Icons.Default.Add, + contentDescription = "Add a new Person" + ) + } + } + ){ + LazyColumn( + modifier = modifier.padding(it) + .fillMaxSize() + ) { + items(count = peopleList.size) { index -> + PeopleListItem(peopleList[index], {}) + } } } - } @Preview(showBackground = true) diff --git a/build.gradle.kts b/build.gradle.kts index f74b04b..130391d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,4 +2,7 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.jetbrains.kotlin.android) apply false + alias(libs.plugins.compose.compiler) apply false + id("com.google.devtools.ksp") version "2.0.0-1.0.22" apply false + } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 83bf67f..6f4a2de 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,26 +1,35 @@ [versions] -agp = "8.4.1" +agp = "8.4.2" coreSplashscreen = "1.0.1" -kotlin = "1.9.0" +coreTesting = "2.2.0" +coreTestingVersion = "1.1.1" +kotlin = "2.0.0" coreKtx = "1.13.1" junit = "4.13.2" junitVersion = "1.1.5" espressoCore = "3.5.1" -lifecycleRuntimeKtx = "2.8.1" +lifecycleRuntimeKtx = "2.8.2" activityCompose = "1.9.0" -composeBom = "2023.08.00" +composeBom = "2024.06.00" navigationCompose = "2.7.7" roomRuntime = "2.6.1" -uiTextGoogleFonts = "1.6.7" +truth = "1.4.2" +uiTextGoogleFonts = "1.6.8" +junitKtx = "1.1.5" + + [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" } +androidx-core-testing = { module = "androidx.arch.core:core-testing", version.ref = "coreTesting" } androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" } androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" } androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomRuntime" } androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomRuntime" } +androidx-room-room-testing = { module = "androidx.room:room-testing", version.ref = "roomRuntime" } androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" } +core-testing = { module = "android.arch.core:core-testing", version.ref = "coreTestingVersion" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } @@ -35,8 +44,11 @@ androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-man androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-ui-text-google-fonts = { group = "androidx.compose.ui", name = "ui-text-google-fonts", version.ref = "uiTextGoogleFonts" } +androidx-junit-ktx = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "junitKtx" } +truth = { module = "com.google.truth:truth", version.ref = "truth" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } From a406b5ff18291903e78c82b47bda35da0c6171b4 Mon Sep 17 00:00:00 2001 From: omer358 Date: Thu, 13 Jun 2024 21:26:14 +0400 Subject: [PATCH 07/18] testing navigation --- app/build.gradle.kts | 1 + .../{ => data}/PeopleDatabaseTest.kt | 2 +- .../presentation/navgraph/NavGraphTest.kt | 37 +++++++++++++++++++ .../presentation/navgraph/NavGraph.kt | 6 ++- .../presentation/people/PeopleListScreen.kt | 2 + gradle/libs.versions.toml | 1 + 6 files changed, 46 insertions(+), 3 deletions(-) rename app/src/androidTest/java/com/example/rememberme/{ => data}/PeopleDatabaseTest.kt (98%) create mode 100644 app/src/androidTest/java/com/example/rememberme/presentation/navgraph/NavGraphTest.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a4323a7..1864821 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -97,6 +97,7 @@ dependencies { // Navigation Compose implementation(libs.androidx.navigation.compose) + androidTestImplementation(libs.androidx.navigation.testing) } \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/rememberme/PeopleDatabaseTest.kt b/app/src/androidTest/java/com/example/rememberme/data/PeopleDatabaseTest.kt similarity index 98% rename from app/src/androidTest/java/com/example/rememberme/PeopleDatabaseTest.kt rename to app/src/androidTest/java/com/example/rememberme/data/PeopleDatabaseTest.kt index 1947c63..f83f7aa 100644 --- a/app/src/androidTest/java/com/example/rememberme/PeopleDatabaseTest.kt +++ b/app/src/androidTest/java/com/example/rememberme/data/PeopleDatabaseTest.kt @@ -1,4 +1,4 @@ -package com.example.rememberme +package com.example.rememberme.data import androidx.room.Room import androidx.test.core.app.ApplicationProvider diff --git a/app/src/androidTest/java/com/example/rememberme/presentation/navgraph/NavGraphTest.kt b/app/src/androidTest/java/com/example/rememberme/presentation/navgraph/NavGraphTest.kt new file mode 100644 index 0000000..6fba3b0 --- /dev/null +++ b/app/src/androidTest/java/com/example/rememberme/presentation/navgraph/NavGraphTest.kt @@ -0,0 +1,37 @@ +package com.example.rememberme + +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.test.ext.junit.runners.AndroidJUnit4 +import com.example.rememberme.presentation.navgraph.NavGraph +import com.example.rememberme.presentation.navgraph.Routes +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class NavGraphTest { + + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun testNavigationToOnBoardingScreen() { + composeTestRule.setContent { + NavGraph(startDestination = Routes.AppStartNavigation.route) + } + + composeTestRule.onNodeWithText("OnBoarding Screen").assertIsDisplayed() + } + + @Test + fun testNavigationToPeopleScreen() { + composeTestRule.setContent { + NavGraph(startDestination = Routes.PeopleNavigation.route) + } + + composeTestRule.onNodeWithTag("PeopleListScreen").assertIsDisplayed() + } +} diff --git a/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt b/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt index 6d1bf81..f24e3fb 100644 --- a/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt +++ b/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt @@ -1,6 +1,7 @@ package com.example.rememberme.presentation.navgraph import androidx.compose.runtime.Composable +import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController @@ -10,9 +11,10 @@ import com.example.rememberme.presentation.people.PeopleScreen @Composable fun NavGraph( - startDestination: String + startDestination: String, + navController: NavHostController = rememberNavController() ){ - val navController = rememberNavController() + NavHost( navController = navController, startDestination = startDestination diff --git a/app/src/main/java/com/example/rememberme/presentation/people/PeopleListScreen.kt b/app/src/main/java/com/example/rememberme/presentation/people/PeopleListScreen.kt index 9eb48cf..d639e7e 100644 --- a/app/src/main/java/com/example/rememberme/presentation/people/PeopleListScreen.kt +++ b/app/src/main/java/com/example/rememberme/presentation/people/PeopleListScreen.kt @@ -15,6 +15,7 @@ import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import com.example.rememberme.R @@ -59,6 +60,7 @@ fun PeopleScreenContent(modifier: Modifier = Modifier) { LazyColumn( modifier = modifier.padding(it) .fillMaxSize() + .testTag("PeopleListScreen") ) { items(count = peopleList.size) { index -> PeopleListItem(peopleList[index], {}) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6f4a2de..cd03848 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -25,6 +25,7 @@ androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", versi androidx-core-testing = { module = "androidx.arch.core:core-testing", version.ref = "coreTesting" } androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" } androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" } +androidx-navigation-testing = { module = "androidx.navigation:navigation-testing", version.ref = "navigationCompose" } androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomRuntime" } androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomRuntime" } androidx-room-room-testing = { module = "androidx.room:room-testing", version.ref = "roomRuntime" } From f65f4501c2373d822fad923a1c95abb78f083128 Mon Sep 17 00:00:00 2001 From: omer358 Date: Mon, 17 Jun 2024 05:46:28 +0400 Subject: [PATCH 08/18] Impl Hilt DI --- .idea/androidTestResultsUserPreferences.xml | 13 +++++++++++++ .idea/deploymentTargetSelector.xml | 3 +++ app/build.gradle.kts | 8 +++++++- app/src/main/AndroidManifest.xml | 1 + .../java/com/example/rememberme/MainActivity.kt | 2 ++ .../com/example/rememberme/RememberMeApplication.kt | 8 ++++++++ .../presentation/details/PersonDetailsViewModel.kt | 10 ++++++++++ build.gradle.kts | 2 ++ gradle/libs.versions.toml | 5 +++++ 9 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/example/rememberme/RememberMeApplication.kt create mode 100644 app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsViewModel.kt diff --git a/.idea/androidTestResultsUserPreferences.xml b/.idea/androidTestResultsUserPreferences.xml index 1413b29..e4cad07 100644 --- a/.idea/androidTestResultsUserPreferences.xml +++ b/.idea/androidTestResultsUserPreferences.xml @@ -17,6 +17,19 @@ + + + + + + + diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index ca96088..b88c95b 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -22,6 +22,9 @@ + + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1864821..2c08081 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -2,8 +2,10 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.jetbrains.kotlin.android) alias(libs.plugins.compose.compiler) - id("com.google.devtools.ksp") + id("com.google.dagger.hilt.android") + id("kotlin-kapt") +// kapt("groupId:artifactId:version") } @@ -99,5 +101,9 @@ dependencies { implementation(libs.androidx.navigation.compose) androidTestImplementation(libs.androidx.navigation.testing) + // Hilt + implementation(libs.hilt.android) + implementation(libs.androidx.hilt.navigation.compose) + kapt(libs.hilt.compiler) } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c422d92..76276a9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools"> Date: Mon, 17 Jun 2024 05:47:28 +0400 Subject: [PATCH 09/18] update data --- .../example/rememberme/data/FakeDataSource.kt | 168 ++++++++++++++++-- .../rememberme/data/database/People.kt | 7 +- 2 files changed, 157 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/example/rememberme/data/FakeDataSource.kt b/app/src/main/java/com/example/rememberme/data/FakeDataSource.kt index 06b617d..335ffc4 100644 --- a/app/src/main/java/com/example/rememberme/data/FakeDataSource.kt +++ b/app/src/main/java/com/example/rememberme/data/FakeDataSource.kt @@ -6,35 +6,169 @@ import com.example.rememberme.data.database.People object FakeDataSource { fun getPeopleList(): List { val firstNames = listOf( - "John", "Jane", "Michael", "Emily", "Robert", "Linda", "David", "Sarah", "James", "Jessica", - "William", "Amanda", "Joseph", "Ashley", "Charles", "Melissa", "Thomas", "Stephanie", "Daniel", "Laura", - "Matthew", "Rachel", "Andrew", "Megan", "Joshua", "Jennifer", "Anthony", "Samantha", "Mark", "Elizabeth" + "John", + "Jane", + "Michael", + "Emily", + "Robert", + "Linda", + "David", + "Sarah", + "James", + "Jessica", + "William", + "Amanda", + "Joseph", + "Ashley", + "Charles", + "Melissa", + "Thomas", + "Stephanie", + "Daniel", + "Laura", + "Matthew", + "Rachel", + "Andrew", + "Megan", + "Joshua", + "Jennifer", + "Anthony", + "Samantha", + "Mark", + "Elizabeth" ) val lastNames = listOf( - "Doe", "Smith", "Johnson", "Davis", "Brown", "Taylor", "Wilson", "Moore", "Jackson", "White", - "Harris", "Martin", "Thompson", "Garcia", "Martinez", "Robinson", "Clark", "Rodriguez", "Lewis", "Lee", - "Walker", "Hall", "Allen", "Young", "King", "Wright", "Scott", "Torres", "Nguyen", "Hill" + "Doe", + "Smith", + "Johnson", + "Davis", + "Brown", + "Taylor", + "Wilson", + "Moore", + "Jackson", + "White", + "Harris", + "Martin", + "Thompson", + "Garcia", + "Martinez", + "Robinson", + "Clark", + "Rodriguez", + "Lewis", + "Lee", + "Walker", + "Hall", + "Allen", + "Young", + "King", + "Wright", + "Scott", + "Torres", + "Nguyen", + "Hill" ) val places = listOf( - "Central Park", "Starbucks", "Office", "Gym", "Conference Hall", "Library", "Mall", "Restaurant", "Beach", "Museum", - "Cinema", "Zoo", "Amusement Park", "Aquarium", "Sports Arena", "Theater", "Cafe", "Bookstore", "Airport", "Train Station", - "Bus Stop", "Hotel", "Hospital", "Clinic", "School", "University", "Park", "Playground", "Concert Hall", "Night Club" + "Central Park", + "Starbucks", + "Office", + "Gym", + "Conference Hall", + "Library", + "Mall", + "Restaurant", + "Beach", + "Museum", + "Cinema", + "Zoo", + "Amusement Park", + "Aquarium", + "Sports Arena", + "Theater", + "Cafe", + "Bookstore", + "Airport", + "Train Station", + "Bus Stop", + "Hotel", + "Hospital", + "Clinic", + "School", + "University", + "Park", + "Playground", + "Concert Hall", + "Night Club" ) val times = listOf( - "12:00 PM", "9:00 AM", "10:00 AM", "6:00 PM", "3:00 PM", "2:00 PM", "11:00 AM", "1:00 PM", "4:00 PM", "5:00 PM", - "7:00 PM", "8:00 PM", "6:30 PM", "9:30 AM", "10:30 AM", "11:30 AM", "12:30 PM", "1:30 PM", "2:30 PM", "3:30 PM", - "4:30 PM", "5:30 PM", "6:00 AM", "7:00 AM", "8:00 AM", "8:30 AM", "7:30 AM", "6:00 AM", "9:00 PM", "10:00 PM" + "12:00 PM", + "9:00 AM", + "10:00 AM", + "6:00 PM", + "3:00 PM", + "2:00 PM", + "11:00 AM", + "1:00 PM", + "4:00 PM", + "5:00 PM", + "7:00 PM", + "8:00 PM", + "6:30 PM", + "9:30 AM", + "10:30 AM", + "11:30 AM", + "12:30 PM", + "1:30 PM", + "2:30 PM", + "3:30 PM", + "4:30 PM", + "5:30 PM", + "6:00 AM", + "7:00 AM", + "8:00 AM", + "8:30 AM", + "7:30 AM", + "6:00 AM", + "9:00 PM", + "10:00 PM" ) val notes = listOf( - "Met during lunch break", "Had a coffee together", "Discussed project details", "Met during workout", "Attended a seminar", "Studied together", - "Shopped for clothes", "Had dinner together", "Relaxed on the beach", "Visited an exhibition", "Watched a movie", "Saw animals", "Enjoyed rides", - "Saw marine life", "Watched a game", "Watched a play", "Had a snack", "Bought books", "Caught a flight", "Caught a train", "Waited for the bus", - "Stayed overnight", "Had a checkup", "Received treatment", "Attended classes", "Studied together", "Walked in the park", "Played together", - "Attended a concert", "Danced at the club" + "Met during lunch break", + "Had a coffee together", + "Discussed project details", + "Met during workout", + "Attended a seminar", + "Studied together", + "Shopped for clothes", + "Had dinner together", + "Relaxed on the beach", + "Visited an exhibition", + "Watched a movie", + "Saw animals", + "Enjoyed rides", + "Saw marine life", + "Watched a game", + "Watched a play", + "Had a snack", + "Bought books", + "Caught a flight", + "Caught a train", + "Waited for the bus", + "Stayed overnight", + "Had a checkup", + "Received treatment", + "Attended classes", + "Studied together", + "Walked in the park", + "Played together", + "Attended a concert", + "Danced at the club" ) return List(30) { index -> People( + id = index.toLong(), firstName = firstNames[index % firstNames.size], secondName = lastNames[index % lastNames.size], place = places[index % places.size], diff --git a/app/src/main/java/com/example/rememberme/data/database/People.kt b/app/src/main/java/com/example/rememberme/data/database/People.kt index f0a9615..4604562 100644 --- a/app/src/main/java/com/example/rememberme/data/database/People.kt +++ b/app/src/main/java/com/example/rememberme/data/database/People.kt @@ -15,4 +15,9 @@ data class People ( @ColumnInfo(name ="note")var note:String? = null, @ColumnInfo(name = "registration_time")val registrationTime: Long = System.currentTimeMillis(), var gender:String, - var avatar: Int) \ No newline at end of file + var avatar: Int){ + override fun toString(): String { + return "$firstName $secondName" + } + +} \ No newline at end of file From 199ca86697d50175bec82243dcd277f24c825092 Mon Sep 17 00:00:00 2001 From: omer358 Date: Mon, 17 Jun 2024 05:48:14 +0400 Subject: [PATCH 10/18] impl Navigation Arguments --- .../presentation/composable/PeopleListItem.kt | 6 +- .../presentation/details/PersonDetails.kt | 160 ++++++++++++++++++ .../presentation/navgraph/NavGraph.kt | 43 +++-- .../presentation/people/PeopleViewModel.kt | 4 - .../PeopleListScreen.kt | 52 +++--- .../peopleList/PeopleViewModel.kt | 4 + 6 files changed, 227 insertions(+), 42 deletions(-) create mode 100644 app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt delete mode 100644 app/src/main/java/com/example/rememberme/presentation/people/PeopleViewModel.kt rename app/src/main/java/com/example/rememberme/presentation/{people => peopleList}/PeopleListScreen.kt (62%) create mode 100644 app/src/main/java/com/example/rememberme/presentation/peopleList/PeopleViewModel.kt diff --git a/app/src/main/java/com/example/rememberme/presentation/composable/PeopleListItem.kt b/app/src/main/java/com/example/rememberme/presentation/composable/PeopleListItem.kt index 077330c..a52d671 100644 --- a/app/src/main/java/com/example/rememberme/presentation/composable/PeopleListItem.kt +++ b/app/src/main/java/com/example/rememberme/presentation/composable/PeopleListItem.kt @@ -21,7 +21,7 @@ import com.example.rememberme.ui.theme.RememberMeTheme @Composable fun PeopleListItem( people: People, - onClickListener: () -> Unit, + onClickListener: (Long) -> Unit, modifier: Modifier = Modifier ) { ListItem( @@ -29,11 +29,11 @@ fun PeopleListItem( .padding(horizontal = 8.dp, vertical = 4.dp) .clip(shape = RoundedCornerShape(8.dp)) .clickable { - onClickListener() + onClickListener(people.id) }, leadingContent = { Image( - painter = painterResource(id = R.drawable.ic_f1), + painter = painterResource(people.avatar), modifier = Modifier.size(64.dp), contentDescription = null ) diff --git a/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt new file mode 100644 index 0000000..a958de1 --- /dev/null +++ b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt @@ -0,0 +1,160 @@ +package com.example.rememberme.presentation.details + +import android.content.res.Configuration.UI_MODE_NIGHT_YES +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Card +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +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.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import com.example.rememberme.R +import com.example.rememberme.data.database.People +import com.example.rememberme.ui.theme.RememberMeTheme + +@Composable +fun PersonDetailsScreen( + person: People, + viewModel: PersonDetailsViewModel = hiltViewModel() +) { + PersonDetailsContent( + person = person, + ) +} + +@Composable +fun PersonDetailsContent( + person: People, + modifier: Modifier = Modifier +) { + val scrollState = rememberScrollState() + Column( + verticalArrangement = Arrangement.Top, + modifier = modifier + .fillMaxSize() + .verticalScroll(scrollState), + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .height(250.dp) + ) { + Box( + modifier = Modifier + .clip( + RoundedCornerShape(bottomStart = 8.dp, bottomEnd = 8.dp) + ) + .background(MaterialTheme.colorScheme.primary) + .fillMaxWidth() + .height(200.dp) + + ) + Box( + modifier = Modifier + .padding(start = 8.dp) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.inversePrimary) + .size(120.dp) + .align(Alignment.BottomStart) + ) { + Image( + painter = painterResource(id = person.avatar), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier + .size(120.dp) + ) + } + } + Column( + modifier = Modifier.padding(horizontal = 8.dp) + ) { + Spacer(modifier = Modifier.height(16.dp)) + Text(text = person.toString(), style = MaterialTheme.typography.headlineLarge) + Spacer(modifier = Modifier.height(16.dp)) + Card( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp), + shape = RoundedCornerShape(8.dp) + ) { + Column( + modifier = Modifier.padding(8.dp) + ) { + Text(text = "Time", style = MaterialTheme.typography.titleLarge) + Text(text = person.time, style = MaterialTheme.typography.bodyLarge) + Spacer(modifier = Modifier.height(8.dp)) + Text(text = "Place", style = MaterialTheme.typography.titleLarge) + Text(text = person.place, style = MaterialTheme.typography.bodyLarge) + } + } + Spacer(modifier = Modifier.height(16.dp)) + Card( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp), + shape = RoundedCornerShape(8.dp) + ) { + Column( + modifier = Modifier.padding(8.dp) + ) { + Text(text = "Gender", style = MaterialTheme.typography.titleLarge) + Text(text = person.gender, style = MaterialTheme.typography.bodyLarge) + } + } + Spacer(modifier = Modifier.height(16.dp)) + Card( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp), + shape = RoundedCornerShape(8.dp) + ) { + Column( + modifier = Modifier.padding(8.dp) + ) { + Text(text = "Additional Notes:", style = MaterialTheme.typography.titleLarge) + person.note?.let { Text(text = it, style = MaterialTheme.typography.bodyLarge) } + } + } + } + } + +} + +@Preview(showBackground = true) +@Preview(uiMode = UI_MODE_NIGHT_YES) +@Composable +fun PersonDetailsContentPreview() { + RememberMeTheme { + PersonDetailsContent( + People( + firstName = "William", + secondName = "Doe", + avatar = R.drawable.ic_m3, + time = "10:00 AM", + place = "Home", + gender = "Male" + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt b/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt index f24e3fb..a47ebbf 100644 --- a/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt +++ b/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt @@ -1,28 +1,35 @@ package com.example.rememberme.presentation.navgraph import androidx.compose.runtime.Composable +import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController +import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import androidx.navigation.navArgument import androidx.navigation.navigation +import com.example.rememberme.R +import com.example.rememberme.data.database.People +import com.example.rememberme.presentation.details.PersonDetailsScreen +import com.example.rememberme.presentation.details.PersonDetailsViewModel import com.example.rememberme.presentation.onboarding.OnBoardingScreen -import com.example.rememberme.presentation.people.PeopleScreen +import com.example.rememberme.presentation.peopleList.PeopleScreen @Composable fun NavGraph( startDestination: String, navController: NavHostController = rememberNavController() -){ +) { NavHost( navController = navController, startDestination = startDestination - ){ + ) { navigation( route = Routes.AppStartNavigation.route, startDestination = Routes.OnBoardingScreen.route, - ){ + ) { composable( route = Routes.OnBoardingScreen.route ) { @@ -33,21 +40,35 @@ fun NavGraph( navigation( route = Routes.PeopleNavigation.route, startDestination = Routes.PeopleListScreen.route, - ){ + ) { composable( route = Routes.PeopleListScreen.route - ){ - PeopleScreen() + ) { + PeopleScreen(navigateToDetailScreen = { personId -> + navController.navigate(Routes.PersonDetailsScreen.route + "/$personId") + }) } composable( route = Routes.AddPersonScreen.route, - ){ + ) { //TODO add person screen } composable( - route = Routes.PersonDetailsScreen.route - ){ - //TODO person details screen + route = Routes.PersonDetailsScreen.route + "/{personId}", + arguments = listOf(navArgument("personId") { type = NavType.LongType }) + ) { + val personId = it.arguments?.getLong("personId") + val viewModel: PersonDetailsViewModel = hiltViewModel() + PersonDetailsScreen( + People( + firstName = "William", + secondName = "Doe", + avatar = R.drawable.ic_m3, + time = "10:00 AM", + place = "Home", + gender = "Male" + ) + ) } } } diff --git a/app/src/main/java/com/example/rememberme/presentation/people/PeopleViewModel.kt b/app/src/main/java/com/example/rememberme/presentation/people/PeopleViewModel.kt deleted file mode 100644 index e88ebcd..0000000 --- a/app/src/main/java/com/example/rememberme/presentation/people/PeopleViewModel.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.rememberme.presentation.people - -class PeopleViewModel { -} \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/presentation/people/PeopleListScreen.kt b/app/src/main/java/com/example/rememberme/presentation/peopleList/PeopleListScreen.kt similarity index 62% rename from app/src/main/java/com/example/rememberme/presentation/people/PeopleListScreen.kt rename to app/src/main/java/com/example/rememberme/presentation/peopleList/PeopleListScreen.kt index d639e7e..a142159 100644 --- a/app/src/main/java/com/example/rememberme/presentation/people/PeopleListScreen.kt +++ b/app/src/main/java/com/example/rememberme/presentation/peopleList/PeopleListScreen.kt @@ -1,4 +1,4 @@ -package com.example.rememberme.presentation.people +package com.example.rememberme.presentation.peopleList import android.content.res.Configuration import androidx.compose.foundation.layout.fillMaxSize @@ -26,44 +26,48 @@ import com.example.rememberme.ui.theme.RememberMeTheme @Composable fun PeopleScreen( modifier: Modifier = Modifier, - viewModel: PeopleViewModel = PeopleViewModel() + viewModel: PeopleViewModel = PeopleViewModel(), + navigateToDetailScreen: (Long) -> Unit ) { - PeopleScreenContent(modifier) + PeopleScreenContent(modifier, navigateToDetailScreen) } @OptIn(ExperimentalMaterial3Api::class) @Composable -fun PeopleScreenContent(modifier: Modifier = Modifier) { +fun PeopleScreenContent(modifier: Modifier = Modifier, navigateToDetailScreen: (Long) -> Unit) { val peopleList = remember { FakeDataSource.getPeopleList() } Scaffold( - topBar = { - TopAppBar( - title = { - Text(stringResource(id = R.string.app_name)) - }, - ) - }, - floatingActionButton = { - FloatingActionButton( - onClick = { + topBar = { + TopAppBar( + title = { + Text(stringResource(id = R.string.app_name)) + }, + ) + }, + floatingActionButton = { + FloatingActionButton( + onClick = { + } + ) { + Icon( + imageVector = Icons.Default.Add, + contentDescription = "Add a new Person" + ) } - ) { - Icon( - imageVector = Icons.Default.Add, - contentDescription = "Add a new Person" - ) } - } - ){ + ) { it -> LazyColumn( - modifier = modifier.padding(it) + modifier = modifier + .padding(it) .fillMaxSize() .testTag("PeopleListScreen") ) { items(count = peopleList.size) { index -> - PeopleListItem(peopleList[index], {}) + PeopleListItem(peopleList[index], { personId -> + navigateToDetailScreen(personId) + }) } } } @@ -74,7 +78,7 @@ fun PeopleScreenContent(modifier: Modifier = Modifier) { @Composable fun PeopleScreenContentPreview() { RememberMeTheme { - PeopleScreenContent() + PeopleScreenContent(navigateToDetailScreen = {}) } } diff --git a/app/src/main/java/com/example/rememberme/presentation/peopleList/PeopleViewModel.kt b/app/src/main/java/com/example/rememberme/presentation/peopleList/PeopleViewModel.kt new file mode 100644 index 0000000..ec9c593 --- /dev/null +++ b/app/src/main/java/com/example/rememberme/presentation/peopleList/PeopleViewModel.kt @@ -0,0 +1,4 @@ +package com.example.rememberme.presentation.peopleList + +class PeopleViewModel { +} \ No newline at end of file From e83fac2b3eae9309438a35416a4c4d3abcc9c8c0 Mon Sep 17 00:00:00 2001 From: omer358 Date: Mon, 17 Jun 2024 09:37:19 +0400 Subject: [PATCH 11/18] impl insert and fetch from the data base --- app/build.gradle.kts | 4 +- .../rememberme/data/PeopleDatabaseTest.kt | 6 +- .../com/example/rememberme/MainActivity.kt | 8 ++ .../example/rememberme/data/FakeDataSource.kt | 2 +- .../rememberme/data/PeopleRepositoryImpl.kt | 38 +++++++++ .../data/database/PeopleDatabase.kt | 79 ------------------- .../data/{database => local}/PeopleDao.kt | 7 +- .../rememberme/data/local/PeopleDatabase.kt | 11 +++ .../com/example/rememberme/di/AppModule.kt | 59 ++++++++++++++ .../{data/database => domain/model}/People.kt | 2 +- .../domain/repository/PeopleRepository.kt | 14 ++++ .../domain/usecases/people/GetAllPeople.kt | 14 ++++ .../domain/usecases/people/PeopleUseCases.kt | 5 ++ .../presentation/composable/PeopleListItem.kt | 2 +- .../presentation/details/PersonDetails.kt | 2 +- .../presentation/navgraph/NavGraph.kt | 2 +- .../peopleList/PeopleListScreen.kt | 28 +++++-- .../peopleList/PeopleViewModel.kt | 11 ++- .../com/example/rememberme/utils/Constants.kt | 5 ++ build.gradle.kts | 14 +++- gradle/libs.versions.toml | 6 +- 21 files changed, 215 insertions(+), 104 deletions(-) create mode 100644 app/src/main/java/com/example/rememberme/data/PeopleRepositoryImpl.kt delete mode 100644 app/src/main/java/com/example/rememberme/data/database/PeopleDatabase.kt rename app/src/main/java/com/example/rememberme/data/{database => local}/PeopleDao.kt (82%) create mode 100644 app/src/main/java/com/example/rememberme/data/local/PeopleDatabase.kt create mode 100644 app/src/main/java/com/example/rememberme/di/AppModule.kt rename app/src/main/java/com/example/rememberme/{data/database => domain/model}/People.kt (94%) create mode 100644 app/src/main/java/com/example/rememberme/domain/repository/PeopleRepository.kt create mode 100644 app/src/main/java/com/example/rememberme/domain/usecases/people/GetAllPeople.kt create mode 100644 app/src/main/java/com/example/rememberme/domain/usecases/people/PeopleUseCases.kt create mode 100644 app/src/main/java/com/example/rememberme/utils/Constants.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2c08081..150423b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -79,10 +79,9 @@ dependencies { implementation(libs.androidx.room.runtime) - annotationProcessor(libs.androidx.room.compiler) + ksp(libs.androidx.room.compiler) implementation(libs.androidx.room.ktx) testImplementation(libs.androidx.room.room.testing) - ksp(libs.androidx.room.compiler) @@ -105,5 +104,4 @@ dependencies { implementation(libs.hilt.android) implementation(libs.androidx.hilt.navigation.compose) kapt(libs.hilt.compiler) - } \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/rememberme/data/PeopleDatabaseTest.kt b/app/src/androidTest/java/com/example/rememberme/data/PeopleDatabaseTest.kt index f83f7aa..25a3ba8 100644 --- a/app/src/androidTest/java/com/example/rememberme/data/PeopleDatabaseTest.kt +++ b/app/src/androidTest/java/com/example/rememberme/data/PeopleDatabaseTest.kt @@ -4,8 +4,8 @@ import androidx.room.Room import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.example.rememberme.data.database.People -import com.example.rememberme.data.database.PeopleDatabase +import com.example.rememberme.data.local.PeopleDatabase +import com.example.rememberme.domain.model.People import com.example.remindme.database.PeopleDao import kotlinx.coroutines.runBlocking import org.junit.After @@ -122,7 +122,7 @@ class PeopleDatabaseTest { avatar = 2 ) - peopleDao.insertAll(person1, person2) + peopleDao.insertAll(listOf(person1, person2)) peopleDao.clear() val people = peopleDao.getAll() diff --git a/app/src/main/java/com/example/rememberme/MainActivity.kt b/app/src/main/java/com/example/rememberme/MainActivity.kt index 5dea26a..625d7ba 100644 --- a/app/src/main/java/com/example/rememberme/MainActivity.kt +++ b/app/src/main/java/com/example/rememberme/MainActivity.kt @@ -9,10 +9,18 @@ import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class MainActivity : ComponentActivity() { +// @Inject +// lateinit var peopleDao: PeopleDao override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() installSplashScreen() + +// lifecycleScope.launch { +// withContext(Dispatchers.IO){ +// peopleDao.insertAll(FakeDataSource.getPeopleList()) +// } +// } setContent { RememberMeApp() } diff --git a/app/src/main/java/com/example/rememberme/data/FakeDataSource.kt b/app/src/main/java/com/example/rememberme/data/FakeDataSource.kt index 335ffc4..5772425 100644 --- a/app/src/main/java/com/example/rememberme/data/FakeDataSource.kt +++ b/app/src/main/java/com/example/rememberme/data/FakeDataSource.kt @@ -1,7 +1,7 @@ package com.example.rememberme.data import com.example.rememberme.R -import com.example.rememberme.data.database.People +import com.example.rememberme.domain.model.People object FakeDataSource { fun getPeopleList(): List { diff --git a/app/src/main/java/com/example/rememberme/data/PeopleRepositoryImpl.kt b/app/src/main/java/com/example/rememberme/data/PeopleRepositoryImpl.kt new file mode 100644 index 0000000..063747d --- /dev/null +++ b/app/src/main/java/com/example/rememberme/data/PeopleRepositoryImpl.kt @@ -0,0 +1,38 @@ +package com.example.rememberme.data + +import com.example.rememberme.domain.model.People +import com.example.rememberme.domain.repository.PeopleRepository +import com.example.remindme.database.PeopleDao +import kotlinx.coroutines.flow.Flow + +class PeopleRepositoryImpl( + private val peopleDao: PeopleDao +): PeopleRepository { + override fun getAllPeople(): Flow> { + return peopleDao.getAllPeople() + } + + override suspend fun insertPeople(people: People) { + TODO("Not yet implemented") + } + + override suspend fun deletePeople(people: People) { + TODO("Not yet implemented") + } + + override suspend fun updatePeople(people: People) { + TODO("Not yet implemented") + } + + override suspend fun deleteAllPeople() { + TODO("Not yet implemented") + } + + override suspend fun getPeopleById(id: Int): People? { + TODO("Not yet implemented") + } + + override suspend fun getPeopleByName(name: String): List { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/data/database/PeopleDatabase.kt b/app/src/main/java/com/example/rememberme/data/database/PeopleDatabase.kt deleted file mode 100644 index 39d4526..0000000 --- a/app/src/main/java/com/example/rememberme/data/database/PeopleDatabase.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.example.rememberme.data.database - -import android.content.Context -import androidx.room.Database -import androidx.room.Room -import androidx.room.RoomDatabase -import com.example.remindme.database.PeopleDao - -@Database(entities = [People::class],version = 5,exportSchema = false) -abstract class PeopleDatabase : RoomDatabase(){ - - abstract val peopleDao: PeopleDao - - /** - * Define a companion object, this allows us to add functions on the SleepDatabase class. - * - * For example, clients can call `SleepDatabase.getInstance(context)` to instantiate - * a new SleepDatabase. - */ - companion object { - /** - * INSTANCE will keep a reference to any database returned via getInstance. - * - * This will help us avoid repeatedly initializing the database, which is expensive. - * - * The value of a volatile variable will never be cached, and all writes and - * reads will be done to and from the main memory. It means that changes made by one - * thread to shared data are visible to other threads. - */ - @Volatile - private var INSTANCE: PeopleDatabase? = null - - /** - * Helper function to get the database. - * - * If a database has already been retrieved, the previous database will be returned. - * Otherwise, create a new database. - * - * This function is threadsafe, and callers should cache the result for multiple database - * calls to avoid overhead. - * - * This is an example of a simple Singleton pattern that takes another Singleton as an - * argument in Kotlin. - * - * To learn more about Singleton read the wikipedia article: - * https://en.wikipedia.org/wiki/Singleton_pattern - * - * @param context The application context Singleton, used to get access to the filesystem. - */ - fun getInstance(context: Context): PeopleDatabase { - // Multiple threads can ask for the database at the same time, ensure we only initialize - // it once by using synchronized. Only one thread may enter a synchronized block at a - // time. - synchronized(this) { - // Copy the current value of INSTANCE to a local variable so Kotlin can smart cast. - // Smart cast is only available to local variables. - var instance = INSTANCE - // If instance is `null` make a new database instance. - if (instance == null) { - instance = Room.databaseBuilder( - context.applicationContext, - PeopleDatabase::class.java, - "people_database" - ) - // Wipes and rebuilds instead of migrating if no Migration object. - // Migration is not part of this lesson. You can learn more about - // migration with Room in this blog post: - // https://medium.com/androiddevelopers/understanding-migrations-with-room-f01e04b07929 - .fallbackToDestructiveMigration() - .build() - // Assign INSTANCE to the newly created database. - INSTANCE = instance - } - // Return instance; smart cast to be non-null. - return instance - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/data/database/PeopleDao.kt b/app/src/main/java/com/example/rememberme/data/local/PeopleDao.kt similarity index 82% rename from app/src/main/java/com/example/rememberme/data/database/PeopleDao.kt rename to app/src/main/java/com/example/rememberme/data/local/PeopleDao.kt index b242176..2e216fb 100644 --- a/app/src/main/java/com/example/rememberme/data/database/PeopleDao.kt +++ b/app/src/main/java/com/example/rememberme/data/local/PeopleDao.kt @@ -6,7 +6,8 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Update -import com.example.rememberme.data.database.People +import com.example.rememberme.domain.model.People +import kotlinx.coroutines.flow.Flow @Dao interface PeopleDao { @@ -15,7 +16,7 @@ interface PeopleDao { fun insert(people: People) @Insert(onConflict = OnConflictStrategy.REPLACE) - fun insertAll(vararg people: People) + fun insertAll(people: List) @Update fun update (people: People) @@ -27,7 +28,7 @@ interface PeopleDao { fun clear() @Query("SELECT * FROM people_table ORDER BY id DESC") - fun getAllPeople(): LiveData> + fun getAllPeople(): Flow> @Query("SELECT * FROM people_table ORDER BY id DESC") fun getAll(): List diff --git a/app/src/main/java/com/example/rememberme/data/local/PeopleDatabase.kt b/app/src/main/java/com/example/rememberme/data/local/PeopleDatabase.kt new file mode 100644 index 0000000..c63258c --- /dev/null +++ b/app/src/main/java/com/example/rememberme/data/local/PeopleDatabase.kt @@ -0,0 +1,11 @@ +package com.example.rememberme.data.local + +import androidx.room.Database +import androidx.room.RoomDatabase +import com.example.rememberme.domain.model.People +import com.example.remindme.database.PeopleDao + +@Database(entities = [People::class],version = 1,exportSchema = true) +abstract class PeopleDatabase : RoomDatabase(){ + abstract val peopleDao: PeopleDao +} \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/di/AppModule.kt b/app/src/main/java/com/example/rememberme/di/AppModule.kt new file mode 100644 index 0000000..609502a --- /dev/null +++ b/app/src/main/java/com/example/rememberme/di/AppModule.kt @@ -0,0 +1,59 @@ +package com.example.rememberme.di + +import android.app.Application +import androidx.room.Room +import com.example.rememberme.data.PeopleRepositoryImpl +import com.example.rememberme.data.local.PeopleDatabase +import com.example.rememberme.domain.repository.PeopleRepository +import com.example.rememberme.domain.usecases.people.GetAllPeople +import com.example.rememberme.domain.usecases.people.PeopleUseCases +import com.example.rememberme.utils.Constants.PEOPLE_DATABASE_NAME +import com.example.remindme.database.PeopleDao +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object AppModule { + + @Provides + @Singleton + fun providePeopleDatabase( + application: Application + ): PeopleDatabase { + return Room.databaseBuilder( + context = application, + klass = PeopleDatabase::class.java, + name = PEOPLE_DATABASE_NAME + ).fallbackToDestructiveMigration() + .build() + } + + @Provides + @Singleton + fun providePeopleDao( + peopleDatabase: PeopleDatabase + ) = peopleDatabase.peopleDao + + @Provides + @Singleton + fun providePeopleRepository( + peopleDao: PeopleDao + ): PeopleRepository { + return PeopleRepositoryImpl(peopleDao) + } + + @Provides + @Singleton + fun providePeopleUseCases( + peopleRepository: PeopleRepository + ): PeopleUseCases { + return PeopleUseCases( + getAllPeople = GetAllPeople(peopleRepository) + ) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/data/database/People.kt b/app/src/main/java/com/example/rememberme/domain/model/People.kt similarity index 94% rename from app/src/main/java/com/example/rememberme/data/database/People.kt rename to app/src/main/java/com/example/rememberme/domain/model/People.kt index 4604562..1f83281 100644 --- a/app/src/main/java/com/example/rememberme/data/database/People.kt +++ b/app/src/main/java/com/example/rememberme/domain/model/People.kt @@ -1,4 +1,4 @@ -package com.example.rememberme.data.database +package com.example.rememberme.domain.model import androidx.room.ColumnInfo import androidx.room.Entity diff --git a/app/src/main/java/com/example/rememberme/domain/repository/PeopleRepository.kt b/app/src/main/java/com/example/rememberme/domain/repository/PeopleRepository.kt new file mode 100644 index 0000000..cadcce6 --- /dev/null +++ b/app/src/main/java/com/example/rememberme/domain/repository/PeopleRepository.kt @@ -0,0 +1,14 @@ +package com.example.rememberme.domain.repository + +import com.example.rememberme.domain.model.People +import kotlinx.coroutines.flow.Flow + +interface PeopleRepository { + fun getAllPeople(): Flow> + suspend fun insertPeople(people: People) + suspend fun deletePeople(people: People) + suspend fun updatePeople(people: People) + suspend fun deleteAllPeople() + suspend fun getPeopleById(id: Int): People? + suspend fun getPeopleByName(name: String): List +} \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/domain/usecases/people/GetAllPeople.kt b/app/src/main/java/com/example/rememberme/domain/usecases/people/GetAllPeople.kt new file mode 100644 index 0000000..8f3317e --- /dev/null +++ b/app/src/main/java/com/example/rememberme/domain/usecases/people/GetAllPeople.kt @@ -0,0 +1,14 @@ +package com.example.rememberme.domain.usecases.people + +import com.example.rememberme.domain.model.People +import com.example.rememberme.domain.repository.PeopleRepository +import kotlinx.coroutines.flow.Flow + +class GetAllPeople( + private val peopleRepository: PeopleRepository +) { + operator fun invoke(): Flow> { + return peopleRepository.getAllPeople() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/domain/usecases/people/PeopleUseCases.kt b/app/src/main/java/com/example/rememberme/domain/usecases/people/PeopleUseCases.kt new file mode 100644 index 0000000..628d718 --- /dev/null +++ b/app/src/main/java/com/example/rememberme/domain/usecases/people/PeopleUseCases.kt @@ -0,0 +1,5 @@ +package com.example.rememberme.domain.usecases.people + +data class PeopleUseCases( + val getAllPeople: GetAllPeople, +) \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/presentation/composable/PeopleListItem.kt b/app/src/main/java/com/example/rememberme/presentation/composable/PeopleListItem.kt index a52d671..33643c8 100644 --- a/app/src/main/java/com/example/rememberme/presentation/composable/PeopleListItem.kt +++ b/app/src/main/java/com/example/rememberme/presentation/composable/PeopleListItem.kt @@ -15,7 +15,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.example.rememberme.R -import com.example.rememberme.data.database.People +import com.example.rememberme.domain.model.People import com.example.rememberme.ui.theme.RememberMeTheme @Composable diff --git a/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt index a958de1..b8a1469 100644 --- a/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt +++ b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt @@ -29,7 +29,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import com.example.rememberme.R -import com.example.rememberme.data.database.People +import com.example.rememberme.domain.model.People import com.example.rememberme.ui.theme.RememberMeTheme @Composable diff --git a/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt b/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt index a47ebbf..4d3468c 100644 --- a/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt +++ b/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt @@ -10,7 +10,7 @@ import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument import androidx.navigation.navigation import com.example.rememberme.R -import com.example.rememberme.data.database.People +import com.example.rememberme.domain.model.People import com.example.rememberme.presentation.details.PersonDetailsScreen import com.example.rememberme.presentation.details.PersonDetailsViewModel import com.example.rememberme.presentation.onboarding.OnBoardingScreen diff --git a/app/src/main/java/com/example/rememberme/presentation/peopleList/PeopleListScreen.kt b/app/src/main/java/com/example/rememberme/presentation/peopleList/PeopleListScreen.kt index a142159..58df5fb 100644 --- a/app/src/main/java/com/example/rememberme/presentation/peopleList/PeopleListScreen.kt +++ b/app/src/main/java/com/example/rememberme/presentation/peopleList/PeopleListScreen.kt @@ -13,30 +13,39 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel import com.example.rememberme.R import com.example.rememberme.data.FakeDataSource +import com.example.rememberme.domain.model.People import com.example.rememberme.presentation.composable.PeopleListItem import com.example.rememberme.ui.theme.RememberMeTheme @Composable fun PeopleScreen( modifier: Modifier = Modifier, - viewModel: PeopleViewModel = PeopleViewModel(), + viewModel: PeopleViewModel = hiltViewModel(), navigateToDetailScreen: (Long) -> Unit ) { - PeopleScreenContent(modifier, navigateToDetailScreen) + val people = viewModel.people.collectAsState(initial = emptyList()) + PeopleScreenContent(people, modifier, navigateToDetailScreen) } @OptIn(ExperimentalMaterial3Api::class) @Composable -fun PeopleScreenContent(modifier: Modifier = Modifier, navigateToDetailScreen: (Long) -> Unit) { +fun PeopleScreenContent( + peopleState: State>, + modifier: Modifier = Modifier, + navigateToDetailScreen: (Long) -> Unit +) { - val peopleList = remember { FakeDataSource.getPeopleList() } Scaffold( topBar = { TopAppBar( @@ -64,8 +73,8 @@ fun PeopleScreenContent(modifier: Modifier = Modifier, navigateToDetailScreen: ( .fillMaxSize() .testTag("PeopleListScreen") ) { - items(count = peopleList.size) { index -> - PeopleListItem(peopleList[index], { personId -> + items(count = peopleState.value.size) { index -> + PeopleListItem(peopleState.value[index], { personId -> navigateToDetailScreen(personId) }) } @@ -78,7 +87,12 @@ fun PeopleScreenContent(modifier: Modifier = Modifier, navigateToDetailScreen: ( @Composable fun PeopleScreenContentPreview() { RememberMeTheme { - PeopleScreenContent(navigateToDetailScreen = {}) + PeopleScreenContent( + peopleState = remember { + mutableStateOf(FakeDataSource.getPeopleList()) + }, + navigateToDetailScreen = {} + ) } } diff --git a/app/src/main/java/com/example/rememberme/presentation/peopleList/PeopleViewModel.kt b/app/src/main/java/com/example/rememberme/presentation/peopleList/PeopleViewModel.kt index ec9c593..53ae699 100644 --- a/app/src/main/java/com/example/rememberme/presentation/peopleList/PeopleViewModel.kt +++ b/app/src/main/java/com/example/rememberme/presentation/peopleList/PeopleViewModel.kt @@ -1,4 +1,13 @@ package com.example.rememberme.presentation.peopleList -class PeopleViewModel { +import androidx.lifecycle.ViewModel +import com.example.rememberme.domain.usecases.people.PeopleUseCases +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class PeopleViewModel @Inject constructor( + private val peopleUseCases: PeopleUseCases +): ViewModel() { + val people = peopleUseCases.getAllPeople() } \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/utils/Constants.kt b/app/src/main/java/com/example/rememberme/utils/Constants.kt new file mode 100644 index 0000000..fe6f7c3 --- /dev/null +++ b/app/src/main/java/com/example/rememberme/utils/Constants.kt @@ -0,0 +1,5 @@ +package com.example.rememberme.utils + +object Constants { + const val PEOPLE_DATABASE_NAME = "people_db" +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index cd43faa..8919288 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,6 +5,16 @@ plugins { alias(libs.plugins.compose.compiler) apply false id("com.google.devtools.ksp") version "2.0.0-1.0.22" apply false id("com.google.dagger.hilt.android") version "2.44" apply false - - +} +buildscript { + val kotlinVersion by extra("2.0.0") + repositories { + google() + mavenCentral() + } + dependencies { + classpath(libs.gradle) + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") + classpath(libs.hilt.android.gradle.plugin) + } } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b913856..c3cd8f6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,9 @@ agp = "8.4.2" coreSplashscreen = "1.0.1" coreTesting = "2.2.0" coreTestingVersion = "1.1.1" -hiltCompiler = "2.51.1" +gradle = "8.0.2" +hiltCompiler = "2.49" +hiltAndroidGradlePlugin = "2.49" hiltNavigationCompose = "1.2.0" kotlin = "2.0.0" coreKtx = "1.13.1" @@ -33,9 +35,11 @@ androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomRunt androidx-room-room-testing = { module = "androidx.room:room-testing", version.ref = "roomRuntime" } androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" } core-testing = { module = "android.arch.core:core-testing", version.ref = "coreTestingVersion" } +gradle = { module = "com.android.tools.build:gradle", version.ref = "gradle" } hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltCompiler" } hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hiltCompiler" } androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigationCompose" } +hilt-android-gradle-plugin = { module = "com.google.dagger:hilt-android-gradle-plugin", version.ref = "hiltAndroidGradlePlugin" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } From 28c4bdac67fa5df84fc5751a475050429b9fd4b1 Mon Sep 17 00:00:00 2001 From: omer358 Date: Wed, 19 Jun 2024 14:33:02 +0400 Subject: [PATCH 12/18] Impl personDetails feature --- app/build.gradle.kts | 6 +++++ .../data/{ => local}/PeopleDatabaseTest.kt | 3 +-- .../rememberme/data/PeopleRepositoryImpl.kt | 10 ++++---- .../rememberme/data/local/PeopleDao.kt | 3 +-- .../com/example/rememberme/di/AppModule.kt | 4 ++- .../domain/repository/PeopleRepository.kt | 2 +- .../domain/usecases/people/GetPersonById.kt | 14 +++++++++++ .../domain/usecases/people/PeopleUseCases.kt | 1 + .../presentation/details/PersonDetails.kt | 25 +++++++++++++++---- .../details/PersonDetailsViewModel.kt | 23 ++++++++++++++++- .../presentation/navgraph/NavGraph.kt | 18 +++---------- gradle/libs.versions.toml | 8 ++++++ 12 files changed, 86 insertions(+), 31 deletions(-) rename app/src/androidTest/java/com/example/rememberme/data/{ => local}/PeopleDatabaseTest.kt (97%) create mode 100644 app/src/main/java/com/example/rememberme/domain/usecases/people/GetPersonById.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 150423b..2cbdb6e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -104,4 +104,10 @@ dependencies { implementation(libs.hilt.android) implementation(libs.androidx.hilt.navigation.compose) kapt(libs.hilt.compiler) + + // Unit Test + testImplementation(libs.mockito.core) + testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.kotlin) + testImplementation(libs.kotlinx.coroutines.test) } \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/rememberme/data/PeopleDatabaseTest.kt b/app/src/androidTest/java/com/example/rememberme/data/local/PeopleDatabaseTest.kt similarity index 97% rename from app/src/androidTest/java/com/example/rememberme/data/PeopleDatabaseTest.kt rename to app/src/androidTest/java/com/example/rememberme/data/local/PeopleDatabaseTest.kt index 25a3ba8..27d0269 100644 --- a/app/src/androidTest/java/com/example/rememberme/data/PeopleDatabaseTest.kt +++ b/app/src/androidTest/java/com/example/rememberme/data/local/PeopleDatabaseTest.kt @@ -1,10 +1,9 @@ -package com.example.rememberme.data +package com.example.rememberme.data.local import androidx.room.Room import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.example.rememberme.data.local.PeopleDatabase import com.example.rememberme.domain.model.People import com.example.remindme.database.PeopleDao import kotlinx.coroutines.runBlocking diff --git a/app/src/main/java/com/example/rememberme/data/PeopleRepositoryImpl.kt b/app/src/main/java/com/example/rememberme/data/PeopleRepositoryImpl.kt index 063747d..2b0ca05 100644 --- a/app/src/main/java/com/example/rememberme/data/PeopleRepositoryImpl.kt +++ b/app/src/main/java/com/example/rememberme/data/PeopleRepositoryImpl.kt @@ -13,23 +13,23 @@ class PeopleRepositoryImpl( } override suspend fun insertPeople(people: People) { - TODO("Not yet implemented") + peopleDao.insert(people) } override suspend fun deletePeople(people: People) { - TODO("Not yet implemented") + peopleDao.removePerson(people.id) } override suspend fun updatePeople(people: People) { - TODO("Not yet implemented") + peopleDao.update(people) } override suspend fun deleteAllPeople() { TODO("Not yet implemented") } - override suspend fun getPeopleById(id: Int): People? { - TODO("Not yet implemented") + override suspend fun getPeopleById(id: Long): Flow { + return peopleDao.getPerson(id) } override suspend fun getPeopleByName(name: String): List { diff --git a/app/src/main/java/com/example/rememberme/data/local/PeopleDao.kt b/app/src/main/java/com/example/rememberme/data/local/PeopleDao.kt index 2e216fb..07366ad 100644 --- a/app/src/main/java/com/example/rememberme/data/local/PeopleDao.kt +++ b/app/src/main/java/com/example/rememberme/data/local/PeopleDao.kt @@ -1,6 +1,5 @@ package com.example.remindme.database -import androidx.lifecycle.LiveData import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy @@ -22,7 +21,7 @@ interface PeopleDao { fun update (people: People) @Query("SELECT * FROM people_table WHERE id = :key") - fun getPerson(key: Long):LiveData + fun getPerson(key: Long):Flow @Query("DELETE FROM people_table") fun clear() diff --git a/app/src/main/java/com/example/rememberme/di/AppModule.kt b/app/src/main/java/com/example/rememberme/di/AppModule.kt index 609502a..598afb3 100644 --- a/app/src/main/java/com/example/rememberme/di/AppModule.kt +++ b/app/src/main/java/com/example/rememberme/di/AppModule.kt @@ -6,6 +6,7 @@ import com.example.rememberme.data.PeopleRepositoryImpl import com.example.rememberme.data.local.PeopleDatabase import com.example.rememberme.domain.repository.PeopleRepository import com.example.rememberme.domain.usecases.people.GetAllPeople +import com.example.rememberme.domain.usecases.people.GetPersonById import com.example.rememberme.domain.usecases.people.PeopleUseCases import com.example.rememberme.utils.Constants.PEOPLE_DATABASE_NAME import com.example.remindme.database.PeopleDao @@ -52,7 +53,8 @@ object AppModule { peopleRepository: PeopleRepository ): PeopleUseCases { return PeopleUseCases( - getAllPeople = GetAllPeople(peopleRepository) + getAllPeople = GetAllPeople(peopleRepository), + getPersonById = GetPersonById(peopleRepository) ) } diff --git a/app/src/main/java/com/example/rememberme/domain/repository/PeopleRepository.kt b/app/src/main/java/com/example/rememberme/domain/repository/PeopleRepository.kt index cadcce6..5b09295 100644 --- a/app/src/main/java/com/example/rememberme/domain/repository/PeopleRepository.kt +++ b/app/src/main/java/com/example/rememberme/domain/repository/PeopleRepository.kt @@ -9,6 +9,6 @@ interface PeopleRepository { suspend fun deletePeople(people: People) suspend fun updatePeople(people: People) suspend fun deleteAllPeople() - suspend fun getPeopleById(id: Int): People? + suspend fun getPeopleById(id: Long): Flow suspend fun getPeopleByName(name: String): List } \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/domain/usecases/people/GetPersonById.kt b/app/src/main/java/com/example/rememberme/domain/usecases/people/GetPersonById.kt new file mode 100644 index 0000000..03f9d19 --- /dev/null +++ b/app/src/main/java/com/example/rememberme/domain/usecases/people/GetPersonById.kt @@ -0,0 +1,14 @@ +package com.example.rememberme.domain.usecases.people + +import com.example.rememberme.domain.model.People +import com.example.rememberme.domain.repository.PeopleRepository +import kotlinx.coroutines.flow.Flow + +class GetPersonById( + private val peopleRepository: PeopleRepository +) { + suspend operator fun invoke(id: Long): Flow { + return peopleRepository.getPeopleById(id) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/domain/usecases/people/PeopleUseCases.kt b/app/src/main/java/com/example/rememberme/domain/usecases/people/PeopleUseCases.kt index 628d718..9386838 100644 --- a/app/src/main/java/com/example/rememberme/domain/usecases/people/PeopleUseCases.kt +++ b/app/src/main/java/com/example/rememberme/domain/usecases/people/PeopleUseCases.kt @@ -2,4 +2,5 @@ package com.example.rememberme.domain.usecases.people data class PeopleUseCases( val getAllPeople: GetAllPeople, + val getPersonById: GetPersonById ) \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt index b8a1469..b93beac 100644 --- a/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt +++ b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt @@ -1,8 +1,10 @@ package com.example.rememberme.presentation.details import android.content.res.Configuration.UI_MODE_NIGHT_YES +import android.util.Log import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -20,9 +22,11 @@ import androidx.compose.material3.Card import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview @@ -32,14 +36,22 @@ import com.example.rememberme.R import com.example.rememberme.domain.model.People import com.example.rememberme.ui.theme.RememberMeTheme +private const val TAG = "PersonDetails" + @Composable fun PersonDetailsScreen( - person: People, - viewModel: PersonDetailsViewModel = hiltViewModel() + viewModel: PersonDetailsViewModel = hiltViewModel(), + personId: Long ) { - PersonDetailsContent( - person = person, - ) + viewModel.getPerson(personId) + val person = viewModel.person.collectAsState().value + //TODO: Check and see why the object is null and then become non-null + if (person != null) { + Log.d(TAG, "PersonDetailsScreen: $person") + PersonDetailsContent(person) + } else { + Log.e(TAG, "PersonDetailsScreen: Person not found") + } } @Composable @@ -61,6 +73,7 @@ fun PersonDetailsContent( ) { Box( modifier = Modifier + .shadow(elevation = 16.dp) .clip( RoundedCornerShape(bottomStart = 8.dp, bottomEnd = 8.dp) ) @@ -73,6 +86,8 @@ fun PersonDetailsContent( modifier = Modifier .padding(start = 8.dp) .clip(CircleShape) + .shadow(elevation = 16.dp) + .border(2.dp, MaterialTheme.colorScheme.secondary, CircleShape) .background(MaterialTheme.colorScheme.inversePrimary) .size(120.dp) .align(Alignment.BottomStart) diff --git a/app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsViewModel.kt b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsViewModel.kt index 78976aa..a5b9660 100644 --- a/app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsViewModel.kt +++ b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsViewModel.kt @@ -1,10 +1,31 @@ package com.example.rememberme.presentation.details import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.rememberme.domain.model.People +import com.example.rememberme.domain.usecases.people.PeopleUseCases import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import javax.inject.Inject @HiltViewModel -class PersonDetailsViewModel @Inject constructor() : ViewModel() { +class PersonDetailsViewModel @Inject constructor( + private val peopleUseCases: PeopleUseCases +) : ViewModel() { + private val _person = MutableStateFlow(null) + val person = _person + fun getPerson(id: Long) { + viewModelScope.launch { + withContext(Dispatchers.IO) { + peopleUseCases.getPersonById(id).collect { + _person.value = it + } + } + } + + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt b/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt index 4d3468c..15cbaf1 100644 --- a/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt +++ b/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt @@ -1,7 +1,6 @@ package com.example.rememberme.presentation.navgraph import androidx.compose.runtime.Composable -import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController import androidx.navigation.NavType import androidx.navigation.compose.NavHost @@ -9,10 +8,7 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument import androidx.navigation.navigation -import com.example.rememberme.R -import com.example.rememberme.domain.model.People import com.example.rememberme.presentation.details.PersonDetailsScreen -import com.example.rememberme.presentation.details.PersonDetailsViewModel import com.example.rememberme.presentation.onboarding.OnBoardingScreen import com.example.rememberme.presentation.peopleList.PeopleScreen @@ -58,17 +54,11 @@ fun NavGraph( arguments = listOf(navArgument("personId") { type = NavType.LongType }) ) { val personId = it.arguments?.getLong("personId") - val viewModel: PersonDetailsViewModel = hiltViewModel() - PersonDetailsScreen( - People( - firstName = "William", - secondName = "Doe", - avatar = R.drawable.ic_m3, - time = "10:00 AM", - place = "Home", - gender = "Male" + if (personId != null) { + PersonDetailsScreen( + personId = personId, ) - ) + } } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c3cd8f6..c3fba41 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,9 +12,13 @@ coreKtx = "1.13.1" junit = "4.13.2" junitVersion = "1.1.5" espressoCore = "3.5.1" +kotlinxCoroutinesTest = "1.8.1" lifecycleRuntimeKtx = "2.8.2" activityCompose = "1.9.0" composeBom = "2024.06.00" +mockitoCore = "5.12.0" +mockitoInline = "3.11.2" +mockitoKotlin = "5.3.1" navigationCompose = "2.7.7" roomRuntime = "2.6.1" truth = "1.4.2" @@ -55,6 +59,10 @@ androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit androidx-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-ui-text-google-fonts = { group = "androidx.compose.ui", name = "ui-text-google-fonts", version.ref = "uiTextGoogleFonts" } androidx-junit-ktx = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "junitKtx" } +kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutinesTest" } +mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockitoCore" } +mockito-inline = { module = "org.mockito:mockito-inline", version.ref = "mockitoInline" } +mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockitoKotlin" } truth = { module = "com.google.truth:truth", version.ref = "truth" } [plugins] From 9d4ef6811e1d955429ec07003a47e384d41337bc Mon Sep 17 00:00:00 2001 From: omer358 Date: Wed, 19 Jun 2024 23:56:38 +0400 Subject: [PATCH 13/18] use states and events in PersonDetails --- app/build.gradle.kts | 1 + .../repository/PeopleRepositoryImplTest.kt | 119 ++++++++++++++++ .../presentation/details/PersonDetails.kt | 129 +++++++++++++++--- .../details/PersonDetailsEvent.kt | 6 + .../details/PersonDetailsUiState.kt | 9 ++ .../details/PersonDetailsViewModel.kt | 33 +++-- .../presentation/navgraph/NavGraph.kt | 3 + gradle/libs.versions.toml | 2 + 8 files changed, 273 insertions(+), 29 deletions(-) create mode 100644 app/src/androidTest/java/com/example/rememberme/data/repository/PeopleRepositoryImplTest.kt create mode 100644 app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsEvent.kt create mode 100644 app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsUiState.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2cbdb6e..9642764 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -65,6 +65,7 @@ dependencies { implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.material3) + implementation(libs.androidx.lifecycle.runtime.compose.android) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/app/src/androidTest/java/com/example/rememberme/data/repository/PeopleRepositoryImplTest.kt b/app/src/androidTest/java/com/example/rememberme/data/repository/PeopleRepositoryImplTest.kt new file mode 100644 index 0000000..a4270e8 --- /dev/null +++ b/app/src/androidTest/java/com/example/rememberme/data/repository/PeopleRepositoryImplTest.kt @@ -0,0 +1,119 @@ +//package com.example.rememberme.data.repository +// +//import com.example.rememberme.R +//import com.example.rememberme.data.PeopleRepositoryImpl +//import com.example.rememberme.domain.model.People +//import com.example.remindme.database.PeopleDao +//import kotlinx.coroutines.ExperimentalCoroutinesApi +//import kotlinx.coroutines.test.runTest +//import org.junit.Assert.assertEquals +//import org.junit.Before +//import org.junit.Test +//import org.mockito.MockitoAnnotations +//import javax.inject.Inject +// +//@ExperimentalCoroutinesApi +//class PeopleRepositoryImplTest { +// +// @Inject +// private lateinit var peopleDao: PeopleDao +// +// private lateinit var peopleRepository: PeopleRepositoryImpl +// +// @Before +// fun setUp() { +// MockitoAnnotations.initMocks(this::class) +// peopleRepository = PeopleRepositoryImpl(peopleDao) +// } +// +// @Test +// fun getAllPeopleShouldReturnListOfPeople() = runTest { +// // Arrange +// val peopleList = listOf( +// People( +// 1, "John", secondName = "Doe", place = "", time = "", gender = "",avatar= R.drawable.ic_m3,), +// People(1, "John", secondName = "Doe", place = "", time = "", gender = "",avatar= R.drawable.ic_m3,) +// ) +// peopleRepository.insertPeople(peopleList[0]) +// peopleRepository.insertPeople(peopleList[1]) +// +// // Act +// val result = peopleRepository.getAllPeople() +// +// // Assert +// result.collect { +// assertEquals(peopleList, it) +// } +// } +// +//// @Test +//// fun insertPeopleShouldCallInsertPeopleOnDao() = runBlocking { +//// // Arrange +//// val person = People(1, "John", secondName = "Doe", place = "", time = "", gender = "",avatar= R.drawable.ic_m3,) +//// +//// // Act +//// peopleRepository.insertPeople(person) +//// +//// // Assert +//// Mockito.verify(peopleDao, Mockito.times(1)).insertPeople(person) +//// } +//// +//// @Test +//// fun `deletePeople should call deletePeople on dao`() = runBlocking { +//// // Arrange +//// val person = People(1, "John", secondName = "Doe", place = "", time = "", gender = "",avatar= R.drawable.ic_m3,) +//// +//// // Act +//// peopleRepository.deletePeople(person) +//// +//// // Assert +//// Mockito.verify(peopleDao, Mockito.times(1)).deletePeople(person) +//// } +//// +//// @Test +//// fun `updatePeople should call updatePeople on dao`() = runBlocking { +//// // Arrange +//// val person = People(1, "John Doe") +//// +//// // Act +//// peopleRepository.updatePeople(person) +//// +//// // Assert +//// Mockito.verify(peopleDao, Mockito.times(1)).updatePeople(person) +//// } +//// +//// @Test +//// fun `deleteAllPeople should call deleteAllPeople on dao`() = runBlocking { +//// // Act +//// peopleRepository.deleteAllPeople() +//// +//// // Assert +//// Mockito.verify(peopleDao, Mockito.times(1)).deleteAllPeople() +//// } +//// +//// @Test +//// fun `getPeopleById should return person`() = runBlocking { +//// // Arrange +//// val person = People(1, "John Doe") +//// Mockito.`when`(peopleDao.getPeopleById(1)).thenReturn(person) +//// +//// // Act +//// val result = peopleRepository.getPeopleById(1) +//// +//// // Assert +//// assertEquals(person, result) +//// } +//// +//// @Test +//// fun `getPeopleByName should return list of people`() = runBlocking { +//// // Arrange +//// val peopleList = listOf(People(1, "John Doe"), People(2, "John Smith")) +//// Mockito.`when`(peopleDao.getPeopleByName("John")).thenReturn(peopleList) +//// +//// // Act +//// val result = peopleRepository.getPeopleByName("John") +//// +//// // Assert +//// assertEquals(peopleList, result) +//// } +//} diff --git a/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt index b93beac..b8df60b 100644 --- a/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt +++ b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetails.kt @@ -18,45 +18,106 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.Card +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.IconButtonColors import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.example.rememberme.R import com.example.rememberme.domain.model.People import com.example.rememberme.ui.theme.RememberMeTheme +import kotlinx.coroutines.launch private const val TAG = "PersonDetails" @Composable fun PersonDetailsScreen( viewModel: PersonDetailsViewModel = hiltViewModel(), - personId: Long + personId: Long, + navigateUp: () -> Unit ) { - viewModel.getPerson(personId) - val person = viewModel.person.collectAsState().value - //TODO: Check and see why the object is null and then become non-null - if (person != null) { - Log.d(TAG, "PersonDetailsScreen: $person") - PersonDetailsContent(person) - } else { - Log.e(TAG, "PersonDetailsScreen: Person not found") + val uiState = viewModel.uiState.collectAsStateWithLifecycle() + val coroutineScope = rememberCoroutineScope() + + + LaunchedEffect(personId) { + coroutineScope.launch { + viewModel.onEvent(PersonDetailsEvent.LoadPerson(personId)) + } + } + when { + uiState.value.isLoading -> { + LoadingIndicator() + Log.d(TAG, "PersonDetailsScreen: Loading") + } + uiState.value.error != null -> { + Log.e(TAG, "PersonDetailsScreen: Error - ${uiState.value.error}") + ErrorContent(uiState.value.error!!) + } + uiState.value.person != null -> { + Log.d(TAG, "PersonDetailsScreen: ${uiState.value.person}") + PersonDetailsContent(uiState.value.person!!, navigateUp) + } + else -> { + Log.e(TAG, "PersonDetailsScreen: Person not found") + // Optionally, you can add a UI to show "Person not found" + } + } +} + +@Composable +fun ErrorContent(error: String) { + // TODO: Implement a custom Error animation in the future + Column { + Icon( + imageVector = Icons.Default.Warning, + contentDescription = null, + modifier = Modifier + .size(120.dp) + .align(Alignment.CenterHorizontally) + ) + Spacer(modifier = Modifier.height(16.dp)) + Text(text = error, style = MaterialTheme.typography.headlineLarge) + } +} + +@Composable +fun LoadingIndicator() { + // TODO: Implement a custom Loading animation in the future + Column ( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ){ + CircularProgressIndicator() + Spacer(modifier = Modifier.height(16.dp)) + Text(text = "Loading...") } } @Composable fun PersonDetailsContent( person: People, + navigateUp: () -> Unit, modifier: Modifier = Modifier ) { val scrollState = rememberScrollState() @@ -81,7 +142,27 @@ fun PersonDetailsContent( .fillMaxWidth() .height(200.dp) - ) + ) { + IconButton( + modifier = Modifier + .align(Alignment.TopStart) + .padding(top = 16.dp, start = 8.dp), + colors = IconButtonColors( + contentColor = MaterialTheme.colorScheme.onPrimary, + disabledContentColor = MaterialTheme.colorScheme.onPrimary, + containerColor = Color.Transparent, + disabledContainerColor = Color.Transparent + ), + onClick = { + Log.d(TAG, "PersonDetailsContent: Clicked") + navigateUp() + }) { + Icon( + imageVector = Icons.AutoMirrored.Default.ArrowBack, + contentDescription = null + ) + } + } Box( modifier = Modifier .padding(start = 8.dp) @@ -153,7 +234,6 @@ fun PersonDetailsContent( } } } - } @Preview(showBackground = true) @@ -162,14 +242,27 @@ fun PersonDetailsContent( fun PersonDetailsContentPreview() { RememberMeTheme { PersonDetailsContent( - People( + person = People( firstName = "William", secondName = "Doe", - avatar = R.drawable.ic_m3, - time = "10:00 AM", + gender = "Male", + time = "12:00", place = "Home", - gender = "Male" - ) + avatar = R.drawable.ic_m4 + ), + navigateUp = { + Log.d(TAG, "PersonDetailsContentPreview: Clicked") + } ) } -} \ No newline at end of file +} + +@Preview(showBackground = true) +@Preview(uiMode = UI_MODE_NIGHT_YES) +@Composable +fun LoadingIndicatorContentPreview() { + RememberMeTheme { + LoadingIndicator() + } +} + diff --git a/app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsEvent.kt b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsEvent.kt new file mode 100644 index 0000000..cdff90e --- /dev/null +++ b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsEvent.kt @@ -0,0 +1,6 @@ +package com.example.rememberme.presentation.details + +sealed class PersonDetailsEvent { + data class LoadPerson(val personId: Long) : PersonDetailsEvent() + data object NavigateUp : PersonDetailsEvent() +} diff --git a/app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsUiState.kt b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsUiState.kt new file mode 100644 index 0000000..126a3a1 --- /dev/null +++ b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsUiState.kt @@ -0,0 +1,9 @@ +package com.example.rememberme.presentation.details + +import com.example.rememberme.domain.model.People + +data class PersonDetailsUiState( + val person: People? = null, + val isLoading: Boolean = false, + val error: String? = null +) diff --git a/app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsViewModel.kt b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsViewModel.kt index a5b9660..7d0b8f4 100644 --- a/app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsViewModel.kt +++ b/app/src/main/java/com/example/rememberme/presentation/details/PersonDetailsViewModel.kt @@ -2,30 +2,41 @@ package com.example.rememberme.presentation.details import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.example.rememberme.domain.model.People import com.example.rememberme.domain.usecases.people.PeopleUseCases import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import javax.inject.Inject @HiltViewModel class PersonDetailsViewModel @Inject constructor( private val peopleUseCases: PeopleUseCases ) : ViewModel() { - private val _person = MutableStateFlow(null) - val person = _person + private val _uiState = MutableStateFlow(PersonDetailsUiState()) + val uiState: StateFlow = _uiState - fun getPerson(id: Long) { + fun onEvent(event: PersonDetailsEvent) { + when (event) { + is PersonDetailsEvent.LoadPerson -> { + loadPerson(event.personId) + } + is PersonDetailsEvent.NavigateUp -> { + // Handle navigation + } + } + } + + private fun loadPerson(personId: Long) { + _uiState.value = _uiState.value.copy(isLoading = true) viewModelScope.launch { - withContext(Dispatchers.IO) { - peopleUseCases.getPersonById(id).collect { - _person.value = it + try { + peopleUseCases.getPersonById(personId).collect { person -> + _uiState.value = PersonDetailsUiState(person = person) } + } catch (e: Exception) { + _uiState.value = PersonDetailsUiState(error = e.message) } } - } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt b/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt index 15cbaf1..6986211 100644 --- a/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt +++ b/app/src/main/java/com/example/rememberme/presentation/navgraph/NavGraph.kt @@ -57,6 +57,9 @@ fun NavGraph( if (personId != null) { PersonDetailsScreen( personId = personId, + navigateUp = { + navController.navigateUp() + } ) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c3fba41..0bcb039 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -24,6 +24,7 @@ roomRuntime = "2.6.1" truth = "1.4.2" uiTextGoogleFonts = "1.6.8" junitKtx = "1.1.5" +lifecycleRuntimeComposeAndroid = "2.8.2" @@ -64,6 +65,7 @@ mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockitoCore mockito-inline = { module = "org.mockito:mockito-inline", version.ref = "mockitoInline" } mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockitoKotlin" } truth = { module = "com.google.truth:truth", version.ref = "truth" } +androidx-lifecycle-runtime-compose-android = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose-android", version.ref = "lifecycleRuntimeComposeAndroid" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } From 22e11ccc689f51af87c50e73fb27bf0d8c10d26b Mon Sep 17 00:00:00 2001 From: omer358 Date: Wed, 19 Jun 2024 23:59:03 +0400 Subject: [PATCH 14/18] remove pull request triggers for github action --- .github/workflows/android.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index f376cde..d56d2f6 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -3,8 +3,6 @@ name: Android CI on: push: branches: [ "main" ] - pull_request: - branches: [ "main" ] jobs: build: From 6415b31d93780413e7f872f96f0a8395312bbabd Mon Sep 17 00:00:00 2001 From: omer358 Date: Sat, 22 Jun 2024 21:54:24 +0400 Subject: [PATCH 15/18] add firebase support --- app/build.gradle.kts | 5 ++++- build.gradle.kts | 1 + gradle/libs.versions.toml | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9642764..608e3ed 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -5,7 +5,7 @@ plugins { id("com.google.devtools.ksp") id("com.google.dagger.hilt.android") id("kotlin-kapt") -// kapt("groupId:artifactId:version") + id("com.google.gms.google-services") } @@ -111,4 +111,7 @@ dependencies { testImplementation(libs.mockito.inline) testImplementation(libs.mockito.kotlin) testImplementation(libs.kotlinx.coroutines.test) + + // Firebase Bom + implementation(libs.firebase.bom) } \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 8919288..60fafb4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,6 +5,7 @@ plugins { alias(libs.plugins.compose.compiler) apply false id("com.google.devtools.ksp") version "2.0.0-1.0.22" apply false id("com.google.dagger.hilt.android") version "2.44" apply false + id("com.google.gms.google-services") version "4.4.2" apply false } buildscript { val kotlinVersion by extra("2.0.0") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0bcb039..c8a7b1a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,6 +3,7 @@ agp = "8.4.2" coreSplashscreen = "1.0.1" coreTesting = "2.2.0" coreTestingVersion = "1.1.1" +firebaseBom = "33.1.0" gradle = "8.0.2" hiltCompiler = "2.49" hiltAndroidGradlePlugin = "2.49" @@ -40,6 +41,7 @@ androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomRunt androidx-room-room-testing = { module = "androidx.room:room-testing", version.ref = "roomRuntime" } androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" } core-testing = { module = "android.arch.core:core-testing", version.ref = "coreTestingVersion" } +firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" } gradle = { module = "com.android.tools.build:gradle", version.ref = "gradle" } hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltCompiler" } hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hiltCompiler" } From 1da52dbfbf37408be566316c2eb333ac049d44e7 Mon Sep 17 00:00:00 2001 From: omer358 Date: Sat, 22 Jun 2024 22:32:56 +0400 Subject: [PATCH 16/18] ignore google-services.json --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index aa724b7..93bfc47 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ .externalNativeBuild .cxx local.properties +app/google-services.json From eb2dafbbc8627bcf55ed77a5c0e810eb8e94d843 Mon Sep 17 00:00:00 2001 From: omer358 Date: Sun, 23 Jun 2024 00:54:44 +0400 Subject: [PATCH 17/18] update .gitignore --- .gitignore | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/.gitignore b/.gitignore index 93bfc47..a56ccc7 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,89 @@ .cxx local.properties app/google-services.json + +# Built application files +*.apk +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +# Uncomment the following line in case you need and you don't have the release build type files in your app +# release/ + +# Gradle files +.gradle/ +gradle/ +build/ + +# Local configuration file (sdk path, etc) + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +.idea/ +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +# Android Studio 3 in .gitignore file. +.idea/caches +.idea/modules.xml +# Comment next line if keeping position of elements in Navigation Editor is relevant for you +.idea/navEditor.xml + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.cxx/ + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ + +apikey.properties From 485caba5f8497f617f2266dfdfcbeaaf8bea26ed Mon Sep 17 00:00:00 2001 From: omer358 Date: Sun, 23 Jun 2024 03:10:01 +0400 Subject: [PATCH 18/18] resolves #6 --- .idea/appInsightsSettings.xml | 8 +- .idea/deploymentTargetSelector.xml | 17 +- .../repository/PeopleRepositoryImplTest.kt | 8 +- .../rememberme/data/PeopleRepositoryImpl.kt | 2 +- .../com/example/rememberme/di/AppModule.kt | 4 +- .../domain/repository/PeopleRepository.kt | 2 +- .../domain/usecases/people/InsertNewPerson.kt | 13 ++ .../domain/usecases/people/PeopleUseCases.kt | 3 +- .../presentation/addperson/AddPersonEvents.kt | 11 ++ .../presentation/addperson/AddPersonScreen.kt | 175 ++++++++++++++++++ .../addperson/AddPersonUiState.kt | 11 ++ .../addperson/AddPersonViewModel.kt | 76 ++++++++ .../common/composables/CustomButton.kt | 40 ++++ .../composables/CustomOutLinedTextField.kt | 46 +++++ .../composables}/PeopleListItem.kt | 2 +- .../presentation/navgraph/NavGraph.kt | 10 +- .../peopleList/PeopleListScreen.kt | 15 +- 17 files changed, 417 insertions(+), 26 deletions(-) create mode 100644 app/src/main/java/com/example/rememberme/domain/usecases/people/InsertNewPerson.kt create mode 100644 app/src/main/java/com/example/rememberme/presentation/addperson/AddPersonEvents.kt create mode 100644 app/src/main/java/com/example/rememberme/presentation/addperson/AddPersonScreen.kt create mode 100644 app/src/main/java/com/example/rememberme/presentation/addperson/AddPersonUiState.kt create mode 100644 app/src/main/java/com/example/rememberme/presentation/addperson/AddPersonViewModel.kt create mode 100644 app/src/main/java/com/example/rememberme/presentation/common/composables/CustomButton.kt create mode 100644 app/src/main/java/com/example/rememberme/presentation/common/composables/CustomOutLinedTextField.kt rename app/src/main/java/com/example/rememberme/presentation/{composable => common/composables}/PeopleListItem.kt (97%) diff --git a/.idea/appInsightsSettings.xml b/.idea/appInsightsSettings.xml index 371f2e2..130bcfc 100644 --- a/.idea/appInsightsSettings.xml +++ b/.idea/appInsightsSettings.xml @@ -8,10 +8,10 @@