diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml
new file mode 100644
index 00000000..165d9c32
--- /dev/null
+++ b/.idea/deploymentTargetDropDown.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
new file mode 100644
index 00000000..b268ef36
--- /dev/null
+++ b/.idea/deploymentTargetSelector.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 4e3844e8..ecd50f02 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -1,18 +1,19 @@
+
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 00000000..28422375
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 860da66a..d5d35ec4 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644
index 00000000..16660f1d
--- /dev/null
+++ b/.idea/runConfigurations.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/sonarlint/issuestore/4/9/49714c180cf12e6bb0fb0f86b8a2e6b52e75ed1d b/.idea/sonarlint/issuestore/4/9/49714c180cf12e6bb0fb0f86b8a2e6b52e75ed1d
new file mode 100644
index 00000000..6f1e916c
--- /dev/null
+++ b/.idea/sonarlint/issuestore/4/9/49714c180cf12e6bb0fb0f86b8a2e6b52e75ed1d
@@ -0,0 +1,2 @@
+
+Okotlin:S1172"2Remove this unused function parameter "darkTheme".(��������
\ No newline at end of file
diff --git a/.idea/sonarlint/issuestore/5/d/5dd0036e6eaabc1cbb2545b80a0bfffc2708a45b b/.idea/sonarlint/issuestore/5/d/5dd0036e6eaabc1cbb2545b80a0bfffc2708a45b
new file mode 100644
index 00000000..e69de29b
diff --git a/.idea/sonarlint/issuestore/9/e/9ea6805d7eed655f05d5c8957775a6e66211bf8e b/.idea/sonarlint/issuestore/9/e/9ea6805d7eed655f05d5c8957775a6e66211bf8e
new file mode 100644
index 00000000..e69de29b
diff --git a/.idea/sonarlint/issuestore/f/0/f07866736216be0ee2aba49e392191aeae700a35 b/.idea/sonarlint/issuestore/f/0/f07866736216be0ee2aba49e392191aeae700a35
new file mode 100644
index 00000000..e69de29b
diff --git a/.idea/sonarlint/issuestore/f/4/f4a01d6a4fcb971362ec00a83903fd3902f52164 b/.idea/sonarlint/issuestore/f/4/f4a01d6a4fcb971362ec00a83903fd3902f52164
new file mode 100644
index 00000000..e69de29b
diff --git a/.idea/sonarlint/issuestore/index.pb b/.idea/sonarlint/issuestore/index.pb
index e69de29b..003d1499 100644
--- a/.idea/sonarlint/issuestore/index.pb
+++ b/.idea/sonarlint/issuestore/index.pb
@@ -0,0 +1,11 @@
+
+y
+Iapp/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Shape.kt,9\e\9ea6805d7eed655f05d5c8957775a6e66211bf8e
+@
+app/build.gradle,f\4\f4a01d6a4fcb971362ec00a83903fd3902f52164
+<
+build.gradle,f\0\f07866736216be0ee2aba49e392191aeae700a35
+y
+Iapp/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Theme.kt,4\9\49714c180cf12e6bb0fb0f86b8a2e6b52e75ed1d
+y
+Iapp/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Color.kt,5\d\5dd0036e6eaabc1cbb2545b80a0bfffc2708a45b
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..94a25f7f
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
deleted file mode 100644
index 5ec04ab2..00000000
--- a/app/build.gradle
+++ /dev/null
@@ -1,90 +0,0 @@
-plugins {
- id 'com.android.application'
- id 'kotlin-android'
- id 'kotlin-kapt'
- id 'dagger.hilt.android.plugin'
-}
-
-android {
- compileSdk 31
-
- defaultConfig {
- applicationId "com.plcoding.cleanarchitecturenoteapp"
- minSdk 21
- targetSdk 31
- versionCode 1
- versionName "1.0"
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- vectorDrawables {
- useSupportLibrary true
- }
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
- }
- }
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
- kotlinOptions {
- jvmTarget = '1.8'
- useIR = true
- }
- buildFeatures {
- compose true
- }
- composeOptions {
- kotlinCompilerExtensionVersion compose_version
- kotlinCompilerVersion '1.5.21'
- }
- packagingOptions {
- resources {
- excludes += '/META-INF/{AL2.0,LGPL2.1}'
- }
- }
-}
-
-dependencies {
-
- implementation 'androidx.core:core-ktx:1.6.0'
- implementation 'androidx.appcompat:appcompat:1.3.1'
- implementation 'com.google.android.material:material:1.4.0'
- implementation "androidx.compose.ui:ui:$compose_version"
- implementation "androidx.compose.material:material:$compose_version"
- implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
- implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
- implementation 'androidx.activity:activity-compose:1.3.1'
- testImplementation 'junit:junit:4.+'
- androidTestImplementation 'androidx.test.ext:junit:1.1.3'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
- androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
- debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
-
- // Compose dependencies
- implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.4.0-beta01"
- implementation "androidx.navigation:navigation-compose:2.4.0-alpha09"
- implementation "androidx.compose.material:material-icons-extended:$compose_version"
- implementation "androidx.hilt:hilt-navigation-compose:1.0.0-alpha03"
-
- // Coroutines
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.1'
-
- //Dagger - Hilt
- implementation "com.google.dagger:hilt-android:2.38.1"
- kapt "com.google.dagger:hilt-android-compiler:2.37"
- implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
- kapt "androidx.hilt:hilt-compiler:1.0.0"
-
- // Room
- implementation "androidx.room:room-runtime:2.3.0"
- kapt "androidx.room:room-compiler:2.3.0"
-
- // Kotlin Extensions and Coroutines support for Room
- implementation "androidx.room:room-ktx:2.3.0"
-}
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
new file mode 100644
index 00000000..c121e9f1
--- /dev/null
+++ b/app/build.gradle.kts
@@ -0,0 +1,87 @@
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.kotlin.compose)
+ id("kotlin-kapt")
+ id("com.google.dagger.hilt.android")
+}
+
+android {
+ namespace = "com.plcoding.cleanarchitecturenoteapp"
+ compileSdk = 35
+
+ defaultConfig {
+ applicationId = "com.plcoding.cleanarchitecturenoteapp"
+ minSdk = 24
+ targetSdk = 34
+ versionCode = 1
+ versionName = "1.0"
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+ kotlinOptions {
+ jvmTarget = "11"
+ }
+ buildFeatures {
+ compose = true
+ }
+}
+
+dependencies {
+
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.lifecycle.runtime.ktx)
+ implementation(libs.androidx.activity.compose)
+ implementation(platform(libs.androidx.compose.bom))
+ implementation(libs.androidx.ui)
+ implementation(libs.androidx.ui.graphics)
+ implementation(libs.androidx.ui.tooling.preview)
+ implementation(libs.androidx.material3)
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.junit)
+ androidTestImplementation(libs.androidx.espresso.core)
+ androidTestImplementation(platform(libs.androidx.compose.bom))
+ androidTestImplementation(libs.androidx.ui.test.junit4)
+ debugImplementation(libs.androidx.ui.tooling)
+ debugImplementation(libs.androidx.ui.test.manifest)
+
+ // Compose dependencies
+ implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.4.0-beta01")
+ implementation("androidx.navigation:navigation-compose:2.8.3")
+ implementation("androidx.compose.material:material-icons-extended:1.7.5")
+ implementation("androidx.hilt:hilt-navigation-compose:1.0.0-alpha03")
+
+ // Coroutines
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0")
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.1")
+
+ //Dagger - Hilt
+ implementation("com.google.dagger:hilt-android:2.49")
+ kapt("com.google.dagger:hilt-android-compiler:2.49")
+ kapt("androidx.hilt:hilt-compiler:1.0.0")
+
+ // Room
+ implementation("androidx.room:room-runtime:2.6.1")
+ kapt("androidx.room:room-compiler:2.6.1")
+
+ // Kotlin Extensions and Coroutines support for Room
+ implementation("androidx.room:room-ktx:2.6.1")
+}
+
+kapt {
+ correctErrorTypes = true
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 481bb434..ff59496d 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -1,6 +1,6 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
+# proguardFiles setting in build.gradle.kts.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 052e8301..88340904 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,6 +3,7 @@
package="com.plcoding.cleanarchitecturenoteapp">
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/MainActivity.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/MainActivity.kt
deleted file mode 100644
index 8282fe24..00000000
--- a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/MainActivity.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.plcoding.cleanarchitecturenoteapp
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material.MaterialTheme
-import androidx.compose.material.Surface
-import androidx.compose.material.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.tooling.preview.Preview
-import com.plcoding.cleanarchitecturenoteapp.ui.theme.CleanArchitectureNoteAppTheme
-
-class MainActivity : ComponentActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContent {
- CleanArchitectureNoteAppTheme {
-
- }
- }
- }
-}
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/NoteApp.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/NoteApp.kt
new file mode 100644
index 00000000..ec915d87
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/NoteApp.kt
@@ -0,0 +1,7 @@
+package com.plcoding.cleanarchitecturenoteapp
+
+import android.app.Application
+import dagger.hilt.android.HiltAndroidApp
+
+@HiltAndroidApp
+class NoteApp : Application()
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/di/AppModule.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/di/AppModule.kt
new file mode 100644
index 00000000..92660b5f
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/di/AppModule.kt
@@ -0,0 +1,45 @@
+package com.plcoding.cleanarchitecturenoteapp.di
+
+import android.app.Application
+import androidx.room.Room
+import com.plcoding.cleanarchitecturenoteapp.feature_note.data.data_source.NoteDatabase
+import com.plcoding.cleanarchitecturenoteapp.feature_note.data.repository.NoteRepositoryImpl
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.repository.NoteRepository
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.use_case.*
+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 provideNoteDatabase(app: Application): NoteDatabase {
+ return Room.databaseBuilder(
+ app,
+ NoteDatabase::class.java,
+ NoteDatabase.DATABASE_NAME
+ ).build()
+ }
+
+ @Provides
+ @Singleton
+ fun provideNoteRepository(db: NoteDatabase): NoteRepository {
+ return NoteRepositoryImpl(db.noteDao)
+ }
+
+ @Provides
+ @Singleton
+ fun provideNoteUseCases(repository: NoteRepository): NoteUseCases {
+ return NoteUseCases(
+ getNotes = GetNotes(repository),
+ deleteNote = DeleteNote(repository),
+ addNote = AddNote(repository),
+ getNote = GetNote(repository)
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/data/data_source/NoteDao.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/data/data_source/NoteDao.kt
new file mode 100644
index 00000000..fdcb3405
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/data/data_source/NoteDao.kt
@@ -0,0 +1,21 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.data.data_source
+
+import androidx.room.*
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.model.Note
+import kotlinx.coroutines.flow.Flow
+
+@Dao
+interface NoteDao {
+
+ @Query("SELECT * FROM note")
+ fun getNotes(): Flow>
+
+ @Query("SELECT * FROM note WHERE id = :id")
+ suspend fun getNoteById(id: Int): Note?
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun insertNote(note: Note)
+
+ @Delete
+ suspend fun deleteNote(note: Note)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/data/data_source/NoteDatabase.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/data/data_source/NoteDatabase.kt
new file mode 100644
index 00000000..4dd871af
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/data/data_source/NoteDatabase.kt
@@ -0,0 +1,18 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.data.data_source
+
+import androidx.room.Database
+import androidx.room.RoomDatabase
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.model.Note
+
+@Database(
+ entities = [Note::class],
+ version = 1
+)
+abstract class NoteDatabase: RoomDatabase() {
+
+ abstract val noteDao: NoteDao
+
+ companion object {
+ const val DATABASE_NAME = "notes_db"
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/data/repository/NoteRepositoryImpl.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/data/repository/NoteRepositoryImpl.kt
new file mode 100644
index 00000000..39d93125
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/data/repository/NoteRepositoryImpl.kt
@@ -0,0 +1,27 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.data.repository
+
+import com.plcoding.cleanarchitecturenoteapp.feature_note.data.data_source.NoteDao
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.model.Note
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.repository.NoteRepository
+import kotlinx.coroutines.flow.Flow
+
+class NoteRepositoryImpl(
+ private val dao: NoteDao
+) : NoteRepository {
+
+ override fun getNotes(): Flow> {
+ return dao.getNotes()
+ }
+
+ override suspend fun getNoteById(id: Int): Note? {
+ return dao.getNoteById(id)
+ }
+
+ override suspend fun insertNote(note: Note) {
+ dao.insertNote(note)
+ }
+
+ override suspend fun deleteNote(note: Note) {
+ dao.deleteNote(note)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/model/Note.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/model/Note.kt
new file mode 100644
index 00000000..b7473f7e
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/model/Note.kt
@@ -0,0 +1,20 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.domain.model
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.plcoding.cleanarchitecturenoteapp.ui.theme.*
+
+@Entity(tableName = "note")
+data class Note(
+ val title: String,
+ val content: String,
+ val timestamp: Long,
+ val color: Int,
+ @PrimaryKey val id: Int? = null
+) {
+ companion object {
+ val noteColors = listOf(RedOrange, LightGreen, Violet, BabyBlue, RedPink)
+ }
+}
+
+class InvalidNoteException(message: String): Exception(message)
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/repository/NoteRepository.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/repository/NoteRepository.kt
new file mode 100644
index 00000000..471b748a
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/repository/NoteRepository.kt
@@ -0,0 +1,15 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.domain.repository
+
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.model.Note
+import kotlinx.coroutines.flow.Flow
+
+interface NoteRepository {
+
+ fun getNotes(): Flow>
+
+ suspend fun getNoteById(id: Int): Note?
+
+ suspend fun insertNote(note: Note)
+
+ suspend fun deleteNote(note: Note)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/use_case/AddNote.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/use_case/AddNote.kt
new file mode 100644
index 00000000..5e243dff
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/use_case/AddNote.kt
@@ -0,0 +1,21 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.domain.use_case
+
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.model.InvalidNoteException
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.model.Note
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.repository.NoteRepository
+
+class AddNote(
+ private val repository: NoteRepository
+) {
+
+ @Throws(InvalidNoteException::class)
+ suspend operator fun invoke(note: Note) {
+ if(note.title.isBlank()) {
+ throw InvalidNoteException("The title of the note can't be empty.")
+ }
+ if(note.content.isBlank()) {
+ throw InvalidNoteException("The content of the note can't be empty.")
+ }
+ repository.insertNote(note)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/use_case/DeleteNote.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/use_case/DeleteNote.kt
new file mode 100644
index 00000000..e57acdfe
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/use_case/DeleteNote.kt
@@ -0,0 +1,13 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.domain.use_case
+
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.model.Note
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.repository.NoteRepository
+
+class DeleteNote(
+ private val repository: NoteRepository
+) {
+
+ suspend operator fun invoke(note: Note) {
+ repository.deleteNote(note)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/use_case/GetNote.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/use_case/GetNote.kt
new file mode 100644
index 00000000..84e8978c
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/use_case/GetNote.kt
@@ -0,0 +1,13 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.domain.use_case
+
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.model.Note
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.repository.NoteRepository
+
+class GetNote(
+ private val repository: NoteRepository
+) {
+
+ suspend operator fun invoke(id: Int): Note? {
+ return repository.getNoteById(id)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/use_case/GetNotes.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/use_case/GetNotes.kt
new file mode 100644
index 00000000..ac94e18f
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/use_case/GetNotes.kt
@@ -0,0 +1,36 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.domain.use_case
+
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.model.Note
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.repository.NoteRepository
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.util.NoteOrder
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.util.OrderType
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+class GetNotes(
+ private val repository: NoteRepository
+) {
+
+ operator fun invoke(
+ noteOrder: NoteOrder = NoteOrder.Date(OrderType.Descending)
+ ): Flow> {
+ return repository.getNotes().map { notes ->
+ when(noteOrder.orderType) {
+ is OrderType.Ascending -> {
+ when(noteOrder) {
+ is NoteOrder.Title -> notes.sortedBy { it.title.lowercase() }
+ is NoteOrder.Date -> notes.sortedBy { it.timestamp }
+ is NoteOrder.Color -> notes.sortedBy { it.color }
+ }
+ }
+ is OrderType.Descending -> {
+ when(noteOrder) {
+ is NoteOrder.Title -> notes.sortedByDescending { it.title.lowercase() }
+ is NoteOrder.Date -> notes.sortedByDescending { it.timestamp }
+ is NoteOrder.Color -> notes.sortedByDescending { it.color }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/use_case/NoteUseCases.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/use_case/NoteUseCases.kt
new file mode 100644
index 00000000..a89b00c1
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/use_case/NoteUseCases.kt
@@ -0,0 +1,8 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.domain.use_case
+
+data class NoteUseCases(
+ val getNotes: GetNotes,
+ val deleteNote: DeleteNote,
+ val addNote: AddNote,
+ val getNote: GetNote
+)
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/util/NoteOrder.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/util/NoteOrder.kt
new file mode 100644
index 00000000..eb7fa913
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/util/NoteOrder.kt
@@ -0,0 +1,15 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.domain.util
+
+sealed class NoteOrder(val orderType: OrderType) {
+ class Title(orderType: OrderType): NoteOrder(orderType)
+ class Date(orderType: OrderType): NoteOrder(orderType)
+ class Color(orderType: OrderType): NoteOrder(orderType)
+
+ fun copy(orderType: OrderType): NoteOrder {
+ return when(this) {
+ is Title -> Title(orderType)
+ is Date -> Date(orderType)
+ is Color -> Color(orderType)
+ }
+ }
+}
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/util/OrderType.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/util/OrderType.kt
new file mode 100644
index 00000000..de897a19
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/domain/util/OrderType.kt
@@ -0,0 +1,6 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.domain.util
+
+sealed class OrderType {
+ object Ascending: OrderType()
+ object Descending: OrderType()
+}
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/MainActivity.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/MainActivity.kt
new file mode 100644
index 00000000..dd194689
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/MainActivity.kt
@@ -0,0 +1,67 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.presentation
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.navigation.NavType
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
+import androidx.navigation.navArgument
+import com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.add_edit_note.AddEditNoteScreen
+import com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.notes.NotesScreen
+import com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.util.Screen
+import com.plcoding.cleanarchitecturenoteapp.ui.theme.CleanArchitectureNoteAppTheme
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class MainActivity : ComponentActivity() {
+ @ExperimentalAnimationApi
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ CleanArchitectureNoteAppTheme {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ val navController = rememberNavController()
+ NavHost(
+ navController = navController,
+ startDestination = Screen.NotesScreen.route
+ ) {
+ composable(route = Screen.NotesScreen.route) {
+ NotesScreen(navController = navController)
+ }
+ composable(
+ route = Screen.AddEditNoteScreen.route +
+ "?noteId={noteId}¬eColor={noteColor}",
+ arguments = listOf(
+ navArgument(
+ name = "noteId"
+ ) {
+ type = NavType.IntType
+ defaultValue = -1
+ },
+ navArgument(
+ name = "noteColor"
+ ) {
+ type = NavType.IntType
+ defaultValue = -1
+ },
+ )
+ ) {
+ val color = it.arguments?.getInt("noteColor") ?: -1
+ AddEditNoteScreen(
+ navController = navController,
+ noteColor = color
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/add_edit_note/AddEditNoteEvent.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/add_edit_note/AddEditNoteEvent.kt
new file mode 100644
index 00000000..84c80676
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/add_edit_note/AddEditNoteEvent.kt
@@ -0,0 +1,13 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.add_edit_note
+
+import androidx.compose.ui.focus.FocusState
+
+sealed class AddEditNoteEvent{
+ data class EnteredTitle(val value: String): AddEditNoteEvent()
+ data class ChangeTitleFocus(val focusState: FocusState): AddEditNoteEvent()
+ data class EnteredContent(val value: String): AddEditNoteEvent()
+ data class ChangeContentFocus(val focusState: FocusState): AddEditNoteEvent()
+ data class ChangeColor(val color: Int): AddEditNoteEvent()
+ object SaveNote: AddEditNoteEvent()
+}
+
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/add_edit_note/AddEditNoteScreen.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/add_edit_note/AddEditNoteScreen.kt
new file mode 100644
index 00000000..a8542ce0
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/add_edit_note/AddEditNoteScreen.kt
@@ -0,0 +1,147 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.add_edit_note
+
+import androidx.compose.animation.Animatable
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material3.*
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Save
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+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.graphics.toArgb
+import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.navigation.NavController
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.model.Note
+import com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.add_edit_note.components.TransparentHintTextField
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
+
+@Composable
+fun AddEditNoteScreen(
+ navController: NavController,
+ noteColor: Int,
+ viewModel: AddEditNoteViewModel = hiltViewModel()
+) {
+ val titleState = viewModel.noteTitle.value
+ val contentState = viewModel.noteContent.value
+
+ val snackbarHostState = remember { SnackbarHostState() }
+
+ val noteBackgroundAnimatable = remember {
+ Animatable(
+ Color(if (noteColor != -1) noteColor else viewModel.noteColor.value)
+ )
+ }
+ val scope = rememberCoroutineScope()
+
+ LaunchedEffect(key1 = true) {
+ viewModel.eventFlow.collectLatest { event ->
+ when(event) {
+ is AddEditNoteViewModel.UiEvent.ShowSnackbar -> {
+ snackbarHostState.showSnackbar(
+ message = event.message
+ )
+ }
+ is AddEditNoteViewModel.UiEvent.SaveNote -> {
+ navController.navigateUp()
+ }
+ }
+ }
+ }
+
+ Scaffold(
+ snackbarHost = { SnackbarHost(snackbarHostState) },
+ floatingActionButton = {
+ FloatingActionButton(
+ onClick = {
+ viewModel.onEvent(AddEditNoteEvent.SaveNote)
+ },
+ containerColor = MaterialTheme.colorScheme.primary
+ ) {
+ Icon(imageVector = Icons.Default.Save, contentDescription = "Save note")
+ }
+ }
+ ) { paddingValues ->
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(noteBackgroundAnimatable.value)
+ .padding(paddingValues)
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(8.dp),
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Note.noteColors.forEach { color ->
+ val colorInt = color.toArgb()
+ Box(
+ modifier = Modifier
+ .size(50.dp)
+ .shadow(15.dp, CircleShape)
+ .clip(CircleShape)
+ .background(color)
+ .border(
+ width = 3.dp,
+ color = if (viewModel.noteColor.value == colorInt) {
+ Color.Black
+ } else Color.Transparent,
+ shape = CircleShape
+ )
+ .clickable {
+ scope.launch {
+ noteBackgroundAnimatable.animateTo(
+ targetValue = Color(colorInt),
+ animationSpec = tween(
+ durationMillis = 500
+ )
+ )
+ }
+ viewModel.onEvent(AddEditNoteEvent.ChangeColor(colorInt))
+ }
+ )
+ }
+ }
+ Spacer(modifier = Modifier.height(16.dp))
+ TransparentHintTextField(
+ text = titleState.text,
+ hint = titleState.hint,
+ onValueChange = {
+ viewModel.onEvent(AddEditNoteEvent.EnteredTitle(it))
+ },
+ onFocusChange = {
+ viewModel.onEvent(AddEditNoteEvent.ChangeTitleFocus(it))
+ },
+ isHintVisible = titleState.isHintVisible,
+ singleLine = true,
+ textStyle = MaterialTheme.typography.headlineSmall
+ )
+ Spacer(modifier = Modifier.height(16.dp))
+ TransparentHintTextField(
+ text = contentState.text,
+ hint = contentState.hint,
+ onValueChange = {
+ viewModel.onEvent(AddEditNoteEvent.EnteredContent(it))
+ },
+ onFocusChange = {
+ viewModel.onEvent(AddEditNoteEvent.ChangeContentFocus(it))
+ },
+ isHintVisible = contentState.isHintVisible,
+ textStyle = MaterialTheme.typography.bodyLarge,
+ modifier = Modifier.fillMaxHeight()
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/add_edit_note/AddEditNoteViewModel.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/add_edit_note/AddEditNoteViewModel.kt
new file mode 100644
index 00000000..147aecf4
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/add_edit_note/AddEditNoteViewModel.kt
@@ -0,0 +1,119 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.add_edit_note
+
+import androidx.compose.runtime.State
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.graphics.toArgb
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.model.InvalidNoteException
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.model.Note
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.use_case.NoteUseCases
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class AddEditNoteViewModel @Inject constructor(
+ private val noteUseCases: NoteUseCases,
+ savedStateHandle: SavedStateHandle
+) : ViewModel() {
+
+ private val _noteTitle = mutableStateOf(NoteTextFieldState(
+ hint = "Enter title..."
+ ))
+ val noteTitle: State = _noteTitle
+
+ private val _noteContent = mutableStateOf(NoteTextFieldState(
+ hint = "Enter some content"
+ ))
+ val noteContent: State = _noteContent
+
+ private val _noteColor = mutableStateOf(Note.noteColors.random().toArgb())
+ val noteColor: State = _noteColor
+
+ private val _eventFlow = MutableSharedFlow()
+ val eventFlow = _eventFlow.asSharedFlow()
+
+ private var currentNoteId: Int? = null
+
+ init {
+ savedStateHandle.get("noteId")?.let { noteId ->
+ if(noteId != -1) {
+ viewModelScope.launch {
+ noteUseCases.getNote(noteId)?.also { note ->
+ currentNoteId = note.id
+ _noteTitle.value = noteTitle.value.copy(
+ text = note.title,
+ isHintVisible = false
+ )
+ _noteContent.value = _noteContent.value.copy(
+ text = note.content,
+ isHintVisible = false
+ )
+ _noteColor.value = note.color
+ }
+ }
+ }
+ }
+ }
+
+ fun onEvent(event: AddEditNoteEvent) {
+ when(event) {
+ is AddEditNoteEvent.EnteredTitle -> {
+ _noteTitle.value = noteTitle.value.copy(
+ text = event.value
+ )
+ }
+ is AddEditNoteEvent.ChangeTitleFocus -> {
+ _noteTitle.value = noteTitle.value.copy(
+ isHintVisible = !event.focusState.isFocused &&
+ noteTitle.value.text.isBlank()
+ )
+ }
+ is AddEditNoteEvent.EnteredContent -> {
+ _noteContent.value = _noteContent.value.copy(
+ text = event.value
+ )
+ }
+ is AddEditNoteEvent.ChangeContentFocus -> {
+ _noteContent.value = _noteContent.value.copy(
+ isHintVisible = !event.focusState.isFocused &&
+ _noteContent.value.text.isBlank()
+ )
+ }
+ is AddEditNoteEvent.ChangeColor -> {
+ _noteColor.value = event.color
+ }
+ is AddEditNoteEvent.SaveNote -> {
+ viewModelScope.launch {
+ try {
+ noteUseCases.addNote(
+ Note(
+ title = noteTitle.value.text,
+ content = noteContent.value.text,
+ timestamp = System.currentTimeMillis(),
+ color = noteColor.value,
+ id = currentNoteId
+ )
+ )
+ _eventFlow.emit(UiEvent.SaveNote)
+ } catch(e: InvalidNoteException) {
+ _eventFlow.emit(
+ UiEvent.ShowSnackbar(
+ message = e.message ?: "Couldn't save note"
+ )
+ )
+ }
+ }
+ }
+ }
+ }
+
+ sealed class UiEvent {
+ data class ShowSnackbar(val message: String): UiEvent()
+ object SaveNote: UiEvent()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/add_edit_note/NoteTextFieldState.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/add_edit_note/NoteTextFieldState.kt
new file mode 100644
index 00000000..6a488bca
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/add_edit_note/NoteTextFieldState.kt
@@ -0,0 +1,7 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.add_edit_note
+
+data class NoteTextFieldState(
+ val text: String = "",
+ val hint: String = "",
+ val isHintVisible: Boolean = true
+)
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/add_edit_note/components/TransparentHintTextField.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/add_edit_note/components/TransparentHintTextField.kt
new file mode 100644
index 00000000..77f262e1
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/add_edit_note/components/TransparentHintTextField.kt
@@ -0,0 +1,43 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.add_edit_note.components
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusState
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.TextStyle
+
+@Composable
+fun TransparentHintTextField(
+ text: String,
+ hint: String,
+ modifier: Modifier = Modifier,
+ isHintVisible: Boolean = true,
+ onValueChange: (String) -> Unit,
+ textStyle: TextStyle = TextStyle(),
+ singleLine: Boolean = false,
+ onFocusChange: (FocusState) -> Unit
+) {
+ Box(
+ modifier = modifier
+ ) {
+ BasicTextField(
+ value = text,
+ onValueChange = onValueChange,
+ singleLine = singleLine,
+ textStyle = textStyle,
+ modifier = Modifier
+ .fillMaxWidth()
+ .onFocusChanged {
+ onFocusChange(it)
+ }
+ )
+ if(isHintVisible) {
+ Text(text = hint, style = textStyle, color = Color.DarkGray)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/NotesEvent.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/NotesEvent.kt
new file mode 100644
index 00000000..624c0634
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/NotesEvent.kt
@@ -0,0 +1,11 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.notes
+
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.model.Note
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.util.NoteOrder
+
+sealed class NotesEvent {
+ data class Order(val noteOrder: NoteOrder): NotesEvent()
+ data class DeleteNote(val note: Note): NotesEvent()
+ object RestoreNote: NotesEvent()
+ object ToggleOrderSection: NotesEvent()
+}
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/NotesScreen.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/NotesScreen.kt
new file mode 100644
index 00000000..08a547f5
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/NotesScreen.kt
@@ -0,0 +1,119 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.notes
+
+import androidx.compose.animation.*
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material3.*
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Sort
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.navigation.NavController
+import com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.notes.components.NoteItem
+import com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.notes.components.OrderSection
+import com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.util.Screen
+import kotlinx.coroutines.launch
+
+@ExperimentalAnimationApi
+@Composable
+fun NotesScreen(
+ navController: NavController,
+ viewModel: NotesViewModel = hiltViewModel()
+) {
+ val state = viewModel.state.value
+ val snackbarHostState = remember { SnackbarHostState() }
+ val scope = rememberCoroutineScope()
+
+ Scaffold(
+ snackbarHost = { SnackbarHost(snackbarHostState) },
+ floatingActionButton = {
+ FloatingActionButton(
+ onClick = {
+ navController.navigate(Screen.AddEditNoteScreen.route)
+ },
+ containerColor = MaterialTheme.colorScheme.primary
+ ) {
+ Icon(imageVector = Icons.Default.Add, contentDescription = "Add note")
+ }
+ }
+ ) { paddingValues ->
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(paddingValues)
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = "Your note",
+ style = MaterialTheme.typography.headlineMedium
+ )
+ IconButton(
+ onClick = {
+ viewModel.onEvent(NotesEvent.ToggleOrderSection)
+ },
+ ) {
+ Icon(
+ imageVector = Icons.Default.Sort,
+ contentDescription = "Sort"
+ )
+ }
+ }
+ AnimatedVisibility(
+ visible = state.isOrderSectionVisible,
+ enter = fadeIn() + slideInVertically(),
+ exit = fadeOut() + slideOutVertically()
+ ) {
+ OrderSection(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = 16.dp),
+ noteOrder = state.noteOrder,
+ onOrderChange = {
+ viewModel.onEvent(NotesEvent.Order(it))
+ }
+ )
+ }
+ Spacer(modifier = Modifier.height(16.dp))
+ LazyColumn(modifier = Modifier.fillMaxSize()) {
+ items(state.notes) { note ->
+ NoteItem(
+ note = note,
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable {
+ navController.navigate(
+ Screen.AddEditNoteScreen.route +
+ "?noteId=${note.id}¬eColor=${note.color}"
+ )
+ },
+ onDeleteClick = {
+ viewModel.onEvent(NotesEvent.DeleteNote(note))
+ scope.launch {
+ val result = snackbarHostState.showSnackbar(
+ message = "Note deleted",
+ actionLabel = "Undo"
+ )
+ if(result == SnackbarResult.ActionPerformed) {
+ viewModel.onEvent(NotesEvent.RestoreNote)
+ }
+ }
+ }
+ )
+ Spacer(modifier = Modifier.height(16.dp))
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/NotesState.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/NotesState.kt
new file mode 100644
index 00000000..d85bfdc2
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/NotesState.kt
@@ -0,0 +1,11 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.notes
+
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.model.Note
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.util.NoteOrder
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.util.OrderType
+
+data class NotesState(
+ val notes: List = emptyList(),
+ val noteOrder: NoteOrder = NoteOrder.Date(OrderType.Descending),
+ val isOrderSectionVisible: Boolean = false
+)
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/NotesViewModel.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/NotesViewModel.kt
new file mode 100644
index 00000000..bd11c8d6
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/NotesViewModel.kt
@@ -0,0 +1,75 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.notes
+
+import androidx.compose.runtime.State
+import androidx.compose.runtime.mutableStateOf
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.model.Note
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.use_case.NoteUseCases
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.util.NoteOrder
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.util.OrderType
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class NotesViewModel @Inject constructor(
+ private val noteUseCases: NoteUseCases
+) : ViewModel() {
+
+ private val _state = mutableStateOf(NotesState())
+ val state: State = _state
+
+ private var recentlyDeletedNote: Note? = null
+
+ private var getNotesJob: Job? = null
+
+ init {
+ getNotes(NoteOrder.Date(OrderType.Descending))
+ }
+
+ fun onEvent(event: NotesEvent) {
+ when (event) {
+ is NotesEvent.Order -> {
+ if (state.value.noteOrder::class == event.noteOrder::class &&
+ state.value.noteOrder.orderType == event.noteOrder.orderType
+ ) {
+ return
+ }
+ getNotes(event.noteOrder)
+ }
+ is NotesEvent.DeleteNote -> {
+ viewModelScope.launch {
+ noteUseCases.deleteNote(event.note)
+ recentlyDeletedNote = event.note
+ }
+ }
+ is NotesEvent.RestoreNote -> {
+ viewModelScope.launch {
+ noteUseCases.addNote(recentlyDeletedNote ?: return@launch)
+ recentlyDeletedNote = null
+ }
+ }
+ is NotesEvent.ToggleOrderSection -> {
+ _state.value = state.value.copy(
+ isOrderSectionVisible = !state.value.isOrderSectionVisible
+ )
+ }
+ }
+ }
+
+ private fun getNotes(noteOrder: NoteOrder) {
+ getNotesJob?.cancel()
+ getNotesJob = noteUseCases.getNotes(noteOrder)
+ .onEach { notes ->
+ _state.value = state.value.copy(
+ notes = notes,
+ noteOrder = noteOrder
+ )
+ }
+ .launchIn(viewModelScope)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/components/DefaultRadioButton.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/components/DefaultRadioButton.kt
new file mode 100644
index 00000000..feb257e8
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/components/DefaultRadioButton.kt
@@ -0,0 +1,37 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.notes.components
+
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.RadioButton
+import androidx.compose.material3.RadioButtonDefaults
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun DefaultRadioButton(
+ text: String,
+ selected: Boolean,
+ onSelect: () -> Unit,
+ modifier: Modifier = Modifier
+) {
+ Row(
+ modifier = modifier,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ RadioButton(
+ selected = selected,
+ onClick = onSelect,
+ colors = RadioButtonDefaults.colors(
+ selectedColor = MaterialTheme.colorScheme.primary,
+ unselectedColor = MaterialTheme.colorScheme.onBackground
+ )
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ Text(text = text, style = MaterialTheme.typography.bodyLarge)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/components/NoteItem.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/components/NoteItem.kt
new file mode 100644
index 00000000..790d0af4
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/components/NoteItem.kt
@@ -0,0 +1,95 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.notes.components
+
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.*
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Delete
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.drawscope.clipPath
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.core.graphics.ColorUtils
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.model.Note
+
+@Composable
+fun NoteItem(
+ note: Note,
+ modifier: Modifier = Modifier,
+ cornerRadius: Dp = 10.dp,
+ cutCornerSize: Dp = 30.dp,
+ onDeleteClick: () -> Unit
+) {
+ Box(
+ modifier = modifier
+ ) {
+ Canvas(modifier = Modifier.matchParentSize()) {
+ val clipPath = Path().apply {
+ lineTo(size.width - cutCornerSize.toPx(), 0f)
+ lineTo(size.width, cutCornerSize.toPx())
+ lineTo(size.width, size.height)
+ lineTo(0f, size.height)
+ close()
+ }
+
+ clipPath(clipPath) {
+ drawRoundRect(
+ color = Color(note.color),
+ size = size,
+ cornerRadius = CornerRadius(cornerRadius.toPx())
+ )
+ drawRoundRect(
+ color = Color(
+ ColorUtils.blendARGB(note.color, 0x000000, 0.2f)
+ ),
+ topLeft = Offset(size.width - cutCornerSize.toPx(), -100f),
+ size = Size(cutCornerSize.toPx() + 100f, cutCornerSize.toPx() + 100f),
+ cornerRadius = CornerRadius(cornerRadius.toPx())
+ )
+ }
+ }
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(16.dp)
+ .padding(end = 32.dp)
+ ) {
+ Text(
+ text = note.title,
+ style = MaterialTheme.typography.headlineSmall,
+ color = MaterialTheme.colorScheme.onSurface,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ Spacer(modifier = Modifier.height(8.dp))
+ Text(
+ text = note.content,
+ style = MaterialTheme.typography.bodyLarge,
+ color = MaterialTheme.colorScheme.onSurface,
+ maxLines = 10,
+ overflow = TextOverflow.Ellipsis
+ )
+ }
+ IconButton(
+ onClick = onDeleteClick,
+ modifier = Modifier.align(Alignment.BottomEnd)
+ ) {
+ Icon(
+ imageVector = Icons.Default.Delete,
+ contentDescription = "Delete note",
+ tint = MaterialTheme.colorScheme.onSurface
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/components/OrderSection.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/components/OrderSection.kt
new file mode 100644
index 00000000..0fdc3634
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/notes/components/OrderSection.kt
@@ -0,0 +1,61 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.notes.components
+
+import androidx.compose.foundation.layout.*
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.util.NoteOrder
+import com.plcoding.cleanarchitecturenoteapp.feature_note.domain.util.OrderType
+
+@Composable
+fun OrderSection(
+ modifier: Modifier = Modifier,
+ noteOrder: NoteOrder = NoteOrder.Date(OrderType.Descending),
+ onOrderChange: (NoteOrder) -> Unit
+) {
+ Column(
+ modifier = modifier
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ DefaultRadioButton(
+ text = "Title",
+ selected = noteOrder is NoteOrder.Title,
+ onSelect = { onOrderChange(NoteOrder.Title(noteOrder.orderType)) }
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ DefaultRadioButton(
+ text = "Date",
+ selected = noteOrder is NoteOrder.Date,
+ onSelect = { onOrderChange(NoteOrder.Date(noteOrder.orderType)) }
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ DefaultRadioButton(
+ text = "Color",
+ selected = noteOrder is NoteOrder.Color,
+ onSelect = { onOrderChange(NoteOrder.Color(noteOrder.orderType)) }
+ )
+ }
+ Spacer(modifier = Modifier.height(16.dp))
+ Row(
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ DefaultRadioButton(
+ text = "Ascending",
+ selected = noteOrder.orderType is OrderType.Ascending,
+ onSelect = {
+ onOrderChange(noteOrder.copy(OrderType.Ascending))
+ }
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ DefaultRadioButton(
+ text = "Descending",
+ selected = noteOrder.orderType is OrderType.Descending,
+ onSelect = {
+ onOrderChange(noteOrder.copy(OrderType.Descending))
+ }
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/util/Screen.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/util/Screen.kt
new file mode 100644
index 00000000..9a0e1d1e
--- /dev/null
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/feature_note/presentation/util/Screen.kt
@@ -0,0 +1,6 @@
+package com.plcoding.cleanarchitecturenoteapp.feature_note.presentation.util
+
+sealed class Screen(val route: String) {
+ object NotesScreen: Screen("notes_screen")
+ object AddEditNoteScreen: Screen("add_edit_note_screen")
+}
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Shape.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Shape.kt
index 3a70048a..1f872ff6 100644
--- a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Shape.kt
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Shape.kt
@@ -1,7 +1,7 @@
package com.plcoding.cleanarchitecturenoteapp.ui.theme
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.Shapes
+import androidx.compose.material3.Shapes
import androidx.compose.ui.unit.dp
val Shapes = Shapes(
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Theme.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Theme.kt
index 24a1508f..2c6e1c67 100644
--- a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Theme.kt
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Theme.kt
@@ -1,11 +1,11 @@
package com.plcoding.cleanarchitecturenoteapp.ui.theme
-import androidx.compose.material.MaterialTheme
-import androidx.compose.material.darkColors
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
-private val DarkColorPalette = darkColors(
+private val DarkColorPalette = darkColorScheme(
primary = Color.White,
background = DarkGray,
onBackground = Color.White,
@@ -16,7 +16,7 @@ private val DarkColorPalette = darkColors(
@Composable
fun CleanArchitectureNoteAppTheme(darkTheme: Boolean = true, content: @Composable() () -> Unit) {
MaterialTheme(
- colors = DarkColorPalette,
+ colorScheme = DarkColorPalette,
typography = Typography,
shapes = Shapes,
content = content
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Type.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Type.kt
index 4db73a88..e4befb58 100644
--- a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Type.kt
+++ b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Type.kt
@@ -1,6 +1,6 @@
package com.plcoding.cleanarchitecturenoteapp.ui.theme
-import androidx.compose.material.Typography
+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
@@ -8,7 +8,7 @@ import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
- body1 = TextStyle(
+ bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
deleted file mode 100644
index 423caff1..00000000
--- a/app/src/main/res/values-night/themes.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index c2739918..3d0dccbe 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -1,26 +1,3 @@
-
-
-
-
-
-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
deleted file mode 100644
index 009f9c11..00000000
--- a/build.gradle
+++ /dev/null
@@ -1,22 +0,0 @@
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
-buildscript {
- ext {
- compose_version = '1.0.2'
- }
- repositories {
- google()
- mavenCentral()
- }
- dependencies {
- classpath "com.android.tools.build:gradle:7.0.2"
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21"
- classpath "com.google.dagger:hilt-android-gradle-plugin:2.38.1"
-
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
- }
-}
-
-task clean(type: Delete) {
- delete rootProject.buildDir
-}
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 00000000..6735ebdd
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,7 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ alias(libs.plugins.android.application) apply false
+ alias(libs.plugins.kotlin.android) apply false
+ alias(libs.plugins.kotlin.compose) apply false
+ id("com.google.dagger.hilt.android") version "2.49" apply false
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 98bed167..65d0af8a 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -18,4 +18,5 @@ android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
-kotlin.code.style=official
\ No newline at end of file
+kotlin.code.style=official
+kapt.use.worker.api=false
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100644
index 00000000..133a0247
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,32 @@
+[versions]
+agp = "8.7.2"
+kotlin = "2.0.10"
+coreKtx = "1.15.0"
+junit = "4.13.2"
+junitVersion = "1.2.1"
+espressoCore = "3.6.1"
+lifecycleRuntimeKtx = "2.8.7"
+activityCompose = "1.9.3"
+composeBom = "2024.10.01"
+
+[libraries]
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
+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" }
+androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
+androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
+androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
+androidx-ui = { group = "androidx.compose.ui", name = "ui" }
+androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
+androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
+androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
+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" }
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "agp" }
+kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
+
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index c56e3cf4..3074de35 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Fri Sep 17 20:48:52 CEST 2021
+#Tue Nov 12 23:45:49 TRT 2024
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
distributionPath=wrapper/dists
-zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/settings.gradle b/settings.gradle
deleted file mode 100644
index 5ce93cce..00000000
--- a/settings.gradle
+++ /dev/null
@@ -1,10 +0,0 @@
-dependencyResolutionManagement {
- repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
- repositories {
- google()
- mavenCentral()
- jcenter() // Warning: this repository is going to shut down soon
- }
-}
-rootProject.name = "CleanArchitectureNoteApp"
-include ':app'
diff --git a/settings.gradle.kts b/settings.gradle.kts
new file mode 100644
index 00000000..26b99f65
--- /dev/null
+++ b/settings.gradle.kts
@@ -0,0 +1,23 @@
+pluginManagement {
+ repositories {
+ google {
+ content {
+ includeGroupByRegex("com\\.android.*")
+ includeGroupByRegex("com\\.google.*")
+ includeGroupByRegex("androidx.*")
+ }
+ }
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.name = "CleanArchitectureNoteApp"
+include(":app")
\ No newline at end of file