diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 00000000..c968170e
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+CleanNoteApp
\ No newline at end of file
diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml
new file mode 100644
index 00000000..4a53bee8
--- /dev/null
+++ b/.idea/AndroidProjectSystem.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index fb7f4a8a..b589d56e 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
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..8493627b
--- /dev/null
+++ b/.idea/deploymentTargetSelector.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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..a269cb1a
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 00000000..5815a4a6
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/migrations.xml b/.idea/migrations.xml
new file mode 100644
index 00000000..f8051a6f
--- /dev/null
+++ b/.idea/migrations.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 860da66a..0ad17cbd 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,6 +1,7 @@
-
+
+
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/README.md b/README.md
new file mode 100644
index 00000000..5fee77e4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,6 @@
+
+# JetCleanNote
+
+App - Light | App-Dark | Add Note Screen
+:-------------------------:|:-------------------------:|:-------------------------:
+ |  | 
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..bc76a4f2
--- /dev/null
+++ b/app/build.gradle.kts
@@ -0,0 +1,85 @@
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.kotlin.compose)
+ id("com.google.devtools.ksp")
+ id("com.google.dagger.hilt.android")
+}
+
+android {
+ namespace = "com.ibrahimcanerdogan.cleannote"
+ compileSdk = 35
+
+ defaultConfig {
+ applicationId = "com.ibrahimcanerdogan.cleannote"
+ 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.9.1")
+ implementation("androidx.navigation:navigation-compose:2.9.0")
+ implementation("androidx.compose.material:material-icons-extended:1.7.8")
+ implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
+
+ // Coroutines
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2")
+
+ //Dagger - Hilt
+ implementation("com.google.dagger:hilt-android:2.49")
+ ksp("com.google.dagger:hilt-android-compiler:2.49")
+ ksp("androidx.hilt:hilt-compiler:1.0.0")
+
+ // Room
+ implementation("androidx.room:room-runtime:2.7.1")
+ ksp("androidx.room:room-compiler:2.7.1")
+ implementation("androidx.room:room-ktx:2.7.1")
+
+ // Splash Screen
+ implementation("androidx.core:core-splashscreen:1.0.0")
+
+}
\ 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/androidTest/java/com/plcoding/cleanarchitecturenoteapp/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/ibrahimcanerdogan/cleannote/ExampleInstrumentedTest.kt
similarity index 81%
rename from app/src/androidTest/java/com/plcoding/cleanarchitecturenoteapp/ExampleInstrumentedTest.kt
rename to app/src/androidTest/java/com/ibrahimcanerdogan/cleannote/ExampleInstrumentedTest.kt
index 19411d6c..d0b17737 100644
--- a/app/src/androidTest/java/com/plcoding/cleanarchitecturenoteapp/ExampleInstrumentedTest.kt
+++ b/app/src/androidTest/java/com/ibrahimcanerdogan/cleannote/ExampleInstrumentedTest.kt
@@ -1,4 +1,4 @@
-package com.plcoding.cleanarchitecturenoteapp
+package com.ibrahimcanerdogan.cleannote
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -19,6 +19,6 @@ class ExampleInstrumentedTest {
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("com.plcoding.cleanarchitecturenoteapp", appContext.packageName)
+ assertEquals("com.ibrahimcanerdogan.cleannote", appContext.packageName)
}
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 052e8301..d365a59a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,19 +1,20 @@
+ package="com.ibrahimcanerdogan.cleannote">
+ android:theme="@style/Theme.CleanNoteApp">
+ android:theme="@style/Theme.CleanNoteApp">
diff --git a/app/src/main/icon_launcher-playstore.png b/app/src/main/icon_launcher-playstore.png
new file mode 100644
index 00000000..94d62423
Binary files /dev/null and b/app/src/main/icon_launcher-playstore.png differ
diff --git a/app/src/main/java/com/ibrahimcanerdogan/cleannote/MainActivity.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/MainActivity.kt
new file mode 100644
index 00000000..69e0cccc
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/MainActivity.kt
@@ -0,0 +1,29 @@
+package com.ibrahimcanerdogan.cleannote
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.material3.Surface
+import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
+import com.ibrahimcanerdogan.cleannote.ui.navigation.NoteNavigation
+import com.ibrahimcanerdogan.cleannote.ui.theme.CleanNoteAppTheme
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class MainActivity : ComponentActivity() {
+ @ExperimentalAnimationApi
+ override fun onCreate(savedInstanceState: Bundle?) {
+ installSplashScreen()
+ super.onCreate(savedInstanceState)
+ enableEdgeToEdge()
+ setContent {
+ CleanNoteAppTheme {
+ Surface {
+ NoteNavigation()
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/ibrahimcanerdogan/cleannote/NoteApp.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/NoteApp.kt
new file mode 100644
index 00000000..47d5733f
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/NoteApp.kt
@@ -0,0 +1,7 @@
+package com.ibrahimcanerdogan.cleannote
+
+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/ibrahimcanerdogan/cleannote/data/database/NoteDao.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/data/database/NoteDao.kt
new file mode 100644
index 00000000..f37e20a4
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/data/database/NoteDao.kt
@@ -0,0 +1,21 @@
+package com.ibrahimcanerdogan.cleannote.data.database
+
+import androidx.room.*
+import com.ibrahimcanerdogan.cleannote.data.model.Note
+import kotlinx.coroutines.flow.Flow
+
+@Dao
+interface NoteDao {
+
+ @Query("SELECT * FROM note")
+ fun getAllNoteDatabase(): Flow>
+
+ @Query("SELECT * FROM note WHERE id = :id")
+ suspend fun getSingleNoteDatabase(id: Int): Note?
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun insertNoteDatabase(note: Note)
+
+ @Delete
+ suspend fun deleteNoteDatabase(note: Note)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ibrahimcanerdogan/cleannote/data/database/NoteDatabase.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/data/database/NoteDatabase.kt
new file mode 100644
index 00000000..b850825c
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/data/database/NoteDatabase.kt
@@ -0,0 +1,19 @@
+package com.ibrahimcanerdogan.cleannote.data.database
+
+import androidx.room.Database
+import androidx.room.RoomDatabase
+import com.ibrahimcanerdogan.cleannote.data.model.Note
+
+@Database(
+ entities = [Note::class],
+ version = 1,
+ exportSchema = false
+)
+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/ibrahimcanerdogan/cleannote/data/model/Note.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/data/model/Note.kt
new file mode 100644
index 00000000..4ad952f3
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/data/model/Note.kt
@@ -0,0 +1,20 @@
+package com.ibrahimcanerdogan.cleannote.data.model
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.ibrahimcanerdogan.cleannote.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/ibrahimcanerdogan/cleannote/data/repository/NoteRepositoryImpl.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/data/repository/NoteRepositoryImpl.kt
new file mode 100644
index 00000000..7f2f46f7
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/data/repository/NoteRepositoryImpl.kt
@@ -0,0 +1,27 @@
+package com.ibrahimcanerdogan.cleannote.data.repository
+
+import com.ibrahimcanerdogan.cleannote.data.database.NoteDao
+import com.ibrahimcanerdogan.cleannote.data.model.Note
+import com.ibrahimcanerdogan.cleannote.domain.repository.NoteRepository
+import kotlinx.coroutines.flow.Flow
+
+class NoteRepositoryImpl(
+ private val noteDao: NoteDao
+) : NoteRepository {
+
+ override fun getAllNoteRepository(): Flow> {
+ return noteDao.getAllNoteDatabase()
+ }
+
+ override suspend fun getSingleNoteRepository(id: Int): Note? {
+ return noteDao.getSingleNoteDatabase(id)
+ }
+
+ override suspend fun insertNoteRepository(note: Note) {
+ noteDao.insertNoteDatabase(note)
+ }
+
+ override suspend fun deleteNoteRepository(note: Note) {
+ noteDao.deleteNoteDatabase(note)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ibrahimcanerdogan/cleannote/di/DatabaseModule.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/di/DatabaseModule.kt
new file mode 100644
index 00000000..8f3aafe6
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/di/DatabaseModule.kt
@@ -0,0 +1,26 @@
+package com.ibrahimcanerdogan.cleannote.di
+
+import android.content.Context
+import androidx.room.Room
+import com.ibrahimcanerdogan.cleannote.data.database.NoteDatabase
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.qualifiers.ApplicationContext
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object DatabaseModule {
+
+ @Provides
+ @Singleton
+ fun provideNoteDatabase(@ApplicationContext context: Context): NoteDatabase {
+ return Room.databaseBuilder(
+ context,
+ NoteDatabase::class.java,
+ NoteDatabase.DATABASE_NAME
+ ).build()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ibrahimcanerdogan/cleannote/di/RepositoryModule.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/di/RepositoryModule.kt
new file mode 100644
index 00000000..d7d8b933
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/di/RepositoryModule.kt
@@ -0,0 +1,21 @@
+package com.ibrahimcanerdogan.cleannote.di
+
+import com.ibrahimcanerdogan.cleannote.data.database.NoteDatabase
+import com.ibrahimcanerdogan.cleannote.data.repository.NoteRepositoryImpl
+import com.ibrahimcanerdogan.cleannote.domain.repository.NoteRepository
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object RepositoryModule {
+
+ @Provides
+ @Singleton
+ fun provideNoteRepository(noteDatabase: NoteDatabase): NoteRepository {
+ return NoteRepositoryImpl(noteDatabase.noteDao)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ibrahimcanerdogan/cleannote/di/UseCaseModule.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/di/UseCaseModule.kt
new file mode 100644
index 00000000..4965a76b
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/di/UseCaseModule.kt
@@ -0,0 +1,29 @@
+package com.ibrahimcanerdogan.cleannote.di
+
+import com.ibrahimcanerdogan.cleannote.domain.repository.NoteRepository
+import com.ibrahimcanerdogan.cleannote.domain.usecase.AddNote
+import com.ibrahimcanerdogan.cleannote.domain.usecase.DeleteNote
+import com.ibrahimcanerdogan.cleannote.domain.usecase.GetNote
+import com.ibrahimcanerdogan.cleannote.domain.usecase.GetNotes
+import com.ibrahimcanerdogan.cleannote.domain.usecase.NoteUseCase
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object UseCaseModule {
+
+ @Provides
+ @Singleton
+ fun provideNoteUseCases(repository: NoteRepository): NoteUseCase {
+ return NoteUseCase(
+ 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/ibrahimcanerdogan/cleannote/domain/repository/NoteRepository.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/repository/NoteRepository.kt
new file mode 100644
index 00000000..1ac7b8b6
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/repository/NoteRepository.kt
@@ -0,0 +1,15 @@
+package com.ibrahimcanerdogan.cleannote.domain.repository
+
+import com.ibrahimcanerdogan.cleannote.data.model.Note
+import kotlinx.coroutines.flow.Flow
+
+interface NoteRepository {
+
+ fun getAllNoteRepository(): Flow>
+
+ suspend fun getSingleNoteRepository(id: Int): Note?
+
+ suspend fun insertNoteRepository(note: Note)
+
+ suspend fun deleteNoteRepository(note: Note)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/usecase/AddNote.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/usecase/AddNote.kt
new file mode 100644
index 00000000..a296ee49
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/usecase/AddNote.kt
@@ -0,0 +1,22 @@
+package com.ibrahimcanerdogan.cleannote.domain.usecase
+
+import com.ibrahimcanerdogan.cleannote.data.model.InvalidNoteException
+import com.ibrahimcanerdogan.cleannote.data.model.Note
+import com.ibrahimcanerdogan.cleannote.domain.repository.NoteRepository
+import javax.inject.Inject
+
+class AddNote @Inject constructor(
+ 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.insertNoteRepository(note)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/usecase/DeleteNote.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/usecase/DeleteNote.kt
new file mode 100644
index 00000000..67f7c626
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/usecase/DeleteNote.kt
@@ -0,0 +1,14 @@
+package com.ibrahimcanerdogan.cleannote.domain.usecase
+
+import com.ibrahimcanerdogan.cleannote.data.model.Note
+import com.ibrahimcanerdogan.cleannote.domain.repository.NoteRepository
+import javax.inject.Inject
+
+class DeleteNote @Inject constructor(
+ private val repository: NoteRepository
+) {
+
+ suspend operator fun invoke(note: Note) {
+ repository.deleteNoteRepository(note)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/usecase/GetNote.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/usecase/GetNote.kt
new file mode 100644
index 00000000..e6fb60b2
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/usecase/GetNote.kt
@@ -0,0 +1,14 @@
+package com.ibrahimcanerdogan.cleannote.domain.usecase
+
+import com.ibrahimcanerdogan.cleannote.data.model.Note
+import com.ibrahimcanerdogan.cleannote.domain.repository.NoteRepository
+import javax.inject.Inject
+
+class GetNote @Inject constructor(
+ private val repository: NoteRepository
+) {
+
+ suspend operator fun invoke(id: Int): Note? {
+ return repository.getSingleNoteRepository(id)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/usecase/GetNotes.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/usecase/GetNotes.kt
new file mode 100644
index 00000000..1f34bb9e
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/usecase/GetNotes.kt
@@ -0,0 +1,37 @@
+package com.ibrahimcanerdogan.cleannote.domain.usecase
+
+import com.ibrahimcanerdogan.cleannote.data.model.Note
+import com.ibrahimcanerdogan.cleannote.domain.repository.NoteRepository
+import com.ibrahimcanerdogan.cleannote.domain.util.NoteOrder
+import com.ibrahimcanerdogan.cleannote.domain.util.OrderType
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import javax.inject.Inject
+
+class GetNotes @Inject constructor(
+ private val repository: NoteRepository
+) {
+
+ operator fun invoke(
+ noteOrder: NoteOrder = NoteOrder.Date(OrderType.Descending)
+ ): Flow> {
+ return repository.getAllNoteRepository().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/ibrahimcanerdogan/cleannote/domain/usecase/NoteUseCase.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/usecase/NoteUseCase.kt
new file mode 100644
index 00000000..99cd5608
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/usecase/NoteUseCase.kt
@@ -0,0 +1,8 @@
+package com.ibrahimcanerdogan.cleannote.domain.usecase
+
+data class NoteUseCase(
+ val getNotes: GetNotes,
+ val deleteNote: DeleteNote,
+ val addNote: AddNote,
+ val getNote: GetNote
+)
diff --git a/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/util/NoteOrder.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/util/NoteOrder.kt
new file mode 100644
index 00000000..732abe55
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/util/NoteOrder.kt
@@ -0,0 +1,15 @@
+package com.ibrahimcanerdogan.cleannote.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/ibrahimcanerdogan/cleannote/domain/util/OrderType.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/util/OrderType.kt
new file mode 100644
index 00000000..6fdc0e83
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/domain/util/OrderType.kt
@@ -0,0 +1,6 @@
+package com.ibrahimcanerdogan.cleannote.domain.util
+
+sealed class OrderType {
+ data object Ascending: OrderType()
+ data object Descending: OrderType()
+}
diff --git a/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/component/DefaultRadioButton.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/component/DefaultRadioButton.kt
new file mode 100644
index 00000000..ed94208b
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/component/DefaultRadioButton.kt
@@ -0,0 +1,37 @@
+package com.ibrahimcanerdogan.cleannote.ui.component
+
+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/ibrahimcanerdogan/cleannote/ui/component/OrderSection.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/component/OrderSection.kt
new file mode 100644
index 00000000..e05fbd6f
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/component/OrderSection.kt
@@ -0,0 +1,61 @@
+package com.ibrahimcanerdogan.cleannote.ui.component
+
+import androidx.compose.foundation.layout.*
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.ibrahimcanerdogan.cleannote.domain.util.NoteOrder
+import com.ibrahimcanerdogan.cleannote.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/ibrahimcanerdogan/cleannote/ui/component/TransparentHintTextField.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/component/TransparentHintTextField.kt
new file mode 100644
index 00000000..a0888f60
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/component/TransparentHintTextField.kt
@@ -0,0 +1,43 @@
+package com.ibrahimcanerdogan.cleannote.ui.component
+
+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/ibrahimcanerdogan/cleannote/ui/navigation/NoteNavigation.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/navigation/NoteNavigation.kt
new file mode 100644
index 00000000..489cd349
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/navigation/NoteNavigation.kt
@@ -0,0 +1,48 @@
+package com.ibrahimcanerdogan.cleannote.ui.navigation
+
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+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.ibrahimcanerdogan.cleannote.ui.view.edit.AddEditNoteScreen
+import com.ibrahimcanerdogan.cleannote.ui.view.list.NoteListScreen
+
+@Composable
+fun NoteNavigation() {
+ val navController = rememberNavController()
+ NavHost(
+ navController = navController,
+ startDestination = NoteScreen.NotesScreen.route
+ ) {
+ composable(route = NoteScreen.NotesScreen.route) {
+ NoteListScreen(navController = navController)
+ }
+ composable(
+ route = NoteScreen.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
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/navigation/NoteScreen.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/navigation/NoteScreen.kt
new file mode 100644
index 00000000..e7a13c46
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/navigation/NoteScreen.kt
@@ -0,0 +1,6 @@
+package com.ibrahimcanerdogan.cleannote.ui.navigation
+
+sealed class NoteScreen(val route: String) {
+ data object NotesScreen: NoteScreen("notes_screen")
+ data object AddEditNoteScreen: NoteScreen("add_edit_note_screen")
+}
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Color.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/theme/Color.kt
similarity index 76%
rename from app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Color.kt
rename to app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/theme/Color.kt
index 9b76e750..bc534250 100644
--- a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Color.kt
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/theme/Color.kt
@@ -1,8 +1,9 @@
-package com.plcoding.cleanarchitecturenoteapp.ui.theme
+package com.ibrahimcanerdogan.cleannote.ui.theme
import androidx.compose.ui.graphics.Color
val DarkGray = Color(0xFF202020)
+val LightGray = Color(0xFFD3D3D3)
val LightBlue = Color(0xFFD7E8DE)
val RedOrange = Color(0xffffab91)
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Shape.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/theme/Shape.kt
similarity index 71%
rename from app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Shape.kt
rename to app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/theme/Shape.kt
index 3a70048a..fb5eaebf 100644
--- a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Shape.kt
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/theme/Shape.kt
@@ -1,7 +1,7 @@
-package com.plcoding.cleanarchitecturenoteapp.ui.theme
+package com.ibrahimcanerdogan.cleannote.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/ibrahimcanerdogan/cleannote/ui/theme/Theme.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/theme/Theme.kt
new file mode 100644
index 00000000..e74bac3d
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/theme/Theme.kt
@@ -0,0 +1,52 @@
+package com.ibrahimcanerdogan.cleannote.ui.theme
+
+import android.os.Build
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+
+private val DarkColorScheme = darkColorScheme(
+ primary = Color.White,
+ background = DarkGray,
+ onBackground = Color.White,
+ surface = LightBlue,
+ onSurface = DarkGray
+)
+
+private val LightColorScheme = lightColorScheme(
+ primary = Color.White,
+ background = LightGray,
+ onBackground = Color.White,
+ surface = LightBlue,
+ onSurface = DarkGray
+)
+
+@Composable
+fun CleanNoteAppTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ // Dynamic color is available on Android 12+
+ dynamicColor: Boolean = true,
+ 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
+ }
+
+ MaterialTheme(
+ colorScheme = colorScheme,
+ typography = Typography,
+ content = content
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Type.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/theme/Type.kt
similarity index 85%
rename from app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Type.kt
rename to app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/theme/Type.kt
index 4db73a88..9ddba820 100644
--- a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Type.kt
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/theme/Type.kt
@@ -1,6 +1,6 @@
-package com.plcoding.cleanarchitecturenoteapp.ui.theme
+package com.ibrahimcanerdogan.cleannote.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/java/com/ibrahimcanerdogan/cleannote/ui/view/edit/AddEditNoteEvent.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/edit/AddEditNoteEvent.kt
new file mode 100644
index 00000000..9dae1160
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/edit/AddEditNoteEvent.kt
@@ -0,0 +1,13 @@
+package com.ibrahimcanerdogan.cleannote.ui.view.edit
+
+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()
+ data object SaveNote: AddEditNoteEvent()
+}
+
diff --git a/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/edit/AddEditNoteScreen.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/edit/AddEditNoteScreen.kt
new file mode 100644
index 00000000..6bb4962a
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/edit/AddEditNoteScreen.kt
@@ -0,0 +1,150 @@
+package com.ibrahimcanerdogan.cleannote.ui.view.edit
+
+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.ibrahimcanerdogan.cleannote.data.model.Note
+import com.ibrahimcanerdogan.cleannote.ui.component.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)
+ },
+ shape = CircleShape,
+ containerColor = MaterialTheme.colorScheme.primary
+ ) {
+ Icon(imageVector = Icons.Default.Save, contentDescription = "Save note")
+ }
+ }
+ ) { paddingValues ->
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(noteBackgroundAnimatable.value)
+ .padding(paddingValues)
+ .padding(16.dp)
+ ) {
+ 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.stateText,
+ hint = titleState.stateHint,
+ onValueChange = {
+ viewModel.onEvent(AddEditNoteEvent.EnteredTitle(it))
+ },
+ onFocusChange = {
+ viewModel.onEvent(AddEditNoteEvent.ChangeTitleFocus(it))
+ },
+ isHintVisible = titleState.stateIsHintVisible,
+ singleLine = true,
+ textStyle = MaterialTheme.typography.headlineSmall
+ )
+ Spacer(modifier = Modifier.height(16.dp))
+ TransparentHintTextField(
+ text = contentState.stateText,
+ hint = contentState.stateHint,
+ onValueChange = {
+ viewModel.onEvent(AddEditNoteEvent.EnteredContent(it))
+ },
+ onFocusChange = {
+ viewModel.onEvent(AddEditNoteEvent.ChangeContentFocus(it))
+ },
+ isHintVisible = contentState.stateIsHintVisible,
+ textStyle = MaterialTheme.typography.bodyLarge,
+ modifier = Modifier.fillMaxHeight()
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/edit/AddEditNoteViewModel.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/edit/AddEditNoteViewModel.kt
new file mode 100644
index 00000000..60fc564b
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/edit/AddEditNoteViewModel.kt
@@ -0,0 +1,119 @@
+package com.ibrahimcanerdogan.cleannote.ui.view.edit
+
+import androidx.compose.runtime.State
+import androidx.compose.runtime.mutableIntStateOf
+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.ibrahimcanerdogan.cleannote.data.model.InvalidNoteException
+import com.ibrahimcanerdogan.cleannote.data.model.Note
+import com.ibrahimcanerdogan.cleannote.domain.usecase.NoteUseCase
+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 noteUseCase: NoteUseCase,
+ savedStateHandle: SavedStateHandle
+) : ViewModel() {
+
+ private val _noteTitle = mutableStateOf(NoteTextFieldState(stateHint = "Note Title"))
+ val noteTitle: State = _noteTitle
+
+ private val _noteContent = mutableStateOf(NoteTextFieldState(stateHint = "Note Description"))
+ val noteContent: State = _noteContent
+
+ private val _noteColor = mutableIntStateOf(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 {
+ noteUseCase.getNote(noteId)?.also { note ->
+ currentNoteId = note.id
+ _noteTitle.value = noteTitle.value.copy(
+ stateText = note.title,
+ stateIsHintVisible = false
+ )
+ _noteContent.value = _noteContent.value.copy(
+ stateText = note.content,
+ stateIsHintVisible = false
+ )
+ _noteColor.intValue = note.color
+ }
+ }
+ }
+ }
+ }
+
+ fun onEvent(event: AddEditNoteEvent) {
+ when (event) {
+ is AddEditNoteEvent.EnteredTitle -> {
+ _noteTitle.value = noteTitle.value.copy(
+ stateText = event.value
+ )
+ }
+
+ is AddEditNoteEvent.ChangeTitleFocus -> {
+ _noteTitle.value = noteTitle.value.copy(
+ stateIsHintVisible = !event.focusState.isFocused && noteTitle.value.stateText.isBlank()
+ )
+ }
+
+ is AddEditNoteEvent.EnteredContent -> {
+ _noteContent.value = _noteContent.value.copy(
+ stateText = event.value
+ )
+ }
+
+ is AddEditNoteEvent.ChangeContentFocus -> {
+ _noteContent.value = _noteContent.value.copy(
+ stateIsHintVisible = !event.focusState.isFocused && _noteContent.value.stateText.isBlank()
+ )
+ }
+
+ is AddEditNoteEvent.ChangeColor -> {
+ _noteColor.intValue = event.color
+ }
+
+ is AddEditNoteEvent.SaveNote -> {
+ viewModelScope.launch {
+ try {
+ noteUseCase.addNote(
+ Note(
+ title = noteTitle.value.stateText,
+ content = noteContent.value.stateText,
+ 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()
+ data object SaveNote : UiEvent()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/edit/NoteTextFieldState.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/edit/NoteTextFieldState.kt
new file mode 100644
index 00000000..d9f244f6
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/edit/NoteTextFieldState.kt
@@ -0,0 +1,7 @@
+package com.ibrahimcanerdogan.cleannote.ui.view.edit
+
+data class NoteTextFieldState(
+ val stateText: String = "",
+ val stateHint: String = "",
+ val stateIsHintVisible: Boolean = true
+)
diff --git a/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/list/NoteListEvent.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/list/NoteListEvent.kt
new file mode 100644
index 00000000..9f5d2e09
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/list/NoteListEvent.kt
@@ -0,0 +1,11 @@
+package com.ibrahimcanerdogan.cleannote.ui.view.list
+
+import com.ibrahimcanerdogan.cleannote.data.model.Note
+import com.ibrahimcanerdogan.cleannote.domain.util.NoteOrder
+
+sealed class NoteListEvent {
+ data class Order(val noteOrder: NoteOrder): NoteListEvent()
+ data class DeleteNote(val note: Note): NoteListEvent()
+ data object RestoreNote: NoteListEvent()
+ data object ToggleOrderSection: NoteListEvent()
+}
diff --git a/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/list/NoteListScreen.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/list/NoteListScreen.kt
new file mode 100644
index 00000000..16a47d1b
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/list/NoteListScreen.kt
@@ -0,0 +1,207 @@
+package com.ibrahimcanerdogan.cleannote.ui.view.list
+
+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.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.ibrahimcanerdogan.cleannote.ui.component.OrderSection
+import com.ibrahimcanerdogan.cleannote.ui.navigation.NoteScreen
+import kotlinx.coroutines.launch
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.icons.automirrored.filled.Sort
+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.filled.Delete
+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.core.graphics.ColorUtils
+import com.ibrahimcanerdogan.cleannote.data.model.Note
+import com.ibrahimcanerdogan.cleannote.ui.theme.DarkGray
+
+@Composable
+fun NoteListScreen(
+ navController: NavController,
+ viewModel: NoteListViewModel = hiltViewModel()
+) {
+ val state = viewModel.state.value
+ val snackbarHostState = remember { SnackbarHostState() }
+ val scope = rememberCoroutineScope()
+
+ Scaffold(
+ snackbarHost = { SnackbarHost(snackbarHostState) },
+ floatingActionButton = {
+ FloatingActionButton(
+ onClick = {
+ navController.navigate(NoteScreen.AddEditNoteScreen.route)
+ },
+ shape = CircleShape,
+ containerColor = MaterialTheme.colorScheme.primary
+ ) {
+ Icon(imageVector = Icons.Default.Add, contentDescription = "Add note")
+ }
+ }
+ ) { paddingValues ->
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(paddingValues)
+ .padding(16.dp)
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = "JetNote",
+ style = MaterialTheme.typography.headlineMedium
+ )
+ IconButton(
+ onClick = {
+ viewModel.onEvent(NoteListEvent.ToggleOrderSection)
+ },
+ ) {
+ Icon(
+ imageVector = Icons.AutoMirrored.Default.Sort,
+ contentDescription = "Sort"
+ )
+ }
+ }
+ AnimatedVisibility(
+ visible = state.isOrderSectionVisible,
+ enter = fadeIn() + slideInVertically()
+ ) {
+ OrderSection(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = 16.dp),
+ noteOrder = state.noteOrder,
+ onOrderChange = {
+ viewModel.onEvent(NoteListEvent.Order(it))
+ }
+ )
+ }
+ Spacer(modifier = Modifier.height(5.dp))
+ LazyColumn(modifier = Modifier.fillMaxSize()) {
+ items(state.notes) { note ->
+ NoteItem(
+ note = note,
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable {
+ navController.navigate(
+ NoteScreen.AddEditNoteScreen.route +
+ "?noteId=${note.id}¬eColor=${note.color}"
+ )
+ },
+ onDeleteClick = {
+ viewModel.onEvent(NoteListEvent.DeleteNote(note))
+ scope.launch {
+ val result = snackbarHostState.showSnackbar(
+ message = "Note deleted",
+ actionLabel = "Undo"
+ )
+ if(result == SnackbarResult.ActionPerformed) {
+ viewModel.onEvent(NoteListEvent.RestoreNote)
+ }
+ }
+ }
+ )
+ Spacer(modifier = Modifier.height(16.dp))
+ }
+ }
+ }
+ }
+}
+
+@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 = DarkGray,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ Spacer(modifier = Modifier.height(8.dp))
+ Text(
+ text = note.content,
+ style = MaterialTheme.typography.bodyLarge,
+ color = DarkGray,
+ 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/ibrahimcanerdogan/cleannote/ui/view/list/NoteListState.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/list/NoteListState.kt
new file mode 100644
index 00000000..92bec41b
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/list/NoteListState.kt
@@ -0,0 +1,11 @@
+package com.ibrahimcanerdogan.cleannote.ui.view.list
+
+import com.ibrahimcanerdogan.cleannote.data.model.Note
+import com.ibrahimcanerdogan.cleannote.domain.util.NoteOrder
+import com.ibrahimcanerdogan.cleannote.domain.util.OrderType
+
+data class NoteListState(
+ val notes: List = emptyList(),
+ val noteOrder: NoteOrder = NoteOrder.Date(OrderType.Descending),
+ val isOrderSectionVisible: Boolean = false
+)
diff --git a/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/list/NoteListViewModel.kt b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/list/NoteListViewModel.kt
new file mode 100644
index 00000000..58555c51
--- /dev/null
+++ b/app/src/main/java/com/ibrahimcanerdogan/cleannote/ui/view/list/NoteListViewModel.kt
@@ -0,0 +1,78 @@
+package com.ibrahimcanerdogan.cleannote.ui.view.list
+
+import androidx.compose.runtime.State
+import androidx.compose.runtime.mutableStateOf
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.ibrahimcanerdogan.cleannote.data.model.Note
+import com.ibrahimcanerdogan.cleannote.domain.usecase.NoteUseCase
+import com.ibrahimcanerdogan.cleannote.domain.util.NoteOrder
+import com.ibrahimcanerdogan.cleannote.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 NoteListViewModel @Inject constructor(
+ private val noteUseCase: NoteUseCase
+) : ViewModel() {
+
+ private val _state = mutableStateOf(NoteListState())
+ val state: State = _state
+
+ private var recentlyDeletedNote: Note? = null
+
+ private var getNotesJob: Job? = null
+
+ init {
+ getNotes(NoteOrder.Date(OrderType.Descending))
+ }
+
+ fun onEvent(event: NoteListEvent) {
+ when (event) {
+ is NoteListEvent.Order -> {
+ if (state.value.noteOrder::class == event.noteOrder::class &&
+ state.value.noteOrder.orderType == event.noteOrder.orderType
+ ) {
+ return
+ }
+ getNotes(event.noteOrder)
+ }
+
+ is NoteListEvent.DeleteNote -> {
+ viewModelScope.launch {
+ noteUseCase.deleteNote(event.note)
+ recentlyDeletedNote = event.note
+ }
+ }
+
+ is NoteListEvent.RestoreNote -> {
+ viewModelScope.launch {
+ noteUseCase.addNote(recentlyDeletedNote ?: return@launch)
+ recentlyDeletedNote = null
+ }
+ }
+
+ is NoteListEvent.ToggleOrderSection -> {
+ _state.value = state.value.copy(
+ isOrderSectionVisible = !state.value.isOrderSectionVisible
+ )
+ }
+ }
+ }
+
+ private fun getNotes(noteOrder: NoteOrder) {
+ getNotesJob?.cancel()
+ getNotesJob = noteUseCase.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/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/ui/theme/Theme.kt b/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Theme.kt
deleted file mode 100644
index 24a1508f..00000000
--- a/app/src/main/java/com/plcoding/cleanarchitecturenoteapp/ui/theme/Theme.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.plcoding.cleanarchitecturenoteapp.ui.theme
-
-import androidx.compose.material.MaterialTheme
-import androidx.compose.material.darkColors
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.graphics.Color
-
-private val DarkColorPalette = darkColors(
- primary = Color.White,
- background = DarkGray,
- onBackground = Color.White,
- surface = LightBlue,
- onSurface = DarkGray
-)
-
-@Composable
-fun CleanArchitectureNoteAppTheme(darkTheme: Boolean = true, content: @Composable() () -> Unit) {
- MaterialTheme(
- colors = DarkColorPalette,
- typography = Typography,
- shapes = Shapes,
- content = content
- )
-}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/icon_launcher_foreground.xml b/app/src/main/res/drawable/icon_launcher_foreground.xml
new file mode 100644
index 00000000..83c3cba7
--- /dev/null
+++ b/app/src/main/res/drawable/icon_launcher_foreground.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/app/src/main/res/mipmap-anydpi-v26/icon_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/icon_launcher.xml
new file mode 100644
index 00000000..519a0847
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/icon_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/icon_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/icon_launcher_round.xml
new file mode 100644
index 00000000..519a0847
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/icon_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/icon_launcher.webp b/app/src/main/res/mipmap-hdpi/icon_launcher.webp
new file mode 100644
index 00000000..0ce139e8
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/icon_launcher.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/icon_launcher_round.webp b/app/src/main/res/mipmap-hdpi/icon_launcher_round.webp
new file mode 100644
index 00000000..5ad44c91
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/icon_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/icon_launcher.webp b/app/src/main/res/mipmap-mdpi/icon_launcher.webp
new file mode 100644
index 00000000..27c0b414
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/icon_launcher.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/icon_launcher_round.webp b/app/src/main/res/mipmap-mdpi/icon_launcher_round.webp
new file mode 100644
index 00000000..3680e68b
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/icon_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/icon_launcher.webp b/app/src/main/res/mipmap-xhdpi/icon_launcher.webp
new file mode 100644
index 00000000..e1f712a8
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/icon_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/icon_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/icon_launcher_round.webp
new file mode 100644
index 00000000..d472fe8e
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/icon_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/icon_launcher.webp b/app/src/main/res/mipmap-xxhdpi/icon_launcher.webp
new file mode 100644
index 00000000..6f9e233f
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/icon_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/icon_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/icon_launcher_round.webp
new file mode 100644
index 00000000..b9c1919a
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/icon_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/icon_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/icon_launcher.webp
new file mode 100644
index 00000000..7b3c94cd
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/icon_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/icon_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/icon_launcher_round.webp
new file mode 100644
index 00000000..c8de0428
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/icon_launcher_round.webp differ
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/icon_launcher_background.xml b/app/src/main/res/values/icon_launcher_background.xml
new file mode 100644
index 00000000..dc8c7221
--- /dev/null
+++ b/app/src/main/res/values/icon_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #FFFFFF
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e3eec93a..6dc35484 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,3 @@
- CleanArchitectureNoteApp
+ Clean Note
\ 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..33a1f20f 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/app/src/test/java/com/plcoding/cleanarchitecturenoteapp/ExampleUnitTest.kt b/app/src/test/java/com/ibrahimcanerdogan/cleannote/ExampleUnitTest.kt
similarity index 87%
rename from app/src/test/java/com/plcoding/cleanarchitecturenoteapp/ExampleUnitTest.kt
rename to app/src/test/java/com/ibrahimcanerdogan/cleannote/ExampleUnitTest.kt
index 6b05f878..5416e2e8 100644
--- a/app/src/test/java/com/plcoding/cleanarchitecturenoteapp/ExampleUnitTest.kt
+++ b/app/src/test/java/com/ibrahimcanerdogan/cleannote/ExampleUnitTest.kt
@@ -1,4 +1,4 @@
-package com.plcoding.cleanarchitecturenoteapp
+package com.ibrahimcanerdogan.cleannote
import org.junit.Test
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..d1069df5
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,8 @@
+// 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.devtools.ksp") version "2.0.21-1.0.27" 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..85da05e1
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,32 @@
+[versions]
+agp = "8.10.1"
+kotlin = "2.1.21"
+coreKtx = "1.16.0"
+junit = "4.13.2"
+junitVersion = "1.2.1"
+espressoCore = "3.6.1"
+lifecycleRuntimeKtx = "2.9.1"
+activityCompose = "1.10.1"
+composeBom = "2025.06.00"
+
+[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..5d4f00af 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.14.2-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..a6e2f3d4
--- /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 = "CleanNoteApp"
+include(":app")
\ No newline at end of file