diff --git a/Note_Application/.gitignore b/Note_Application/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/Note_Application/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/Note_Application/.idea/.gitignore b/Note_Application/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/Note_Application/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/Note_Application/.idea/AndroidProjectSystem.xml b/Note_Application/.idea/AndroidProjectSystem.xml new file mode 100644 index 0000000..4a53bee --- /dev/null +++ b/Note_Application/.idea/AndroidProjectSystem.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/Note_Application/.idea/compiler.xml b/Note_Application/.idea/compiler.xml new file mode 100644 index 0000000..b86273d --- /dev/null +++ b/Note_Application/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Note_Application/.idea/deploymentTargetSelector.xml b/Note_Application/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..b268ef3 --- /dev/null +++ b/Note_Application/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/Note_Application/.idea/deviceManager.xml b/Note_Application/.idea/deviceManager.xml new file mode 100644 index 0000000..91f9558 --- /dev/null +++ b/Note_Application/.idea/deviceManager.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/Note_Application/.idea/gradle.xml b/Note_Application/.idea/gradle.xml new file mode 100644 index 0000000..639c779 --- /dev/null +++ b/Note_Application/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/Note_Application/.idea/inspectionProfiles/Project_Default.xml b/Note_Application/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..7061a0d --- /dev/null +++ b/Note_Application/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,61 @@ + + + + \ No newline at end of file diff --git a/Note_Application/.idea/migrations.xml b/Note_Application/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/Note_Application/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/Note_Application/.idea/misc.xml b/Note_Application/.idea/misc.xml new file mode 100644 index 0000000..74dd639 --- /dev/null +++ b/Note_Application/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/Note_Application/.idea/runConfigurations.xml b/Note_Application/.idea/runConfigurations.xml new file mode 100644 index 0000000..16660f1 --- /dev/null +++ b/Note_Application/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/Note_Application/app/.gitignore b/Note_Application/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/Note_Application/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/Note_Application/app/build.gradle.kts b/Note_Application/app/build.gradle.kts new file mode 100644 index 0000000..16b0458 --- /dev/null +++ b/Note_Application/app/build.gradle.kts @@ -0,0 +1,82 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.compose.compiler) + id("org.jetbrains.kotlin.kapt") + id("dagger.hilt.android.plugin") +} + +android { + namespace = "com.note_application" + compileSdk = 36 + + defaultConfig { + applicationId = "com.note_application" + minSdk = 24 + targetSdk = 36 + 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 { + + val roomVersion = "2.6.1" + implementation("androidx.room:room-runtime:$roomVersion") + kapt("androidx.room:room-compiler:$roomVersion") + implementation("androidx.room:room-ktx:$roomVersion") + +// val hilt_version: String by project + val hilt_version = "2.44" + + implementation("com.google.dagger:hilt-android:$hilt_version") + kapt("com.google.dagger:hilt-compiler:$hilt_version") + + // Hilt ViewModel qo‘llab-quvvatlash + implementation("androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03") + kapt("androidx.hilt:hilt-compiler:1.0.0") + implementation("androidx.compose.material3:material3:1.0.0") + 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.compose.ui) + implementation(libs.androidx.compose.ui.graphics) + implementation(libs.androidx.compose.ui.tooling.preview) + implementation(libs.androidx.compose.material3) + implementation("androidx.navigation:navigation-compose:2.4.2") + implementation("androidx.hilt:hilt-navigation-compose:1.0.0") + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(platform(libs.androidx.compose.bom)) + androidTestImplementation(libs.androidx.compose.ui.test.junit4) + debugImplementation(libs.androidx.compose.ui.tooling) + debugImplementation(libs.androidx.compose.ui.test.manifest) +} + +kapt { + correctErrorTypes = true +} diff --git a/Note_Application/app/proguard-rules.pro b/Note_Application/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/Note_Application/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/Note_Application/app/src/androidTest/java/com/note_application/ExampleInstrumentedTest.kt b/Note_Application/app/src/androidTest/java/com/note_application/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..e63ed7e --- /dev/null +++ b/Note_Application/app/src/androidTest/java/com/note_application/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.note_application + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.note_application", appContext.packageName) + } +} \ No newline at end of file diff --git a/Note_Application/app/src/main/AndroidManifest.xml b/Note_Application/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..01b3b4d --- /dev/null +++ b/Note_Application/app/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Note_Application/app/src/main/java/com/note_application/NoteApp.kt b/Note_Application/app/src/main/java/com/note_application/NoteApp.kt new file mode 100644 index 0000000..ce33a26 --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/NoteApp.kt @@ -0,0 +1,10 @@ +package com.note_application + +import android.app.Application +import dagger.hilt.android.HiltAndroidApp + +@HiltAndroidApp +class NoteApp : Application(){ + + +} diff --git a/Note_Application/app/src/main/java/com/note_application/di/AppModule.kt b/Note_Application/app/src/main/java/com/note_application/di/AppModule.kt new file mode 100644 index 0000000..c4830a6 --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/di/AppModule.kt @@ -0,0 +1,49 @@ +package com.note_application.di + +import android.app.Application +import androidx.room.Room +import com.note_application.feature_note.data.data_source.NoteDatabase +import com.note_application.feature_note.data.repository.NoteRepositoryImp +import com.note_application.feature_note.domain.repository.NoteRepository +import com.note_application.feature_note.domain.use_case.AddNote +import com.note_application.feature_note.domain.use_case.DeleteNote +import com.note_application.feature_note.domain.use_case.GetNote +import com.note_application.feature_note.domain.use_case.GetNotes +import com.note_application.feature_note.domain.use_case.NoteUseCases +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 NoteRepositoryImp(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/Note_Application/app/src/main/java/com/note_application/feature_note/data/data_source/NoteDao.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/data/data_source/NoteDao.kt new file mode 100644 index 0000000..f2c8c61 --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/data/data_source/NoteDao.kt @@ -0,0 +1,23 @@ +package com.note_application.feature_note.data.data_source + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import kotlinx.coroutines.flow.Flow +import com.note_application.feature_note.domain.model.Note +@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/Note_Application/app/src/main/java/com/note_application/feature_note/data/data_source/NoteDatabase.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/data/data_source/NoteDatabase.kt new file mode 100644 index 0000000..39056e4 --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/data/data_source/NoteDatabase.kt @@ -0,0 +1,17 @@ +package com.note_application.feature_note.data.data_source + +import androidx.room.Database +import androidx.room.RoomDatabase +import com.note_application.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/Note_Application/app/src/main/java/com/note_application/feature_note/data/repository/NoteRepositoryImp.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/data/repository/NoteRepositoryImp.kt new file mode 100644 index 0000000..3035246 --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/data/repository/NoteRepositoryImp.kt @@ -0,0 +1,26 @@ +package com.note_application.feature_note.data.repository + +import com.note_application.feature_note.data.data_source.NoteDao +import com.note_application.feature_note.domain.model.Note +import com.note_application.feature_note.domain.repository.NoteRepository +import kotlinx.coroutines.flow.Flow + +class NoteRepositoryImp( + 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) { + return dao.insertNote(note) + } + + override suspend fun deleteNote(note: Note) { + return dao.deleteNote(note) + } +} \ No newline at end of file diff --git a/Note_Application/app/src/main/java/com/note_application/feature_note/domain/model/Note.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/domain/model/Note.kt new file mode 100644 index 0000000..eeeba0a --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/domain/model/Note.kt @@ -0,0 +1,23 @@ +package com.note_application.feature_note.domain.model + +import androidx.room.Entity +import androidx.room.PrimaryKey +import androidx.compose.ui.graphics.Color +import com.note_application.ui.theme.* + +@Entity +data class Note( + val title: String, + val content: String, + val timestamp: Long, + val color: Int, + @PrimaryKey val id: Int? = null +) +{ + companion object { + val noteColors: List = listOf(RedOrange, LightGreen, Violet, BabyBlue, RedPink) + } +} + + +class InvalidNoteException(message: String) : Exception(message) \ No newline at end of file diff --git a/Note_Application/app/src/main/java/com/note_application/feature_note/domain/repository/NoteRepository.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/domain/repository/NoteRepository.kt new file mode 100644 index 0000000..2f5a1c5 --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/domain/repository/NoteRepository.kt @@ -0,0 +1,14 @@ +package com.note_application.feature_note.domain.repository + +import com.note_application.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/Note_Application/app/src/main/java/com/note_application/feature_note/domain/use_case/AddNote.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/domain/use_case/AddNote.kt new file mode 100644 index 0000000..d2d0f59 --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/domain/use_case/AddNote.kt @@ -0,0 +1,22 @@ +package com.note_application.feature_note.domain.use_case + +import com.note_application.feature_note.domain.model.InvalidNoteException +import com.note_application.feature_note.domain.model.Note +import com.note_application.feature_note.domain.repository.NoteRepository +import kotlin.jvm.Throws + +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/Note_Application/app/src/main/java/com/note_application/feature_note/domain/use_case/DeleteNote.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/domain/use_case/DeleteNote.kt new file mode 100644 index 0000000..752552d --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/domain/use_case/DeleteNote.kt @@ -0,0 +1,13 @@ +package com.note_application.feature_note.domain.use_case + +import com.note_application.feature_note.domain.model.Note +import com.note_application.feature_note.domain.repository.NoteRepository + +class DeleteNote( + private val repository: NoteRepository +){ + + suspend operator fun invoke(note: Note){ + repository.deleteNote(note) + } +} diff --git a/Note_Application/app/src/main/java/com/note_application/feature_note/domain/use_case/GetNote.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/domain/use_case/GetNote.kt new file mode 100644 index 0000000..99c9878 --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/domain/use_case/GetNote.kt @@ -0,0 +1,15 @@ +package com.note_application.feature_note.domain.use_case + +import com.note_application.feature_note.domain.model.Note +import com.note_application.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/Note_Application/app/src/main/java/com/note_application/feature_note/domain/use_case/GetNotes.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/domain/use_case/GetNotes.kt new file mode 100644 index 0000000..bfe588a --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/domain/use_case/GetNotes.kt @@ -0,0 +1,35 @@ +package com.note_application.feature_note.domain.use_case + +import com.note_application.feature_note.domain.model.Note +import com.note_application.feature_note.domain.repository.NoteRepository +import com.note_application.feature_note.domain.util.NoteOrder +import com.note_application.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 } + } + } + } + } + } +} diff --git a/Note_Application/app/src/main/java/com/note_application/feature_note/domain/use_case/NoteUseCases.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/domain/use_case/NoteUseCases.kt new file mode 100644 index 0000000..310b1d2 --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/domain/use_case/NoteUseCases.kt @@ -0,0 +1,9 @@ +package com.note_application.feature_note.domain.use_case + +data class NoteUseCases( + + val getNotes: GetNotes, + val deleteNote: DeleteNote, + val addNote: AddNote, + val getNote: GetNote +) diff --git a/Note_Application/app/src/main/java/com/note_application/feature_note/domain/util/NoteOrder.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/domain/util/NoteOrder.kt new file mode 100644 index 0000000..a5e477d --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/domain/util/NoteOrder.kt @@ -0,0 +1,15 @@ +package com.note_application.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) + } + } +} \ No newline at end of file diff --git a/Note_Application/app/src/main/java/com/note_application/feature_note/domain/util/OrderType.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/domain/util/OrderType.kt new file mode 100644 index 0000000..3d8da8d --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/domain/util/OrderType.kt @@ -0,0 +1,6 @@ +package com.note_application.feature_note.domain.util + +sealed class OrderType{ + object Ascending: OrderType() + object Descending: OrderType() +} \ No newline at end of file diff --git a/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/MainActivity.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/MainActivity.kt new file mode 100644 index 0000000..0ff2735 --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/MainActivity.kt @@ -0,0 +1,86 @@ +package com.note_application.feature_note.presentation + +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.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.material3.Text +import androidx.compose.ui.tooling.preview.Preview +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.note_application.feature_note.presentation.notes.NotesScreen +import com.note_application.feature_note.presentation.add_edit_note.AddEditNoteScreen +import com.note_application.feature_note.presentation.util.Screen +import com.note_application.ui.theme.Note_ApplicationTheme +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class MainActivity : ComponentActivity() { + @ExperimentalAnimationApi + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContent { + Note_ApplicationTheme { + Surface( + modifier = Modifier, + color = MaterialTheme.colorScheme.background + ) { + val navController = rememberNavController() + NavHost( + navController = navController, + startDestination = Screen.NoteScreen.route + ) { + composable(route = Screen.NoteScreen.route) { + NotesScreen(navController = navController) + } + composable( + route = Screen.AddEditNoteScreen.route + + "?noteId={noteId}¬eColor={noteColor}", + arguments = listOf( + navArgument("noteId") { + type = NavType.IntType + defaultValue = -1 + }, + navArgument("noteColor") { + type = NavType.IntType + defaultValue = -1 + } + ) + ) { backStackEntry -> + val color = backStackEntry.arguments?.getInt("noteColor") ?: -1 + AddEditNoteScreen( + navController = navController, + noteColor = color + ) + } + } + } + } + } + } +} + +@Composable +fun Greeting(name: String, modifier: Modifier = Modifier) { + Text( + text = "Hello $name!", + modifier = modifier + ) +} + +@Preview(showBackground = true) +@Composable +fun GreetingPreview() { + Note_ApplicationTheme { + Greeting("Android") + } +} diff --git a/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/add_edit_note/AddEditNoteEvent.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/add_edit_note/AddEditNoteEvent.kt new file mode 100644 index 0000000..4d4a813 --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/add_edit_note/AddEditNoteEvent.kt @@ -0,0 +1,18 @@ +package com.note_application.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: UiEvent() +} + +sealed class UiEvent { + data class ShowSnackbar(val message: String): UiEvent() + object SaveNote : UiEvent() +} \ No newline at end of file diff --git a/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/add_edit_note/AddEditNoteScreen.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/add_edit_note/AddEditNoteScreen.kt new file mode 100644 index 0000000..1a6bcac --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/add_edit_note/AddEditNoteScreen.kt @@ -0,0 +1,173 @@ +package com.note_application.feature_note.presentation.add_edit_note + +import TransparentHintTextField +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Check +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +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.note_application.feature_note.domain.model.Note + +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.tween + + +@Composable +fun AddEditNoteScreen( + navController: NavController, + noteColor: Int, + viewModel: AddEditNoteViewModel = hiltViewModel() +) { + val titleState = viewModel.noteTitle.value + val contentState = viewModel.noteContent.value + + val snackbarHostState = remember { SnackbarHostState() } + + // ✅ Float tipidagi animatsiya — Color emas + val noteBackgroundAnimatable = remember { + Animatable( + if (noteColor != -1) noteColor.toFloat() else viewModel.noteColor.value.toFloat() + ) + } + + val scope = rememberCoroutineScope() + + LaunchedEffect(true) { + viewModel.eventFlow.collectLatest { event -> + when (event) { + is AddEditNoteViewModel.UiEvent.ShowSnackbar -> { + snackbarHostState.showSnackbar(message = event.message) + } + + is AddEditNoteViewModel.UiEvent.SaveNote -> { + navController.navigateUp() + } + } + } + } + + Scaffold( + floatingActionButton = { + FloatingActionButton( + onClick = { + viewModel.onEvent(AddEditNoteEvent.SaveNote) + }, + containerColor = MaterialTheme.colorScheme.primary + ) { + Icon( + imageVector = Icons.Default.Check, + contentDescription = "Save note" + ) + } + }, + snackbarHost = { SnackbarHost(hostState = snackbarHostState) } + ) { paddingValues -> + Column( + modifier = Modifier + .fillMaxSize() + .background(Color(noteBackgroundAnimatable.value.toInt())) // ✅ Float'dan Color + .padding(paddingValues) + .padding(16.dp) + ) { + // Color selector + 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 = colorInt.toFloat(), // ✅ Float qiymat + 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.headlineMedium + ) + + 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.bodyMedium, + modifier = Modifier.weight(1f) + ) + } + } +} \ No newline at end of file diff --git a/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/add_edit_note/AddEditNoteViewModel.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/add_edit_note/AddEditNoteViewModel.kt new file mode 100644 index 0000000..9863a64 --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/add_edit_note/AddEditNoteViewModel.kt @@ -0,0 +1,119 @@ +package com.note_application.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.note_application.feature_note.domain.model.InvalidNoteException +import com.note_application.feature_note.domain.model.Note +import com.note_application.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 // <- BU YERDA XATO: "-" o'rniga "=" + } + } + } + } + } + + 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, // <- _noteContent o'rniga noteContent + timestamp = System.currentTimeMillis(), + color = noteColor.value, + id = currentNoteId + ) + ) + _eventFlow.emit(UiEvent.SaveNote) + } catch (e: InvalidNoteException) { + _eventFlow.emit( + UiEvent.ShowSnackbar( + message = e.message ?: "Could not save note." + ) + ) + } + } + } + } + } + + sealed class UiEvent { + data class ShowSnackbar(val message: String) : UiEvent() + object SaveNote : UiEvent() + } +} \ No newline at end of file diff --git a/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/add_edit_note/NoteTextFieldState.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/add_edit_note/NoteTextFieldState.kt new file mode 100644 index 0000000..356aeee --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/add_edit_note/NoteTextFieldState.kt @@ -0,0 +1,7 @@ +package com.note_application.feature_note.presentation.add_edit_note + +data class NoteTextFieldState( + val text: String = "", + val hint: String = "", + val isHintVisible: Boolean = true +) \ No newline at end of file diff --git a/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/add_edit_note/components/TransparentHintTextField.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/add_edit_note/components/TransparentHintTextField.kt new file mode 100644 index 0000000..6a8aced --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/add_edit_note/components/TransparentHintTextField.kt @@ -0,0 +1,43 @@ +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/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/NotesEvent.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/NotesEvent.kt new file mode 100644 index 0000000..9f8e7ab --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/NotesEvent.kt @@ -0,0 +1,11 @@ +package com.note_application.feature_note.presentation.notes + +import com.note_application.feature_note.domain.util.NoteOrder +import com.note_application.feature_note.domain.model.Note + +sealed class NotesEvent { + data class Order(val noteOrder: NoteOrder): NotesEvent() + data class DeleteNote(val note: Note): NotesEvent() + object RestoreNote: NotesEvent() + object ToggleOrderSection: NotesEvent() +} \ No newline at end of file diff --git a/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/NotesScreen.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/NotesScreen.kt new file mode 100644 index 0000000..2690a65 --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/NotesScreen.kt @@ -0,0 +1,138 @@ +package com.note_application.feature_note.presentation.notes + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.KeyboardArrowDown +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.SnackbarResult +import androidx.compose.material3.Text +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.note_application.feature_note.presentation.notes.components.NoteItem +import com.note_application.feature_note.presentation.notes.components.OrderSection +import com.note_application.feature_note.presentation.util.Screen +import kotlinx.coroutines.launch + +@Composable +fun NotesScreen( + navController: NavController, + viewModel: NotesViewModel = hiltViewModel() +) { + val state = viewModel.state.value + val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + + Scaffold( + floatingActionButton = { + FloatingActionButton( + onClick = { + navController.navigate(Screen.AddEditNoteScreen.route) + }, + containerColor = MaterialTheme.colorScheme.primary + ) { + Icon(imageVector = Icons.Default.Add, contentDescription = "Add note") + } + }, + snackbarHost = { androidx.compose.material3.SnackbarHost(snackbarHostState) } + ) { paddingValues -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + .padding(16.dp) + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "Your notes", + style = MaterialTheme.typography.headlineMedium + ) + + IconButton( + onClick = { + viewModel.onEvent(NotesEvent.ToggleOrderSection) + }, + ) { + Icon( + imageVector = Icons.Default.KeyboardArrowDown, + 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/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/NotesState.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/NotesState.kt new file mode 100644 index 0000000..00abc12 --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/NotesState.kt @@ -0,0 +1,12 @@ +package com.note_application.feature_note.presentation.notes + +import com.note_application.feature_note.domain.util.NoteOrder +import com.note_application.feature_note.domain.util.OrderType +import com.note_application.feature_note.domain.model.Note + + +data class NotesState( + val notes: List = emptyList(), + val noteOrder: NoteOrder = NoteOrder.Date(OrderType.Descending), + val isOrderSectionVisible : Boolean = false +) diff --git a/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/NotesViewModel.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/NotesViewModel.kt new file mode 100644 index 0000000..22d343f --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/NotesViewModel.kt @@ -0,0 +1,75 @@ +package com.note_application.feature_note.presentation.notes + +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.note_application.feature_note.domain.use_case.NoteUseCases +import kotlinx.coroutines.launch +import javax.inject.Inject +import com.note_application.feature_note.domain.model.Note +import com.note_application.feature_note.domain.util.NoteOrder +import com.note_application.feature_note.domain.util.OrderType +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.Job + + +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) + } +} diff --git a/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/components/DefaultRadioButton.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/components/DefaultRadioButton.kt new file mode 100644 index 0000000..6fdf076 --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/components/DefaultRadioButton.kt @@ -0,0 +1,39 @@ +package com.note_application.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, + checked: Boolean, + onCheck: () -> Unit, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically + ) { + + RadioButton( + selected = checked, + onClick = onCheck, + colors = RadioButtonDefaults.colors( + selectedColor = MaterialTheme.colorScheme.primary, + unselectedColor = MaterialTheme.colorScheme.onBackground + ) + ) + + Spacer(modifier = Modifier.width(8.dp)) + Text(text = text, style = MaterialTheme.typography.bodyLarge) + } +} diff --git a/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/components/NoteItem.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/components/NoteItem.kt new file mode 100644 index 0000000..d288fe8 --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/components/NoteItem.kt @@ -0,0 +1,111 @@ +package com.note_application.feature_note.presentation.notes.components + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.CornerRadius +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Path +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.note_application.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.fillMaxSize() + ) { + val clipPath = Path().apply { + lineTo(size.width - cutCornerSize.toPx(), size.height) + lineTo(size.width, size.height) + lineTo(size.width, size.height - cutCornerSize.toPx()) + close() + } + + + drawPath( + path = clipPath, + color = Color(note.color) + ) + + 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 = androidx.compose.ui.geometry.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.bodyLarge, + color = MaterialTheme.colorScheme.onSurface, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = note.content, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurface, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + + } + + IconButton( + onClick = onDeleteClick, + modifier = Modifier.align(Alignment.BottomEnd) + ) { + Icon( + imageVector = Icons.Default.Delete, + contentDescription = "Delete note", + tint = MaterialTheme.colorScheme.onSurface + + ) + } + } +} diff --git a/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/components/OrderSection.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/components/OrderSection.kt new file mode 100644 index 0000000..8b3a6af --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/notes/components/OrderSection.kt @@ -0,0 +1,68 @@ +package com.note_application.feature_note.presentation.notes.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.note_application.feature_note.domain.util.NoteOrder +import com.note_application.feature_note.domain.util.OrderType +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.ui.unit.dp + +@Composable +fun OrderSection( + modifier: Modifier = Modifier, + noteOrder: NoteOrder = NoteOrder.Date(OrderType.Descending), + onOrderChange: (NoteOrder) -> Unit + ) { + Column( + modifier = modifier + ) { + Row( + modifier = Modifier.fillMaxWidth() + ){ + DefaultRadioButton( + "Title", + checked = noteOrder is NoteOrder.Title, + onCheck = {onOrderChange(NoteOrder.Title(noteOrder.orderType))} + ) + Spacer(modifier = Modifier.width(8.dp)) + + DefaultRadioButton( + "Date", + checked = noteOrder is NoteOrder.Date, + onCheck = {onOrderChange(NoteOrder.Date(noteOrder.orderType))} + ) + Spacer(modifier = Modifier.width(8.dp)) + + DefaultRadioButton( + "Color", + checked = noteOrder is NoteOrder.Color, + onCheck = {onOrderChange(NoteOrder.Date(noteOrder.orderType))} + ) + } + Spacer(modifier = Modifier.height(16.dp)) + Row( + modifier = Modifier.fillMaxWidth() + ) { + DefaultRadioButton( + "Ascending", + checked = noteOrder.orderType is OrderType.Ascending, + onCheck = { + onOrderChange(noteOrder.copy(OrderType.Ascending))} + ) + Spacer(modifier = Modifier.width(8.dp)) + + DefaultRadioButton( + "Descending", + checked = noteOrder.orderType is OrderType.Descending, + onCheck = { + onOrderChange(noteOrder.copy(OrderType.Descending))} + ) + + } + } +} diff --git a/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/util/Screen.kt b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/util/Screen.kt new file mode 100644 index 0000000..80b1cbe --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/feature_note/presentation/util/Screen.kt @@ -0,0 +1,6 @@ +package com.note_application.feature_note.presentation.util + +sealed class Screen (val route: String){ + object NoteScreen: Screen("note_screen") + object AddEditNoteScreen: Screen("add_edit_note_screen") +} \ No newline at end of file diff --git a/Note_Application/app/src/main/java/com/note_application/ui/theme/Color.kt b/Note_Application/app/src/main/java/com/note_application/ui/theme/Color.kt new file mode 100644 index 0000000..e5bc120 --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/ui/theme/Color.kt @@ -0,0 +1,17 @@ +package com.note_application.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) + +val RedOrange = Color(0xFFFFAB91) +val LightGreen = Color(0xFFE7ED9B) +val Violet = Color(0xFFCF94DA) +val BabyBlue = Color(0xFFAEDFF7) +val RedPink = Color(0xFFF48FB1) \ No newline at end of file diff --git a/Note_Application/app/src/main/java/com/note_application/ui/theme/Theme.kt b/Note_Application/app/src/main/java/com/note_application/ui/theme/Theme.kt new file mode 100644 index 0000000..a162fa9 --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/ui/theme/Theme.kt @@ -0,0 +1,58 @@ +package com.note_application.ui.theme + +import android.app.Activity +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.platform.LocalContext + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun Note_ApplicationTheme( + 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/Note_Application/app/src/main/java/com/note_application/ui/theme/Type.kt b/Note_Application/app/src/main/java/com/note_application/ui/theme/Type.kt new file mode 100644 index 0000000..d8fdd41 --- /dev/null +++ b/Note_Application/app/src/main/java/com/note_application/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package com.note_application.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/Note_Application/app/src/main/res/drawable/ic_launcher_background.xml b/Note_Application/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/Note_Application/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Note_Application/app/src/main/res/drawable/ic_launcher_foreground.xml b/Note_Application/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/Note_Application/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Note_Application/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/Note_Application/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/Note_Application/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Note_Application/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Note_Application/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/Note_Application/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Note_Application/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/Note_Application/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/Note_Application/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/Note_Application/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/Note_Application/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/Note_Application/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/Note_Application/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/Note_Application/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/Note_Application/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/Note_Application/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/Note_Application/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/Note_Application/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/Note_Application/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/Note_Application/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/Note_Application/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/Note_Application/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/Note_Application/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/Note_Application/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/Note_Application/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/Note_Application/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/Note_Application/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/Note_Application/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/Note_Application/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/Note_Application/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/Note_Application/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/Note_Application/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/Note_Application/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/Note_Application/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/Note_Application/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/Note_Application/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/Note_Application/app/src/main/res/values/colors.xml b/Note_Application/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/Note_Application/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/Note_Application/app/src/main/res/values/strings.xml b/Note_Application/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..19fd86f --- /dev/null +++ b/Note_Application/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Note_Application + \ No newline at end of file diff --git a/Note_Application/app/src/main/res/values/themes.xml b/Note_Application/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..0a279d9 --- /dev/null +++ b/Note_Application/app/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + +