diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..c0299dc
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,40 @@
+# This is a workflow to verify that Android build is successful.
+
+name: Android Build test
+
+# Controls when the workflow will run
+on:
+ # Triggers the workflow on push or pull request events but only for the "master" branch
+ push:
+ branches: [ "master", "dev", "branch_hadiyarajesh", "branch_loveleen", "branch_geet", "branch_siddu"]
+ pull_request:
+ branches: [ "master", "dev", "branch_hadiyarajesh", "branch_loveleen", "branch_geet", "branch_siddu"]
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+# A workflow run is made up of one or more jobs that can run sequentially or in parallel
+jobs:
+ # This workflow contains a single job called "build_project"
+ build_project:
+ name: Build NoteX Android project
+ # The type of runner that the job will run on
+ runs-on: ubuntu-latest
+
+ # Steps represent a sequence of tasks that will be executed as part of the job
+ steps:
+ # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
+ - uses: actions/checkout@v3
+ - name: set up JDK 11
+ uses: actions/setup-java@v3
+ with:
+ distribution: 'temurin'
+ java-version: 11
+
+ # Provide required permission to gradle
+ - name: Change gradle wrapper permissions
+ run: chmod +x ./gradlew
+
+ # Build Android project
+ - name: Build Project
+ run: ./gradlew assemble
diff --git a/.gitignore b/.gitignore
index 17b4156..9347128 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,7 @@ local.properties
# Android Studio / IntelliJ IDEA
*.iws
+/.idea
/.idea/libraries
/.idea/tasks.xml
/.idea/caches
@@ -24,6 +25,7 @@ local.properties
/.idea/modules.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
+/.idea/misc.xml
/out/
.DS_Store
diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 26d3352..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index fb7f4a8..b589d56 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index a2d7c21..c43f067 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -7,6 +7,7 @@
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
index a9c58fb..4dadd01 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -3,6 +3,9 @@
+
+
+
@@ -11,7 +14,8 @@
-
+
+
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 4cac126..4848663 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -78,7 +78,7 @@ android {
object LibVersion {
const val composeVersion = "1.2.0"
- const val composeCompilerVersion = "1.2.0"
+ const val composeCompilerVersion = "1.3.2"
const val navigationComposeVersion = "2.5.1"
const val roomVersion = "2.4.2"
const val dataStoreVersion = "1.0.0"
@@ -88,23 +88,28 @@ object LibVersion {
}
dependencies {
- implementation("androidx.core:core-ktx:1.8.0")
- implementation("androidx.activity:activity-compose:1.5.1")
+ val composeBom = platform("androidx.compose:compose-bom:2022.10.00")
+
+ implementation("androidx.core:core-ktx:1.9.0")
+ implementation("androidx.activity:activity-compose:1.6.1")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.5.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")
- implementation("androidx.compose.ui:ui:${LibVersion.composeVersion}")
- implementation("androidx.compose.ui:ui-tooling-preview:${LibVersion.composeVersion}")
- implementation("androidx.compose.material3:material3:1.0.0-alpha16")
+ implementation(composeBom)
+ implementation("androidx.compose.material3:material3")
+ implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.navigation:navigation-compose:${LibVersion.navigationComposeVersion}")
implementation("androidx.datastore:datastore-preferences:${LibVersion.dataStoreVersion}")
- implementation("androidx.paging:paging-compose:1.0.0-alpha16")
- // DO NOT upgrade desugar_jdk_libs to 1.2.0
- coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.6")
+ implementation("androidx.paging:paging-compose:1.0.0-alpha17")
+ implementation("androidx.work:work-runtime-ktx:2.7.1")
+ coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.2.0")
implementation("androidx.hilt:hilt-navigation-compose:1.0.0")
implementation("com.google.dagger:hilt-android:${rootProject.extra["hiltVersion"]}")
+ implementation("androidx.hilt:hilt-work:1.0.0")
kapt("com.google.dagger:hilt-android-compiler:${rootProject.extra["hiltVersion"]}")
+ kapt("androidx.hilt:hilt-compiler:1.0.0")
+ implementation("androidx.room:room-runtime:${LibVersion.roomVersion}")
implementation("androidx.room:room-ktx:${LibVersion.roomVersion}")
implementation("androidx.room:room-paging:${LibVersion.roomVersion}")
kapt("androidx.room:room-compiler:${LibVersion.roomVersion}")
@@ -130,7 +135,8 @@ dependencies {
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.3")
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
- androidTestImplementation("androidx.compose.ui:ui-test-junit4:${LibVersion.composeVersion}")
- debugImplementation("androidx.compose.ui:ui-tooling:${LibVersion.composeVersion}")
- debugImplementation("androidx.compose.ui:ui-test-manifest:${LibVersion.composeVersion}")
+ androidTestImplementation("androidx.compose.ui:ui-test-junit4")
+ androidTestImplementation(composeBom)
+ debugImplementation("androidx.compose.ui:ui-tooling")
+ debugImplementation("androidx.compose.ui:ui-test-manifest")
}
diff --git a/app/src/androidTest/java/com/hadiyarajesh/notex/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/hadiyarajesh/notex/ExampleInstrumentedTest.kt
index 4824663..38b26bc 100644
--- a/app/src/androidTest/java/com/hadiyarajesh/notex/ExampleInstrumentedTest.kt
+++ b/app/src/androidTest/java/com/hadiyarajesh/notex/ExampleInstrumentedTest.kt
@@ -1,13 +1,11 @@
package com.hadiyarajesh.notex
-import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
-
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.Assert.*
-
/**
* Instrumented test, which will execute on an Android device.
*
@@ -21,4 +19,4 @@ class ExampleInstrumentedTest {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.hadiyarajesh.notex", appContext.packageName)
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index afb7c03..6daabbe 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -28,6 +28,14 @@
+
+
+
+
+
diff --git a/app/src/main/java/com/hadiyarajesh/notex/MyApplication.kt b/app/src/main/java/com/hadiyarajesh/notex/MyApplication.kt
index e8ccd78..d3299a0 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/MyApplication.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/MyApplication.kt
@@ -1,7 +1,20 @@
package com.hadiyarajesh.notex
import android.app.Application
+import androidx.hilt.work.HiltWorkerFactory
+import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
+import javax.inject.Inject
@HiltAndroidApp
-class MyApplication: Application()
+class MyApplication : Application(), Configuration.Provider {
+
+ @Inject
+ lateinit var workerFactory: HiltWorkerFactory
+
+ override fun getWorkManagerConfiguration(): Configuration {
+ return Configuration.Builder()
+ .setWorkerFactory(workerFactory)
+ .build()
+ }
+}
diff --git a/app/src/main/java/com/hadiyarajesh/notex/database/converter/InstantConverter.kt b/app/src/main/java/com/hadiyarajesh/notex/database/converter/InstantConverter.kt
index a2aa463..c430c79 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/database/converter/InstantConverter.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/database/converter/InstantConverter.kt
@@ -2,6 +2,10 @@ package com.hadiyarajesh.notex.database.converter
import androidx.room.TypeConverter
import java.time.Instant
+import java.time.LocalDate
+import java.time.LocalDateTime
+import java.time.ZoneOffset
+import java.time.format.DateTimeFormatter
object InstantConverter {
@JvmStatic
@@ -11,4 +15,13 @@ object InstantConverter {
@JvmStatic
@TypeConverter
fun toInstant(value: String?): Instant? = value?.let { Instant.parse(value) }
+
+ @JvmStatic
+ @TypeConverter
+ fun getLocalDate(instant: Instant): LocalDate {
+ val localDateTime: LocalDateTime =
+ LocalDateTime.ofInstant(instant, ZoneOffset.systemDefault())
+ val formatter = DateTimeFormatter.ofPattern("yyyyMMdd")
+ return LocalDate.parse(localDateTime.format(formatter), formatter)
+ }
}
diff --git a/app/src/main/java/com/hadiyarajesh/notex/database/dao/NoteDao.kt b/app/src/main/java/com/hadiyarajesh/notex/database/dao/NoteDao.kt
index cc8147b..7eb05df 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/database/dao/NoteDao.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/database/dao/NoteDao.kt
@@ -1,7 +1,12 @@
package com.hadiyarajesh.notex.database.dao
import androidx.paging.PagingSource
-import androidx.room.*
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import androidx.room.Transaction
import com.hadiyarajesh.notex.database.entity.Note
import com.hadiyarajesh.notex.utility.InternalUseOnly
import java.time.Instant
@@ -20,6 +25,17 @@ interface NoteDao {
updateFolderModificationProperty(parentFolderId)
}
+ /**
+ * This method will update the provided Note and also modify the updatedOn field of the containing Folder(if the note is in a folder)
+ */
+ @Transaction
+ suspend fun updateNote(note: Note, parentFolderId: Long?) {
+ insertOrUpdate(note)
+ parentFolderId?.let {
+ updateFolderModificationProperty(folderId = parentFolderId, updatedOn = note.updatedOn)
+ }
+ }
+
@InternalUseOnly
@Query("UPDATE Note SET parentFolderId = :parentFolderId WHERE noteId = :noteId")
suspend fun attachNoteToFolder(noteId: Long, parentFolderId: Long)
@@ -29,7 +45,7 @@ interface NoteDao {
suspend fun updateFolderModificationProperty(folderId: Long, updatedOn: Instant = Instant.now())
@Query("SELECT * FROM Note WHERE noteId = :noteId")
- fun getById(noteId: Long): Note
+ suspend fun getById(noteId: Long): Note
/**
* This method wil only return non-deleted notes.
@@ -49,24 +65,27 @@ interface NoteDao {
* Mark a note as deleted (archived)
*/
@Query("UPDATE Note SET archived = 1 WHERE noteId = :noteId")
- fun markAsArchived(noteId: Long)
+ suspend fun markAsArchived(noteId: Long)
+
+ @Query("UPDATE Note SET archived = 0 WHERE noteId = :noteId")
+ suspend fun markAsUnarchived(noteId: Long)
@Delete
- fun delete(note: Note)
+ suspend fun delete(note: Note): Int
@Query("DELETE FROM Note WHERE noteId = :noteId")
- fun deleteById(noteId: Long)
+ suspend fun deleteById(noteId: Long): Int
/**
* Permanently delete all notes that are already archived.
*/
@Query("DELETE FROM Note WHERE archived = 1")
- fun deleteAllArchivedNotes()
+ suspend fun deleteAllArchivedNotes(): Int
/**
* WARNING: USE WITH CAUTION
* Permanently delete all notes
*/
@Query("DELETE FROM Note")
- fun deleteAll()
+ suspend fun deleteAll(): Int
}
diff --git a/app/src/main/java/com/hadiyarajesh/notex/database/dao/ReminderDao.kt b/app/src/main/java/com/hadiyarajesh/notex/database/dao/ReminderDao.kt
index 096aaa8..dd2f4be 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/database/dao/ReminderDao.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/database/dao/ReminderDao.kt
@@ -9,7 +9,7 @@ import java.time.Instant
@Dao
interface ReminderDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
- suspend fun insertOrUpdate(reminder: Reminder)
+ suspend fun insertOrUpdate(reminder: Reminder): Long
/**
* This method will add reminder to folder
diff --git a/app/src/main/java/com/hadiyarajesh/notex/database/entity/Folder.kt b/app/src/main/java/com/hadiyarajesh/notex/database/entity/Folder.kt
index bc46b8c..19c1bd9 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/database/entity/Folder.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/database/entity/Folder.kt
@@ -14,7 +14,7 @@ data class Folder(
val title: String,
val description: String?,
val folderType: FolderType,
- //HexCode of a color
+ // HexCode of a color
val color: String? = null,
val createdOn: Instant,
val updatedOn: Instant
diff --git a/app/src/main/java/com/hadiyarajesh/notex/database/entity/Note.kt b/app/src/main/java/com/hadiyarajesh/notex/database/entity/Note.kt
index e829b80..614f251 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/database/entity/Note.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/database/entity/Note.kt
@@ -14,9 +14,10 @@ data class Note(
val content: String?,
val archived: Boolean,
val archivedOn: Instant? = null,
- //HexCode of a color
+ // HexCode of a color
val color: String? = null,
val createdOn: Instant,
val updatedOn: Instant,
val parentFolderId: Long? = null,
)
+
diff --git a/app/src/main/java/com/hadiyarajesh/notex/database/entity/Reminder.kt b/app/src/main/java/com/hadiyarajesh/notex/database/entity/Reminder.kt
index 25c6ed1..4fd8354 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/database/entity/Reminder.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/database/entity/Reminder.kt
@@ -18,7 +18,7 @@ data class Reminder(
val cancelledOn: Instant? = null,
val completed: Boolean,
val completedOn: Instant?,
- //HexCode of a color
+ // HexCode of a color
val color: String? = null,
val createdOn: Instant,
val updatedOn: Instant,
diff --git a/app/src/main/java/com/hadiyarajesh/notex/di/NetworkModule.kt b/app/src/main/java/com/hadiyarajesh/notex/di/NetworkModule.kt
index 90a2639..ad9349e 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/di/NetworkModule.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/di/NetworkModule.kt
@@ -8,11 +8,11 @@ import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
-import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
diff --git a/app/src/main/java/com/hadiyarajesh/notex/di/ScopeModule.kt b/app/src/main/java/com/hadiyarajesh/notex/di/ScopeModule.kt
index e258925..544b04b 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/di/ScopeModule.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/di/ScopeModule.kt
@@ -1,13 +1,14 @@
package com.hadiyarajesh.notex.di
+import com.hadiyarajesh.notex.reminder.notification.NotificationHelper
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
-import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
@Module
@@ -18,4 +19,10 @@ class ScopeModule {
fun provideCoroutineScope(): CoroutineScope {
return CoroutineScope(SupervisorJob() + Dispatchers.IO)
}
+
+ @Singleton
+ @Provides
+ fun provideNotificationHelper(): NotificationHelper {
+ return NotificationHelper()
+ }
}
diff --git a/app/src/main/java/com/hadiyarajesh/notex/network/api/NoteApi.kt b/app/src/main/java/com/hadiyarajesh/notex/network/api/NoteApi.kt
index 7d7f3b7..27c7bce 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/network/api/NoteApi.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/network/api/NoteApi.kt
@@ -1,4 +1,3 @@
package com.hadiyarajesh.notex.network.api
-interface NoteApi {
-}
+interface NoteApi
diff --git a/app/src/main/java/com/hadiyarajesh/notex/reminder/notification/NotificationBroadCastReceiver.kt b/app/src/main/java/com/hadiyarajesh/notex/reminder/notification/NotificationBroadCastReceiver.kt
new file mode 100644
index 0000000..ef09e5a
--- /dev/null
+++ b/app/src/main/java/com/hadiyarajesh/notex/reminder/notification/NotificationBroadCastReceiver.kt
@@ -0,0 +1,64 @@
+package com.hadiyarajesh.notex.reminder.notification
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.util.Log
+import androidx.annotation.CallSuper
+import androidx.core.app.NotificationManagerCompat
+import com.hadiyarajesh.notex.R
+import com.hadiyarajesh.notex.reminder.worker.ReminderWorkManager
+import dagger.hilt.android.AndroidEntryPoint
+import java.time.Instant
+import java.time.temporal.ChronoUnit
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class NotificationBroadCastReceiver : HiltBroadcastReceiver() {
+
+ companion object {
+ const val TAG = "NotificationBroadCast"
+ }
+
+ @Inject
+ lateinit var reminderWorkManager: ReminderWorkManager
+
+ override fun onReceive(context: Context, intent: Intent) {
+ super.onReceive(context, intent)
+ when (intent.action) {
+ context.resources.getString(R.string.done_action) ->
+ reminderWorkManager.cancelWorkRequest(
+ context,
+ intent.getStringExtra(context.resources.getString(R.string.worker_tag)) ?: ""
+ )
+ context.resources.getString(R.string.postpone_action) -> {
+ reminderWorkManager.cancelWorkRequest(
+ context,
+ intent.getStringExtra(context.resources.getString(R.string.worker_tag)) ?: ""
+ )
+
+ reminderWorkManager.createWorkRequestAndEnqueue(
+ context,
+ time = Instant.now().plus(1, ChronoUnit.HOURS),
+ isFirstTime = false,
+ reminderId = intent.getLongExtra(
+ context.resources.getString(R.string.reminder_id),
+ -1
+ )
+ )
+ }
+ else -> Log.i(TAG, "Nothing to Perform this Action ${intent.action}")
+ }
+
+ with(NotificationManagerCompat.from(context)) {
+ cancel(intent.getIntExtra(context.resources.getString(R.string.notification_id), -1))
+ }
+ }
+}
+
+abstract class HiltBroadcastReceiver : BroadcastReceiver() {
+ @SuppressWarnings("EmptyFunctionBlock")
+ @CallSuper
+ override fun onReceive(context: Context, intent: Intent) {
+ }
+}
diff --git a/app/src/main/java/com/hadiyarajesh/notex/reminder/notification/NotificationDTO.kt b/app/src/main/java/com/hadiyarajesh/notex/reminder/notification/NotificationDTO.kt
new file mode 100644
index 0000000..f51b6fa
--- /dev/null
+++ b/app/src/main/java/com/hadiyarajesh/notex/reminder/notification/NotificationDTO.kt
@@ -0,0 +1,8 @@
+package com.hadiyarajesh.notex.reminder.notification
+
+data class NotificationDTO(
+ val title: String,
+ val subTitle: String,
+ val reminderId: Long,
+ val workerTag: String
+)
diff --git a/app/src/main/java/com/hadiyarajesh/notex/reminder/notification/NotificationHelper.kt b/app/src/main/java/com/hadiyarajesh/notex/reminder/notification/NotificationHelper.kt
new file mode 100644
index 0000000..4e3f37a
--- /dev/null
+++ b/app/src/main/java/com/hadiyarajesh/notex/reminder/notification/NotificationHelper.kt
@@ -0,0 +1,67 @@
+package com.hadiyarajesh.notex.reminder.notification
+
+import android.app.PendingIntent
+import android.app.PendingIntent.FLAG_IMMUTABLE
+import android.content.Context
+import android.content.Intent
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
+import com.hadiyarajesh.notex.MainActivity
+import com.hadiyarajesh.notex.R
+import kotlin.random.Random
+
+class NotificationHelper {
+
+ fun createNotification(
+ context: Context,
+ notificationDTO: NotificationDTO
+ ) {
+ val actionIntent = Intent(context, MainActivity::class.java)
+ val actionPendingIntent = PendingIntent.getActivity(
+ context, 0,
+ actionIntent, PendingIntent.FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE
+ )
+
+ @SuppressWarnings("MagicNumber")
+ val notificationId = Random(121).nextInt(10000)
+
+ val postponeIntent = Intent(context, NotificationBroadCastReceiver::class.java).apply {
+ action = context.resources.getString(R.string.postpone_action)
+ putExtra(context.resources.getString(R.string.worker_tag), notificationDTO.workerTag)
+ putExtra(context.resources.getString(R.string.reminder_id), notificationDTO.reminderId)
+ putExtra(context.resources.getString(R.string.notification_id), notificationId)
+ }
+
+ val postponePendingIntent = PendingIntent.getBroadcast(
+ context, 0,
+ postponeIntent, PendingIntent.FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE
+ )
+
+ val cancelIntent = Intent(context, NotificationBroadCastReceiver::class.java).apply {
+ action = context.resources.getString(R.string.done_action)
+ putExtra(context.resources.getString(R.string.worker_tag), notificationDTO.workerTag)
+ putExtra(context.resources.getString(R.string.notification_id), notificationId)
+ }
+
+ val cancelPendingIntent = PendingIntent.getBroadcast(
+ context, 0,
+ cancelIntent, PendingIntent.FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE
+ )
+
+ val builder =
+ NotificationCompat.Builder(context, context.getString(R.string.notification_channel_id))
+ .setSmallIcon(R.drawable.ic_note_filled)
+ .setContentTitle(notificationDTO.title)
+ .setContentText(notificationDTO.subTitle)
+ .setPriority(NotificationCompat.PRIORITY_DEFAULT)
+ .setContentIntent(actionPendingIntent)
+ .addAction(R.drawable.ic_note_filled, context.getString(R.string.one_hour), postponePendingIntent)
+ .addAction(R.drawable.ic_note_filled, context.getString(R.string.done), cancelPendingIntent)
+ .setAutoCancel(true)
+
+ with(NotificationManagerCompat.from(context)) {
+ // notificationId is a unique int for each notification that you must define
+ notify(notificationId, builder.build())
+ }
+ }
+}
diff --git a/app/src/main/java/com/hadiyarajesh/notex/reminder/worker/ReminderWorkManager.kt b/app/src/main/java/com/hadiyarajesh/notex/reminder/worker/ReminderWorkManager.kt
new file mode 100644
index 0000000..39a2851
--- /dev/null
+++ b/app/src/main/java/com/hadiyarajesh/notex/reminder/worker/ReminderWorkManager.kt
@@ -0,0 +1,76 @@
+package com.hadiyarajesh.notex.reminder.worker
+
+import android.content.Context
+import androidx.work.Data
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.WorkManager
+import androidx.work.WorkRequest
+import com.hadiyarajesh.notex.R
+import com.hadiyarajesh.notex.database.dao.ReminderDao
+import com.hadiyarajesh.notex.database.model.RepetitionStrategy
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import java.time.Instant
+import java.time.temporal.ChronoUnit
+import java.util.concurrent.TimeUnit
+import javax.inject.Inject
+
+class ReminderWorkManager @Inject constructor(var reminderDao: ReminderDao) {
+ fun createWorkRequestAndEnqueue(
+ context: Context,
+ reminderId: Long,
+ time: Instant,
+ isFirstTime: Boolean = true
+ ) {
+ CoroutineScope(Dispatchers.IO).launch {
+ val data: Data.Builder = Data.Builder()
+ data.putLong(
+ context.resources.getString(R.string.reminder_instance_key),
+ reminderId
+ )
+
+ val workerTag =
+ "${context.resources.getString(R.string.reminder_worker_tag)}$reminderId"
+
+ data.putString(context.resources.getString(R.string.worker_tag), workerTag)
+
+ val reminder = reminderDao.getById(reminderId)
+
+ val initialDelay = if (isFirstTime) {
+ time.toEpochMilli() - Instant.now().toEpochMilli()
+ } else {
+ getDurationInMilli(reminderStrategy = reminder.repeat, reminderTime = time)
+ }
+
+ val dailyWorkRequest: WorkRequest = OneTimeWorkRequestBuilder()
+ .setInitialDelay(
+ initialDelay,
+ TimeUnit.MILLISECONDS
+ )
+ .setInputData(data.build())
+ .addTag(workerTag)
+ .build()
+ WorkManager.getInstance(context)
+ .enqueue(dailyWorkRequest)
+ }
+ }
+
+ private fun getDurationInMilli(
+ reminderStrategy: RepetitionStrategy,
+ reminderTime: Instant
+ ): Long {
+ val duration: Long
+ when (reminderStrategy) {
+ RepetitionStrategy.Daily -> duration = Instant.now().plus(1, ChronoUnit.DAYS).toEpochMilli()
+ RepetitionStrategy.Monthly -> duration = Instant.now().plus(1, ChronoUnit.MONTHS).toEpochMilli()
+ RepetitionStrategy.Yearly -> duration = Instant.now().plus(1, ChronoUnit.YEARS).toEpochMilli()
+ RepetitionStrategy.Weekly -> duration = Instant.now().plus(1, ChronoUnit.WEEKS).toEpochMilli()
+ else -> duration = reminderTime.toEpochMilli()
+ }
+ return duration - Instant.now().toEpochMilli()
+ }
+
+ fun cancelWorkRequest(context: Context, tag: String) =
+ WorkManager.getInstance(context).cancelAllWorkByTag(tag)
+}
diff --git a/app/src/main/java/com/hadiyarajesh/notex/reminder/worker/ReminderWorker.kt b/app/src/main/java/com/hadiyarajesh/notex/reminder/worker/ReminderWorker.kt
new file mode 100644
index 0000000..493fbb8
--- /dev/null
+++ b/app/src/main/java/com/hadiyarajesh/notex/reminder/worker/ReminderWorker.kt
@@ -0,0 +1,60 @@
+package com.hadiyarajesh.notex.reminder.worker
+
+import android.content.Context
+import androidx.hilt.work.HiltWorker
+import androidx.work.CoroutineWorker
+import androidx.work.WorkerParameters
+import com.hadiyarajesh.notex.R
+import com.hadiyarajesh.notex.database.dao.ReminderDao
+import com.hadiyarajesh.notex.database.entity.Reminder
+import com.hadiyarajesh.notex.database.model.RepetitionStrategy
+import com.hadiyarajesh.notex.reminder.notification.NotificationDTO
+import com.hadiyarajesh.notex.reminder.notification.NotificationHelper
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import java.time.LocalDateTime
+import java.time.ZoneOffset
+
+@HiltWorker
+class ReminderWorker @AssistedInject constructor(
+ @Assisted appContext: Context,
+ @Assisted workerParams: WorkerParameters,
+ val reminderDao: ReminderDao,
+ private val notificationHelper: NotificationHelper
+) : CoroutineWorker(appContext, workerParams) {
+
+ private val reminderWorkManager = ReminderWorkManager(reminderDao)
+
+ override suspend fun doWork(): Result {
+ val reminderId =
+ inputData.getLong(applicationContext.getString(R.string.reminder_instance_key), -1)
+ if (reminderId == -1L) {
+ return Result.failure()
+ }
+ val reminder: Reminder = reminderDao.getById(reminderId)
+
+ val localDate: LocalDateTime =
+ LocalDateTime.ofInstant(reminder.reminderTime, ZoneOffset.UTC)
+
+ notificationHelper.createNotification(
+ applicationContext,
+ NotificationDTO(
+ title = reminder.content,
+ subTitle = "${localDate.hour}:${localDate.minute}",
+ reminderId = reminderId,
+ workerTag = inputData.getString(applicationContext.getString(R.string.worker_tag))
+ ?: ""
+ )
+ )
+
+ if (reminder.repeat != RepetitionStrategy.None) {
+ reminderWorkManager.createWorkRequestAndEnqueue(
+ reminderId = reminderId,
+ context = applicationContext,
+ isFirstTime = false,
+ time = reminder.reminderTime
+ )
+ }
+ return Result.success()
+ }
+}
diff --git a/app/src/main/java/com/hadiyarajesh/notex/repository/folder/FolderRepository.kt b/app/src/main/java/com/hadiyarajesh/notex/repository/folder/FolderRepository.kt
index af35fc4..667b635 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/repository/folder/FolderRepository.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/repository/folder/FolderRepository.kt
@@ -1,8 +1,14 @@
package com.hadiyarajesh.notex.repository.folder
+import androidx.paging.Pager
+import androidx.paging.PagingConfig
+import androidx.paging.PagingData
import com.hadiyarajesh.notex.database.dao.FolderDao
import com.hadiyarajesh.notex.database.entity.Folder
import com.hadiyarajesh.notex.database.model.FolderType
+import com.hadiyarajesh.notex.database.model.NoteFolder
+import com.hadiyarajesh.notex.database.model.ReminderFolder
+import kotlinx.coroutines.flow.Flow
import java.time.Instant
import javax.inject.Inject
import javax.inject.Singleton
@@ -26,4 +32,16 @@ class FolderRepository @Inject constructor(
)
)
}
+
+ fun getAllNoteFolders(): Flow> = Pager(
+ config = PagingConfig(pageSize = 8)//since we want a maximum of 8 folders in one screen
+ ){
+ folderDao.getAllNoteFolders(FolderType.NoteFolder)
+ }.flow
+
+ fun getAllReminderFolders(): Flow> = Pager(
+ config = PagingConfig(pageSize = 8)//since we want a maximum of 8 folders in one screen
+ ){
+ folderDao.getAllReminderFolders(FolderType.ReminderFolder)
+ }.flow
}
diff --git a/app/src/main/java/com/hadiyarajesh/notex/repository/notes/NotesRepository.kt b/app/src/main/java/com/hadiyarajesh/notex/repository/notes/NotesRepository.kt
index 5b6a8d3..8e4b8f8 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/repository/notes/NotesRepository.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/repository/notes/NotesRepository.kt
@@ -5,10 +5,10 @@ import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.hadiyarajesh.notex.database.dao.NoteDao
import com.hadiyarajesh.notex.database.entity.Note
-import kotlinx.coroutines.flow.Flow
import java.time.Instant
import javax.inject.Inject
import javax.inject.Singleton
+import kotlinx.coroutines.flow.Flow
@Singleton
class NotesRepository @Inject constructor(
@@ -35,4 +35,36 @@ class NotesRepository @Inject constructor(
) {
noteDao.getAllUnArchivedByDesc()
}.flow
+
+ suspend fun getNote(noteId: Long): Note = noteDao.getById(noteId)
+
+ /**
+ * Inserts the provided Note into the database.
+ */
+ suspend fun addNote(note: Note) = noteDao.insertOrUpdate(note)
+
+ /**
+ * Updates the provided Note in the database.
+ */
+ suspend fun updateNote(note: Note,parentFolderId: Long?) = noteDao.updateNote(note, parentFolderId)
+
+
+
+ suspend fun deleteNote(note: Note): Int = noteDao.delete(note)
+
+ suspend fun deleteNote(noteId: Long): Int = noteDao.deleteById(noteId)
+
+ suspend fun deleteArchived(): Int = noteDao.deleteAllArchivedNotes()
+
+ suspend fun deleteAllNotes(): Int = noteDao.deleteAll()
+
+
+
+ suspend fun archive(noteId: Long) = noteDao.markAsArchived(noteId)
+
+ suspend fun unarchive(noteId: Long) = noteDao.markAsUnarchived(noteId)
+
+
+
+ suspend fun addToFolder(noteId: Long, folderId: Long) = noteDao.addToFolder(noteId,folderId)
}
diff --git a/app/src/main/java/com/hadiyarajesh/notex/repository/reminders/RemindersRepository.kt b/app/src/main/java/com/hadiyarajesh/notex/repository/reminders/RemindersRepository.kt
index 6e0c994..692a033 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/repository/reminders/RemindersRepository.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/repository/reminders/RemindersRepository.kt
@@ -1,15 +1,17 @@
package com.hadiyarajesh.notex.repository.reminders
+import android.content.Context
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.hadiyarajesh.notex.database.dao.ReminderDao
import com.hadiyarajesh.notex.database.entity.Reminder
import com.hadiyarajesh.notex.database.model.RepetitionStrategy
-import kotlinx.coroutines.flow.Flow
+import com.hadiyarajesh.notex.reminder.worker.ReminderWorkManager
import java.time.Instant
import javax.inject.Inject
import javax.inject.Singleton
+import kotlinx.coroutines.flow.Flow
@Singleton
class RemindersRepository @Inject constructor(
@@ -18,20 +20,33 @@ class RemindersRepository @Inject constructor(
suspend fun createReminder(
title: String,
reminderTime: Instant,
- repeat: RepetitionStrategy
+ repeat: RepetitionStrategy,
+ context: Context? = null
) {
- reminderDao.insertOrUpdate(
- Reminder(
- content = title,
- reminderTime = reminderTime,
- repeat = repeat,
- cancelled = false,
- completed = false,
- completedOn = null,
- createdOn = Instant.now(),
- updatedOn = Instant.now(),
- )
+ val reminder = Reminder(
+ content = title,
+ reminderTime = reminderTime,
+ repeat = repeat,
+ cancelled = false,
+ completed = false,
+ completedOn = null,
+ createdOn = Instant.now(),
+ updatedOn = Instant.now(),
)
+
+ val reminderId = reminderDao.insertOrUpdate(
+ reminder
+ )
+
+ val reminderWorkManager = ReminderWorkManager(reminderDao)
+ reminderWorkManager.reminderDao = reminderDao
+ if (context != null) {
+ reminderWorkManager.createWorkRequestAndEnqueue(
+ context,
+ reminderId = reminderId,
+ time = reminderTime
+ )
+ }
}
fun getAllReminders(): Flow> = Pager(
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/NoteXApp.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/NoteXApp.kt
index 8788091..8147644 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/ui/NoteXApp.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/NoteXApp.kt
@@ -10,10 +10,11 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
+import androidx.navigation.NavGraph.Companion.findStartDestination
+import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.hadiyarajesh.notex.ui.navigation.MainBottomBar
import com.hadiyarajesh.notex.ui.navigation.NoteXNavigation
-import com.hadiyarajesh.notex.ui.navigation.Screens
import com.hadiyarajesh.notex.ui.navigation.bottomNavItems
import com.hadiyarajesh.notex.ui.theme.NoteXTheme
@@ -23,7 +24,7 @@ fun NoteXApp() {
NoteXTheme {
val navController = rememberNavController()
// A state that maintains visibility of a bottom bar
- val bottomBarState = rememberSaveable { (mutableStateOf(true)) }
+ val bottomBarState = rememberSaveable { mutableStateOf(true) }
Scaffold(
bottomBar = {
@@ -33,11 +34,17 @@ fun NoteXApp() {
exit = slideOutVertically(targetOffsetY = { it }),
) {
MainBottomBar(
- navController = navController,
- items = bottomNavItems,
- onFABClick = {
- navController.navigate(Screens.AddNote.route)
- }
+ destinations = bottomNavItems,
+ onNavigateToDestination = { destination ->
+ navController.navigate(destination.route) {
+ popUpTo(navController.graph.findStartDestination().id) {
+ saveState = true
+ }
+ launchSingleTop = true
+ restoreState = true
+ }
+ },
+ currentDestination = navController.currentBackStackEntryAsState().value?.destination
)
}
}
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/component/AllNotesComponents.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/component/AllNotesComponents.kt
new file mode 100644
index 0000000..af4c75e
--- /dev/null
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/component/AllNotesComponents.kt
@@ -0,0 +1,108 @@
+package com.hadiyarajesh.notex.ui.component
+
+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.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.hadiyarajesh.notex.database.converter.InstantConverter
+import com.hadiyarajesh.notex.database.entity.Note
+import java.time.Instant
+
+@Composable
+fun NoteCard(note: Note, onClick: (Note) -> Unit) {
+ Card(
+ elevation = CardDefaults.cardElevation(),
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(start = 16.dp, end = 16.dp, top = 16.dp)
+ .clickable { onClick(note) },
+ shape = RoundedCornerShape(16.dp)
+ ) {
+ Column(
+ modifier = Modifier.padding(16.dp),
+ verticalArrangement = Arrangement.Center
+ ) {
+ note.title?.let {
+ Text(
+ text = it,
+ fontWeight = FontWeight.SemiBold,
+ style = MaterialTheme.typography.titleLarge,
+ maxLines = 1,
+ )
+ }
+
+ note.content?.let {
+ Text(
+ text = it,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ }
+
+ Row(
+ Modifier
+ .fillMaxWidth()
+ .padding(top = 16.dp)
+ ) {
+// Row(
+// modifier = Modifier
+// .fillMaxWidth()
+// .height(IntrinsicSize.Min)
+// .weight(1f)
+// ) {
+// TextSemiBold(
+// content = "Succeed", Modifier.padding(end = 8.dp)
+// )
+// Divider(
+// color = Color.Gray,
+// modifier = Modifier
+// .fillMaxHeight()
+// .width(1.dp)
+// )
+// TextSemiBold(content = "Goal", Modifier.padding(start = 8.dp))
+// }
+
+ Row(
+ Modifier
+ .fillMaxWidth()
+ .weight(1f),
+ horizontalArrangement = Arrangement.End
+ ) {
+ TextSemiBold(
+ content = InstantConverter.getLocalDate(note.createdOn).toString()
+ )
+ }
+
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+fun NoteCardPrev() {
+ NoteCard(
+ Note(
+ noteId = 12345,
+ title = "Note title",
+ content = "Note content",
+ archived = false,
+ createdOn = Instant.now(),
+ updatedOn = Instant.now()
+ ),
+ onClick = {}
+ )
+}
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/component/BorderLessTextField.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/component/BorderLessTextField.kt
new file mode 100644
index 0000000..6a2800d
--- /dev/null
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/component/BorderLessTextField.kt
@@ -0,0 +1,40 @@
+package com.hadiyarajesh.notex.ui.component
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.text.TextStyle
+
+@Composable
+fun BorderLessTextField(
+ text: String,
+ hint: String,
+ onValueChange: (String) -> Unit,
+ textStyle: TextStyle = TextStyle(),
+ maxLines: Int = Int.MAX_VALUE,
+) {
+ BasicTextField(
+ modifier = Modifier.fillMaxWidth(),
+ value = text,
+ onValueChange = onValueChange,
+ maxLines = maxLines,
+ textStyle = textStyle,
+// cursorBrush = SolidColor(hintColor),
+ cursorBrush = SolidColor(MaterialTheme.colorScheme.onBackground),
+ decorationBox = { inlineTextField ->
+ AnimatedVisibility(visible = text.isBlank()) {
+ Text(
+ text = hint,
+// color = hintColor,
+ style = textStyle
+ )
+ }
+ inlineTextField()
+ }
+ )
+}
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/component/Other.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/component/Other.kt
index c6ff11d..18dca7e 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/ui/component/Other.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/component/Other.kt
@@ -84,7 +84,6 @@ fun LoadingProgressBar(
color: Color = MaterialTheme.colorScheme.primary,
strokeWidth: Dp = 4.dp
) {
-
Box(modifier = modifier) {
CircularProgressIndicator(
modifier = Modifier
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/component/TextComposables.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/component/TextComposables.kt
new file mode 100644
index 0000000..df219b4
--- /dev/null
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/component/TextComposables.kt
@@ -0,0 +1,23 @@
+package com.hadiyarajesh.notex.ui.component
+
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontWeight
+
+@Composable
+fun TextSemiBold(
+ content: String,
+ modifier: Modifier? = Modifier,
+ textStyle: TextStyle? = null,
+ color: Color? = null
+) {
+ Text(
+ text = content, fontWeight = FontWeight.SemiBold,
+ color = color ?: Color.Gray,
+ modifier = modifier ?: Modifier,
+ style = textStyle ?: TextStyle.Default
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/folders/FoldersScreen.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/folders/FoldersScreen.kt
new file mode 100644
index 0000000..1a35d03
--- /dev/null
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/folders/FoldersScreen.kt
@@ -0,0 +1,77 @@
+package com.hadiyarajesh.notex.ui.folders
+
+import androidx.compose.foundation.layout.*
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Scaffold
+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.platform.LocalContext
+import androidx.compose.ui.tooling.preview.Devices
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.navigation.NavController
+import androidx.paging.compose.LazyPagingItems
+import androidx.paging.compose.collectAsLazyPagingItems
+import com.hadiyarajesh.notex.database.entity.Folder
+import com.hadiyarajesh.notex.database.model.NoteFolder
+import com.hadiyarajesh.notex.database.model.ReminderFolder
+
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun FoldersScreen(
+ navController: NavController,
+ foldersViewModel: FoldersViewModel
+) {
+ val scope = rememberCoroutineScope()
+ val context = LocalContext.current
+ val notesFolders = remember { foldersViewModel.noteFolders }.collectAsLazyPagingItems()
+ val reminderFolders = remember { foldersViewModel.reminderFolders }.collectAsLazyPagingItems()
+
+ Scaffold { innerPadding ->
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(innerPadding),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ AllFoldersView(onClick = {})
+ }
+
+ }
+
+}
+
+
+
+@Composable
+private fun AllFoldersView(
+ modifier: Modifier = Modifier,
+ noteFolders: LazyPagingItems? = null,
+ reminderFolder: LazyPagingItems? = null,
+ onClick: (Folder) -> Unit
+) {
+ Column(modifier = Modifier.padding(16.dp)) {
+ Text(
+ text = "Folders Screen",
+ modifier = Modifier.fillMaxWidth()
+ )
+
+ }
+}
+
+@Preview(
+ name = "All Folders Preview",
+ showBackground = true,
+ showSystemUi = true,
+ device = Devices.PIXEL_2_XL
+)
+@Composable
+fun AllFoldersViewPreview(){
+ AllFoldersView(onClick = {})
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/folders/FoldersViewModel.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/folders/FoldersViewModel.kt
new file mode 100644
index 0000000..3efc80a
--- /dev/null
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/folders/FoldersViewModel.kt
@@ -0,0 +1,38 @@
+package com.hadiyarajesh.notex.ui.folders
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import androidx.paging.PagingData
+import androidx.paging.cachedIn
+import com.hadiyarajesh.notex.database.entity.Folder
+import com.hadiyarajesh.notex.database.model.FolderType
+import com.hadiyarajesh.notex.database.model.NoteFolder
+import com.hadiyarajesh.notex.database.model.ReminderFolder
+import com.hadiyarajesh.notex.repository.folder.FolderRepository
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class FoldersViewModel @Inject constructor(
+ private val folderRepository: FolderRepository
+) : ViewModel() {
+
+ val noteFolders: Flow> =
+ folderRepository.getAllNoteFolders().cachedIn(viewModelScope)
+
+ val reminderFolders : Flow> =
+ folderRepository.getAllReminderFolders().cachedIn(viewModelScope)
+
+
+ fun createFolder(
+ name: String,
+ description: String?,
+ folderType: FolderType
+ ) = viewModelScope.launch {
+ folderRepository.createFolder(
+ name, description, folderType
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/folders/NoteFolderScreen.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/folders/NoteFolderScreen.kt
new file mode 100644
index 0000000..f859476
--- /dev/null
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/folders/NoteFolderScreen.kt
@@ -0,0 +1,266 @@
+package com.hadiyarajesh.notex.ui.folders
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
+import androidx.compose.foundation.lazy.grid.items
+import androidx.compose.foundation.lazy.grid.rememberLazyGridState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+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.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.navigation.NavController
+import androidx.paging.compose.collectAsLazyPagingItems
+import com.hadiyarajesh.notex.R
+import com.hadiyarajesh.notex.database.entity.Folder
+import com.hadiyarajesh.notex.database.model.FolderType.NoteFolder
+import com.hadiyarajesh.notex.ui.navigation.Screens
+import java.time.Instant
+
+@Composable
+fun NoteFolderScreen(
+ modifier: Modifier = Modifier,
+ navController: NavController,
+ folderViewModel: NoteFolderViewModel
+) {
+ val scope = rememberCoroutineScope()
+ val context = LocalContext.current
+ val folders = remember { folderViewModel.getFolders() }.collectAsLazyPagingItems()
+
+ val data = listOf(
+ Folder(
+ folderId = 12345,
+ title = "Android",
+ description = "Android Notes",
+ folderType = NoteFolder,
+ createdOn = Instant.now(),
+ updatedOn = Instant.now()
+ ),
+ Folder(
+ folderId = 12346,
+ title = "Android1",
+ description = "Android Notes",
+ folderType = NoteFolder,
+ createdOn = Instant.now(),
+ updatedOn = Instant.now()
+ ),
+ Folder(
+ folderId = 12347,
+ title = "Android2",
+ description = "Android Notes",
+ folderType = NoteFolder,
+ createdOn = Instant.now(),
+ updatedOn = Instant.now()
+ ),
+ Folder(
+ folderId = 12348,
+ title = "Android3",
+ description = "Android Notes",
+ folderType = NoteFolder,
+ createdOn = Instant.now(),
+ updatedOn = Instant.now()
+ ),
+ Folder(
+ folderId = 12349,
+ title = "Android4",
+ description = "Android Notes",
+ folderType = NoteFolder,
+ createdOn = Instant.now(),
+ updatedOn = Instant.now()
+ )
+ )
+ val state = rememberLazyGridState()
+ Surface {
+ Column(
+ modifier = modifier.fillMaxSize()
+ ) {
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .padding(10.dp),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Text(
+ text = stringResource(id = R.string.categories),
+ style = MaterialTheme.typography.headlineMedium,
+ modifier = modifier.padding(start = 120.dp)
+ )
+ Icon(
+ painter = painterResource(id = R.drawable.ic_add_circle_outline),
+ contentDescription = "add_note",
+ modifier = modifier
+ .padding(end = 10.dp)
+ .clickable {
+ navController.navigate(Screens.AddNote.route)
+ }
+ )
+
+ }
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(20.dp),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = stringResource(id = R.string.list_categories),
+ style = MaterialTheme.typography.bodyMedium
+ )
+ }
+
+ LazyVerticalGrid(
+ modifier = modifier.padding(8.dp),
+ columns = GridCells.Fixed(2),
+ verticalArrangement = Arrangement.spacedBy(8.dp),
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ state = state,
+ ) {
+ items(
+ items = data,
+ key = {
+ it.title
+ }
+ ) { noteItem ->
+ NoteFolderItemUI(onClick = {}, folder = noteItem)
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun NoteFolderItemUI(
+ modifier: Modifier = Modifier,
+ onClick: () -> Unit,
+ folder: Folder,
+) {
+ Box(
+ modifier = modifier
+ .padding(8.dp)
+ .aspectRatio(1f)
+ .clip(RoundedCornerShape(8.dp))
+ .background(Color.LightGray),
+ contentAlignment = Alignment.Center
+ ) {
+ Column(
+ modifier = modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Icon(painterResource(id = R.drawable.ic_folder_filled), contentDescription = null)
+
+ Text(
+ text = "Item ${folder.title}",
+ style = MaterialTheme.typography.bodyLarge,
+ fontWeight = FontWeight.Bold
+ )
+
+ Text(
+ text = "${folder.description}",
+ style = MaterialTheme.typography.bodyMedium
+ )
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun NoteFolderScreenPreview() {
+ val state = rememberLazyGridState()
+ Surface {
+ Column(
+ modifier = Modifier.fillMaxSize()
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(10.dp),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = stringResource(id = R.string.categories),
+ style = MaterialTheme.typography.headlineMedium,
+ modifier = Modifier.padding(start = 130.dp)
+ )
+
+ Icon(
+ painter = painterResource(id = R.drawable.ic_add_circle_outline),
+ contentDescription = "add_note",
+ modifier = Modifier
+ .padding(end = 10.dp)
+ )
+ }
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(10.dp),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = stringResource(id = R.string.list_categories),
+ style = MaterialTheme.typography.bodyMedium
+ )
+ }
+
+ LazyVerticalGrid(
+ columns = GridCells.Fixed(2),
+ verticalArrangement = Arrangement.spacedBy(8.dp),
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ state = state
+ ) {
+ items(100) { noteFolderItems ->
+ Box(
+ modifier = Modifier
+ .padding(8.dp)
+ .aspectRatio(1f)
+ .clip(RoundedCornerShape(8.dp))
+ .background(Color.LightGray),
+ contentAlignment = Alignment.Center
+ ) {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Icon(
+ painterResource(id = R.drawable.ic_folder_filled),
+ contentDescription = null
+ )
+
+ Text(
+ text = "Item $noteFolderItems",
+ style = MaterialTheme.typography.bodyLarge,
+ fontWeight = FontWeight.Bold
+ )
+
+ Text(
+ text = "$noteFolderItems item description",
+ style = MaterialTheme.typography.bodyMedium
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/folders/NoteFolderViewModel.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/folders/NoteFolderViewModel.kt
new file mode 100644
index 0000000..71b5c28
--- /dev/null
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/folders/NoteFolderViewModel.kt
@@ -0,0 +1,34 @@
+package com.hadiyarajesh.notex.ui.folders
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import androidx.paging.PagingData
+import androidx.paging.cachedIn
+import com.hadiyarajesh.notex.database.model.FolderType
+import com.hadiyarajesh.notex.database.model.NoteFolder
+import com.hadiyarajesh.notex.repository.folder.FolderRepository
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class NoteFolderViewModel @Inject constructor(
+ private val folderRepository: FolderRepository
+) : ViewModel() {
+ fun createFolder(
+ name: String,
+ description: String?,
+ folderType: FolderType
+ ) = viewModelScope.launch {
+ folderRepository.createFolder(
+ name = name,
+ description = description,
+ folderType = folderType
+ )
+ }
+
+ fun getFolders(): Flow> {
+ return folderRepository.getAllNoteFolders().cachedIn(viewModelScope)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/navigation/NoteXNavigation.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/navigation/NoteXNavigation.kt
index 70550bf..f60ec2b 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/ui/navigation/NoteXNavigation.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/navigation/NoteXNavigation.kt
@@ -1,26 +1,28 @@
package com.hadiyarajesh.notex.ui.navigation
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.Add
-import androidx.compose.material3.BottomAppBar
-import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
+import androidx.compose.material3.NavigationBar
+import androidx.compose.material3.NavigationBarItem
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
-import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
-import androidx.navigation.NavController
-import androidx.navigation.NavGraph.Companion.findStartDestination
+import androidx.navigation.NavDestination
+import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavHostController
+import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
-import androidx.navigation.compose.currentBackStackEntryAsState
-import com.hadiyarajesh.notex.ui.note.NotesScreen
-import com.hadiyarajesh.notex.ui.note.NotesViewModel
+import androidx.navigation.navArgument
+import com.hadiyarajesh.notex.ui.folders.NoteFolderScreen
+import com.hadiyarajesh.notex.ui.folders.NoteFolderViewModel
import com.hadiyarajesh.notex.ui.note.add.AddNoteScreen
+import com.hadiyarajesh.notex.ui.note.add.AddNotesViewModel
+import com.hadiyarajesh.notex.ui.note.all.NotesScreen
+import com.hadiyarajesh.notex.ui.note.all.NotesViewModel
import com.hadiyarajesh.notex.ui.reminders.RemindersScreen
import com.hadiyarajesh.notex.ui.reminders.RemindersViewModel
@@ -45,9 +47,25 @@ fun NoteXNavigation(
)
}
- composable(route = Screens.AddNote.route) {
+ composable(
+ route = Screens.AddNote.route + "?noteId={noteId}",
+ arguments = listOf(
+ navArgument(name = "noteId") {
+ type = NavType.LongType
+ defaultValue = -1
+ }
+ )
+ ) { backStackEntry ->
bottomBarState.value = false
- AddNoteScreen(navController = navController)
+
+ val noteId = backStackEntry.arguments?.getLong("noteId")
+ val addNotesViewModel: AddNotesViewModel = hiltViewModel()
+
+ AddNoteScreen(
+ navController = navController,
+ addNotesViewModel = addNotesViewModel,
+ noteId = if (noteId?.compareTo(-1) == 0) null else noteId
+ )
}
composable(route = Screens.Reminders.route) {
@@ -60,6 +78,16 @@ fun NoteXNavigation(
)
}
+ composable(route = Screens.Folders.route) {
+ bottomBarState.value = true
+ val noteFolderViewModel = hiltViewModel()
+
+ NoteFolderScreen(
+ navController = navController,
+ folderViewModel = noteFolderViewModel
+ )
+ }
+
composable(route = Screens.Settings.route) {
bottomBarState.value = false
}
@@ -68,39 +96,36 @@ fun NoteXNavigation(
@Composable
fun MainBottomBar(
- navController: NavController,
- items: List,
- onFABClick: () -> Unit
+ destinations: List,
+ onNavigateToDestination: (Screens) -> Unit,
+ currentDestination: NavDestination?
) {
- val navBackStackEntry by navController.currentBackStackEntryAsState()
+ NavigationBar(tonalElevation = 0.dp) {
+ destinations.forEach { destination ->
+ val selected = currentDestination.isTopLevelDestinationInHierarchy(destination)
- BottomAppBar(
- actions = {
- items.forEach { screen ->
- val selected = navBackStackEntry?.destination?.route == screen.route
-
- IconButton(
- onClick = {
- navController.navigate(screen.route) {
- popUpTo(navController.graph.findStartDestination().id) {
- saveState = true
- }
- launchSingleTop = true
- restoreState = true
- }
+ NavigationBarItem(
+ selected = selected,
+ onClick = { onNavigateToDestination(destination) },
+ icon = {
+ val icon = if (selected) {
+ destination.selectedIcon
+ } else {
+ destination.icon
}
- ) {
+
Icon(
- painter = painterResource(id = if (selected) screen.selectedIcon else screen.icon),
- contentDescription = screen.route
+ painter = painterResource(id = icon),
+ contentDescription = destination.route
)
- }
- }
- },
- floatingActionButton = {
- FloatingActionButton(onClick = onFABClick) {
- Icon(imageVector = Icons.Outlined.Add, contentDescription = null)
- }
+ },
+ label = { Text(destination.route) }
+ )
}
- )
+ }
}
+
+private fun NavDestination?.isTopLevelDestinationInHierarchy(destination: Screens) =
+ this?.hierarchy?.any {
+ it.route?.contains(destination.route, true) ?: false
+ } ?: false
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/navigation/Screens.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/navigation/Screens.kt
index aa36039..b87e97d 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/ui/navigation/Screens.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/navigation/Screens.kt
@@ -20,6 +20,12 @@ sealed class Screens(
selectedIcon = R.drawable.ic_note_filled
)
+ object Folders:Screens(
+ route = "Folders",
+ icon = R.drawable.ic_baseline_folder_open,
+ selectedIcon = R.drawable.ic_baseline_folder
+ )
+
object Reminders : Screens(
route = "Reminders",
icon = R.drawable.ic_task_outlined,
@@ -32,6 +38,12 @@ sealed class Screens(
selectedIcon = R.drawable.ic_task_filled
)
+ object NoteFolder : Screens(
+ route = "Folders",
+ icon = R.drawable.ic_task_outlined,
+ selectedIcon = R.drawable.ic_task_filled
+ )
+
fun withArgs(vararg args: Any): String {
return buildString {
append(route)
@@ -44,5 +56,6 @@ sealed class Screens(
val bottomNavItems = listOf(
Screens.Notes,
- Screens.Reminders
+ Screens.Reminders,
+// Screens.Folders
)
\ No newline at end of file
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/note/NotesViewModel.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/note/NotesViewModel.kt
deleted file mode 100644
index 20cd515..0000000
--- a/app/src/main/java/com/hadiyarajesh/notex/ui/note/NotesViewModel.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.hadiyarajesh.notex.ui.note
-
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
-import androidx.paging.PagingData
-import androidx.paging.cachedIn
-import com.hadiyarajesh.notex.database.entity.Note
-import com.hadiyarajesh.notex.repository.notes.NotesRepository
-import dagger.hilt.android.lifecycle.HiltViewModel
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.launch
-import javax.inject.Inject
-
-@HiltViewModel
-class NotesViewModel @Inject constructor(
- private val notesRepository: NotesRepository
-) : ViewModel() {
- // Get all notes as soon as collector collect this flow
- val notes: Flow> =
- notesRepository
- .getAllNotes()
- .cachedIn(viewModelScope)
-
- fun createNote(
- title: String?,
- content: String?
- ) = viewModelScope.launch {
- notesRepository.createNote(title = title, content = content)
- }
-}
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/note/add/AddNoteScreen.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/note/add/AddNoteScreen.kt
index 4245564..b7bb65a 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/ui/note/add/AddNoteScreen.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/note/add/AddNoteScreen.kt
@@ -1,31 +1,216 @@
package com.hadiyarajesh.notex.ui.note.add
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.Scaffold
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
+import androidx.activity.compose.BackHandler
+import androidx.compose.animation.Animatable
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationVector4D
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.*
+import androidx.compose.foundation.layout.*
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.material3.*
+import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.draw.shadow
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
+import com.hadiyarajesh.notex.R
+import com.hadiyarajesh.notex.ui.component.BorderLessTextField
+import com.hadiyarajesh.notex.ui.component.VerticalSpacer
+import com.hadiyarajesh.notex.ui.note.add.NoteState.Companion.noteColors
+import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AddNoteScreen(
- navController: NavController
+ navController: NavController, addNotesViewModel: AddNotesViewModel,
+ noteId: Long?
) {
- Scaffold { innerPadding ->
+ val state = remember { mutableStateOf(NoteState()) }
+
+ val noteBackground = remember { Animatable(Color(state.value.color.value)) }
+
+ LaunchedEffect(Unit) {
+ noteId?.let { id ->
+ val note = addNotesViewModel.getNote(id)
+
+ state.value.apply {
+ title.value = note.title
+ noteDesc.value = note.content
+ color.value = Color(android.graphics.Color.parseColor(note.color)).toArgb()
+ }
+
+ noteBackground.animateTo(
+ targetValue = Color(state.value.color.value),
+ animationSpec = tween(
+ durationMillis = 400
+ )
+ )
+ }
+ }
+
+ BackHandler {
+ if (checkNoteIsNotEmpty(noteState = state.value)) {
+ addNotesViewModel.saveNote(state.value, noteId)
+ }
+ navController.navigateUp()
+ }
+
+ Scaffold(
+ topBar = {
+ AddNoteTopBar(
+ onBackClick = { navController.navigateUp() },
+ onDoneClick = {
+ if (checkNoteIsNotEmpty(noteState = state.value)) {
+ addNotesViewModel.saveNote(state.value, noteId)
+ }
+ navController.navigateUp()
+ },
+ showDoneButton = checkNoteIsNotEmpty(state.value)
+ )
+ },
+// bottomBar = {
+// BottomAppBar(
+// modifier = Modifier.height(40.dp),
+// containerColor = noteBackground.value,
+// ) {
+// ShowColor(state = state, noteBackground)
+// }
+// }
+ ) { innerPadding ->
Column(
modifier = Modifier
.fillMaxSize()
+// .background(noteBackground.value)
.padding(innerPadding),
horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Center
+ verticalArrangement = Arrangement.Top
) {
- Text(text = "Add some notes here")
+ Column(
+ modifier = Modifier.padding(
+ start = 16.dp,
+ end = 16.dp,
+ top = 10.dp,
+ bottom = 1.dp
+ )
+ ) {
+ BorderLessTextField(
+// text = state.value.title.value ?: stringResource(id = R.string.hint_note_title),
+ text = state.value.title.value ?: "",
+ hint = stringResource(id = R.string.hint_note_title),
+ onValueChange = {
+ state.value.title.value = it
+ },
+ maxLines = 1,
+ textStyle = MaterialTheme.typography.headlineMedium.copy(color = MaterialTheme.colorScheme.onBackground)
+ )
+ VerticalSpacer(size = 16)
+
+ BorderLessTextField(
+// text = state.value.noteDesc.value
+// ?: stringResource(id = R.string.hint_note_description),
+ text = state.value.noteDesc.value ?: "",
+ hint = stringResource(id = R.string.hint_note_description),
+ onValueChange = {
+ state.value.noteDesc.value = it
+ },
+ textStyle = MaterialTheme.typography.bodyLarge.copy(color = MaterialTheme.colorScheme.onBackground)
+ )
+ }
}
}
}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun AddNoteTopBar(
+ onBackClick: () -> Unit,
+ onDoneClick: () -> Unit,
+ showDoneButton: Boolean
+) {
+ SmallTopAppBar(
+ title = { },
+ navigationIcon = {
+ IconButton(onClick = onBackClick) {
+ Icon(
+ imageVector = Icons.Default.ArrowBack,
+ contentDescription = stringResource(R.string.cd_back)
+ )
+ }
+ },
+ actions = {
+ IconButton(onClick = onDoneClick) {
+ AnimatedVisibility(
+ visible = showDoneButton
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_save_note),
+ contentDescription = stringResource(R.string.cd_save)
+ )
+ }
+ }
+ }
+ )
+}
+
+@Composable
+private fun ShowColor(
+ state: MutableState,
+ noteBackground: Animatable
+) {
+ val scope = rememberCoroutineScope()
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceAround
+ ) {
+ noteColors.forEach { color ->
+ val colorInt = color.toArgb()
+ Box(
+ modifier = Modifier
+ .size(30.dp)
+ .shadow(5.dp, RectangleShape)
+ .clip(RectangleShape)
+ .background(color)
+ .border(
+ width = 1.dp,
+ color = if (state.value.color.value == colorInt) {
+ Color.DarkGray
+ } else Color.Transparent,
+ shape = RectangleShape,
+ )
+ .clickable {
+ scope.launch {
+ noteBackground.animateTo(
+ targetValue = Color(colorInt),
+ animationSpec = tween(
+ durationMillis = 400
+ )
+ )
+ }
+ state.value.color.value = colorInt
+ }
+ )
+ }
+ }
+}
+
+//fun checkNoteIsNotEmpty(noteState: NoteState): Boolean {
+// return (noteState.title.value.isNotBlank() || noteState.noteDesc.value.isNotBlank())
+//}
+
+//fun checkNoteIsNotEmpty(noteState: NoteState): Boolean {
+// return !(noteState.title.value.isNullOrBlank() || noteState.noteDesc.value.isNullOrBlank())
+//}
+fun checkNoteIsNotEmpty(noteState: NoteState): Boolean {
+ return !noteState.noteDesc.value.isNullOrBlank()
+}
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/note/add/AddNotesViewModel.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/note/add/AddNotesViewModel.kt
new file mode 100644
index 0000000..b54095e
--- /dev/null
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/note/add/AddNotesViewModel.kt
@@ -0,0 +1,49 @@
+package com.hadiyarajesh.notex.ui.note.add
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.hadiyarajesh.notex.database.dao.NoteDao
+import com.hadiyarajesh.notex.database.entity.Note
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.async
+import kotlinx.coroutines.launch
+import java.time.Instant
+import javax.inject.Inject
+
+@HiltViewModel
+class AddNotesViewModel @Inject constructor(private val noteDao: NoteDao) : ViewModel() {
+ fun saveNote(noteState: NoteState, noteId: Long?) {
+ viewModelScope.launch {
+ noteDao.insertOrUpdate(
+ note = Note(
+ noteId = noteId,
+ title = noteState.title.value?.ifEmpty { null },
+ content = noteState.noteDesc.value?.ifEmpty { null },
+ archived = false,
+ color = convertIntColorToHex(noteState.color.value),
+ createdOn = Instant.now(),
+ updatedOn = Instant.now()
+ )
+ )
+
+ }
+ }
+
+ suspend fun getNote(noteId: Long): Note {
+ val note = viewModelScope.async(Dispatchers.IO) {
+ noteDao.getById(noteId)
+ }
+
+ return note.await()
+ }
+
+ private fun convertIntColorToHex(color: Int): String {
+ return String.format("#%06X", 0xFFFFFF and color)
+ }
+
+ sealed class UiEvent {
+ data class ShowToast(val message: String) : UiEvent()
+ object SaveNote : UiEvent()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/note/add/NoteState.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/note/add/NoteState.kt
new file mode 100644
index 0000000..db0770f
--- /dev/null
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/note/add/NoteState.kt
@@ -0,0 +1,30 @@
+package com.hadiyarajesh.notex.ui.note.add
+
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.graphics.toArgb
+import com.hadiyarajesh.notex.ui.theme.BabyBlue
+import com.hadiyarajesh.notex.ui.theme.LightGreen
+import com.hadiyarajesh.notex.ui.theme.LightOrange
+import com.hadiyarajesh.notex.ui.theme.LightYellow
+import com.hadiyarajesh.notex.ui.theme.RedOrange
+import com.hadiyarajesh.notex.ui.theme.RedPink
+import com.hadiyarajesh.notex.ui.theme.Violet
+import com.hadiyarajesh.notex.ui.theme.White
+
+data class NoteState(
+ val title: MutableState = mutableStateOf(""),
+ val noteDesc: MutableState = mutableStateOf(""),
+ val color: MutableState = mutableStateOf(noteColors[0].toArgb())
+){
+ companion object {
+ val noteColors =
+ listOf(
+ White, RedOrange, LightGreen, Violet, BabyBlue, RedPink, LightYellow, LightOrange)
+ }
+
+}
+
+
+
+
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/note/NotesScreen.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/note/all/NotesScreen.kt
similarity index 78%
rename from app/src/main/java/com/hadiyarajesh/notex/ui/note/NotesScreen.kt
rename to app/src/main/java/com/hadiyarajesh/notex/ui/note/all/NotesScreen.kt
index f85f375..82057e8 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/ui/note/NotesScreen.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/note/all/NotesScreen.kt
@@ -1,11 +1,13 @@
-package com.hadiyarajesh.notex.ui.note
+package com.hadiyarajesh.notex.ui.note.all
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Add
import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.FloatingActionButton
+import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
-import androidx.compose.material3.Surface
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@@ -13,7 +15,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.paging.LoadState
@@ -24,8 +25,9 @@ import com.hadiyarajesh.notex.R
import com.hadiyarajesh.notex.database.entity.Note
import com.hadiyarajesh.notex.ui.component.EmptyView
import com.hadiyarajesh.notex.ui.component.LoadingProgressBar
+import com.hadiyarajesh.notex.ui.component.NoteCard
import com.hadiyarajesh.notex.ui.component.RetryItem
-import java.time.Instant
+import com.hadiyarajesh.notex.ui.navigation.Screens
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -37,24 +39,34 @@ fun NotesScreen(
val context = LocalContext.current
val notes = remember { notesViewModel.notes }.collectAsLazyPagingItems()
- Scaffold { innerPadding ->
+ Scaffold(
+ floatingActionButton = {
+ FloatingActionButton(
+ onClick = {
+ navController.navigate(Screens.AddNote.route)
+ }
+ ) {
+ Icon(imageVector = Icons.Outlined.Add, contentDescription = null)
+ }
+ }
+ ) { innerPadding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding),
horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Center
) {
AllNotesView(
notes = notes,
onClick = { note ->
-
+ navController.navigate(Screens.AddNote.route + "?noteId=${note.noteId}")
}
)
}
}
}
+@SuppressWarnings("OptionalWhenBraces")
@Composable
private fun AllNotesView(
modifier: Modifier = Modifier,
@@ -64,10 +76,7 @@ private fun AllNotesView(
LazyColumn(modifier = modifier) {
items(notes) { item ->
item?.let { note ->
- NoteItem(
- note = note,
- onClick = onClick
- )
+ NoteCard(note = note, onClick = onClick)
}
}
@@ -76,6 +85,7 @@ private fun AllNotesView(
loadState.refresh is LoadState.Loading -> {
item { LoadingProgressBar(modifier = Modifier.fillParentMaxSize()) }
}
+
loadState.append is LoadState.Loading -> {
item {
LoadingProgressBar(
@@ -105,6 +115,7 @@ private fun AllNotesView(
)
}
}
+
loadState.append is LoadState.Error -> {
item {
RetryItem(
@@ -117,32 +128,3 @@ private fun AllNotesView(
}
}
}
-
-@Composable
-private fun NoteItem(
- modifier: Modifier = Modifier,
- note: Note,
- onClick: (Note) -> Unit,
-) {
- Column(modifier = modifier) {
- Text(text = note.title ?: "")
- }
-}
-
-@Preview
-@Composable
-fun NoteItemPreview() {
- Surface {
- NoteItem(
- note = Note(
- noteId = 12345,
- title = "NOte title",
- content = "Note content",
- archived = false,
- createdOn = Instant.now(),
- updatedOn = Instant.now()
- ),
- onClick = {}
- )
- }
-}
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/note/all/NotesViewModel.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/note/all/NotesViewModel.kt
new file mode 100644
index 0000000..9f61d43
--- /dev/null
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/note/all/NotesViewModel.kt
@@ -0,0 +1,97 @@
+package com.hadiyarajesh.notex.ui.note.all
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import androidx.paging.PagingData
+import androidx.paging.cachedIn
+import com.hadiyarajesh.notex.database.entity.Note
+import com.hadiyarajesh.notex.repository.notes.NotesRepository
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class NotesViewModel @Inject constructor(
+ private val notesRepository: NotesRepository
+) : ViewModel() {
+
+// private val _notesFlow = MutableStateFlow>(PagingData.empty())
+// val notesFlow: StateFlow>
+// get() = _notesFlow
+
+ // Get all notes as soon as collector collect this flow
+ val notes: Flow> =
+ notesRepository
+ .getAllNotes()
+ .cachedIn(viewModelScope)
+
+ private var _numberOfNotesDeleted: Int = 0
+ val numberOfNotesDeleted: Int
+ get() = _numberOfNotesDeleted
+
+ private var _note = MutableStateFlow(null)
+ val note: StateFlow
+ get() = _note
+
+ fun createNote(
+ title: String?,
+ content: String?
+ ) = viewModelScope.launch {
+ notesRepository.createNote(title = title, content = content)
+ }
+
+ fun deleteNote(note: Note) {
+ viewModelScope.launch {
+ _numberOfNotesDeleted = notesRepository.deleteNote(note)
+ }
+ }
+
+ fun deleteNote(noteId: Long) {
+ viewModelScope.launch {
+ _numberOfNotesDeleted = notesRepository.deleteNote(noteId)
+ }
+ }
+
+ fun deleteArchivedNotes() {
+ viewModelScope.launch {
+ _numberOfNotesDeleted = notesRepository.deleteArchived()
+ }
+ }
+
+ /**
+ * Deletes all Notes
+ */
+ fun deleteAll() {
+ viewModelScope.launch {
+ _numberOfNotesDeleted = notesRepository.deleteAllNotes()
+ }
+ }
+
+ fun archive(noteId: Long) {
+ viewModelScope.launch {
+ notesRepository.archive(noteId)
+ }
+ }
+
+ fun unarchive(noteId: Long) {
+ viewModelScope.launch {
+ notesRepository.unarchive(noteId)
+ }
+ }
+
+
+ fun getNote(noteId: Long) {
+ viewModelScope.launch {
+ _note.value = notesRepository.getNote(noteId)
+ }
+ }
+
+ fun addToFolder(noteId: Long,folderId: Long) {
+ viewModelScope.launch {
+ notesRepository.addToFolder(noteId,folderId)
+ }
+ }
+}
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/reminders/RemindersScreen.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/reminders/RemindersScreen.kt
index 6456400..1d9b703 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/ui/reminders/RemindersScreen.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/reminders/RemindersScreen.kt
@@ -45,13 +45,13 @@ fun RemindersScreen(
AllRemindersView(
reminders = reminders,
onClick = { reminder ->
-
}
)
}
}
}
+@SuppressWarnings("OptionalWhenBraces")
@Composable
private fun AllRemindersView(
modifier: Modifier = Modifier,
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/reminders/RemindersViewModel.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/reminders/RemindersViewModel.kt
index fd65e98..c3d466e 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/ui/reminders/RemindersViewModel.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/reminders/RemindersViewModel.kt
@@ -1,5 +1,6 @@
package com.hadiyarajesh.notex.ui.reminders
+import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
@@ -8,10 +9,10 @@ import com.hadiyarajesh.notex.database.entity.Reminder
import com.hadiyarajesh.notex.database.model.RepetitionStrategy
import com.hadiyarajesh.notex.repository.reminders.RemindersRepository
import dagger.hilt.android.lifecycle.HiltViewModel
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.launch
import java.time.Instant
import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
@HiltViewModel
class RemindersViewModel @Inject constructor(
@@ -26,11 +27,11 @@ class RemindersViewModel @Inject constructor(
fun createReminder(
title: String,
reminderTime: Instant,
- repeat: RepetitionStrategy
+ repeat: RepetitionStrategy,
+ context: Context?
) = viewModelScope.launch {
remindersRepository.createReminder(
- title, reminderTime, repeat
+ title, reminderTime, repeat, context
)
}
-
}
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/theme/Color.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/theme/Color.kt
index 1e09db3..58af6d4 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/ui/theme/Color.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/theme/Color.kt
@@ -8,4 +8,14 @@ val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
-val Pink40 = Color(0xFF7D5260)
\ No newline at end of file
+val Pink40 = Color(0xFF7D5260)
+
+val RedOrange = Color(0xffffab91)
+val RedPink = Color(0xfff48fb1)
+val BabyBlue = Color(0xff81deea)
+val Violet = Color(0xffcf94da)
+val LightGreen = Color(0xffe7ed9b)
+val LightYellow = Color(0xFFEEDF9B)
+val LightOrange = Color(0xFFF3C279)
+val White = Color(0xFFFFFFFF)
+val hintColor = Color(0xff4c4c4c)
\ No newline at end of file
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/theme/Theme.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/theme/Theme.kt
index 8b7df94..b83f61c 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/ui/theme/Theme.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/theme/Theme.kt
@@ -30,7 +30,7 @@ private val LightColorScheme = lightColorScheme(
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
- */
+ */
)
@Composable
diff --git a/app/src/main/java/com/hadiyarajesh/notex/ui/theme/Type.kt b/app/src/main/java/com/hadiyarajesh/notex/ui/theme/Type.kt
index 175abcd..06861ad 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/ui/theme/Type.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/ui/theme/Type.kt
@@ -30,5 +30,11 @@ val Typography = Typography(
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
- */
-)
\ No newline at end of file
+
+ headlineSmall = TextStyle(
+ letterSpacing = 1.sp,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ )*/
+)
diff --git a/app/src/main/java/com/hadiyarajesh/notex/utility/Constants.kt b/app/src/main/java/com/hadiyarajesh/notex/utility/Constants.kt
index 3513199..501e093 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/utility/Constants.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/utility/Constants.kt
@@ -6,4 +6,4 @@ object Constants {
}
}
-const val TAG = Constants.App.APP_NAME
\ No newline at end of file
+const val TAG = Constants.App.APP_NAME
diff --git a/app/src/main/java/com/hadiyarajesh/notex/utility/PreferenceManager.kt b/app/src/main/java/com/hadiyarajesh/notex/utility/PreferenceManager.kt
index b1c5da2..f9e321b 100644
--- a/app/src/main/java/com/hadiyarajesh/notex/utility/PreferenceManager.kt
+++ b/app/src/main/java/com/hadiyarajesh/notex/utility/PreferenceManager.kt
@@ -8,11 +8,11 @@ import androidx.datastore.preferences.core.emptyPreferences
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import dagger.hilt.android.qualifiers.ApplicationContext
+import java.io.IOException
+import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.map
-import java.io.IOException
-import javax.inject.Inject
private val Context.dataStore: DataStore by preferencesDataStore(
name = "${Constants.App.APP_NAME.lowercase()}_prefs"
@@ -35,7 +35,6 @@ class PreferenceManager @Inject constructor(@ApplicationContext val context: Con
preferences[APP_LANGUAGE_KEY]
}
-
suspend fun saveAppLanguage(language: String) {
context.dataStore.edit { preferences ->
preferences[APP_LANGUAGE_KEY] = language
diff --git a/app/src/main/res/drawable/ic_add_circle_outline.xml b/app/src/main/res/drawable/ic_add_circle_outline.xml
new file mode 100644
index 0000000..9b10da7
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add_circle_outline.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_baseline_folder.xml b/app/src/main/res/drawable/ic_baseline_folder.xml
new file mode 100644
index 0000000..5e930a2
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_folder.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_baseline_folder_open.xml b/app/src/main/res/drawable/ic_baseline_folder_open.xml
new file mode 100644
index 0000000..7ea2999
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_folder_open.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_folder_filled.xml b/app/src/main/res/drawable/ic_folder_filled.xml
new file mode 100644
index 0000000..5e930a2
--- /dev/null
+++ b/app/src/main/res/drawable/ic_folder_filled.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_notify.xml b/app/src/main/res/drawable/ic_notify.xml
new file mode 100644
index 0000000..605362e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_notify.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_save_note.xml b/app/src/main/res/drawable/ic_save_note.xml
new file mode 100644
index 0000000..cf143d4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_save_note.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml
index 6fdb2c5..673036c 100644
--- a/app/src/main/res/values-hi/strings.xml
+++ b/app/src/main/res/values-hi/strings.xml
@@ -3,4 +3,6 @@
NoteX में आपका स्वागत है
फिर से प्रयास करें
यहाँ सब खाली है
+ श्रेणियाँ
+ सूची श्रेणियाँ
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2edc9a4..d5c50a9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -8,4 +8,24 @@
Welcome to NoteX
Retry
It\'s all empty here
+ Categories
+ List Categories
+
+
+ Add
+ Back
+ Save
+
+ Title
+ Start typing…
+
+ Reminder Instance
+ ReminderTag
+ Reminder_ID
+ WORKER_TAG
+ POSTPONE
+ DONE
+ NOTIFICATION_ID
+ 1 hour
+ Done
diff --git a/app/src/test/java/com/hadiyarajesh/notex/ExampleUnitTest.kt b/app/src/test/java/com/hadiyarajesh/notex/ExampleUnitTest.kt
index e0f2f08..f4f091a 100644
--- a/app/src/test/java/com/hadiyarajesh/notex/ExampleUnitTest.kt
+++ b/app/src/test/java/com/hadiyarajesh/notex/ExampleUnitTest.kt
@@ -1,8 +1,7 @@
package com.hadiyarajesh.notex
-import org.junit.Test
-
import org.junit.Assert.*
+import org.junit.Test
/**
* Example local unit test, which will execute on the development machine (host).
diff --git a/build.gradle.kts b/build.gradle.kts
index 1c1e440..4f5d501 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,15 +1,100 @@
+import io.gitlab.arturbosch.detekt.Detekt
+import org.jlleitschuh.gradle.ktlint.KtlintExtension
+import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
+import org.jlleitschuh.gradle.ktlint.tasks.GenerateReportsTask
+
buildscript {
- val hiltVersion by extra("2.42")
+ val hiltVersion by extra("2.44")
dependencies {
classpath("com.google.dagger:hilt-android-gradle-plugin:$hiltVersion")
+ classpath("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.22.0-RC1")
}
-}// Top-level build file where you can add configuration options common to all sub-projects/modules.
+}
+
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
- id("com.android.application") version "7.2.2" apply false
- id("com.android.library") version "7.2.2" apply false
- id("org.jetbrains.kotlin.android") version "1.7.0" apply false
+ id("com.android.application") version "7.3.1" apply false
+ id("com.android.library") version "7.3.1" apply false
+ id("org.jetbrains.kotlin.android") version "1.7.20" apply false
+ id("io.gitlab.arturbosch.detekt") version "1.22.0-RC1"
+ id("org.jlleitschuh.gradle.ktlint") version "10.2.1"
+}
+
+allprojects {
+ apply(plugin = "org.jlleitschuh.gradle.ktlint")
+ apply(plugin = "io.gitlab.arturbosch.detekt")
+ configureDetekt()
+ configureKtLint()
+}
+
+configureDetekt()
+fun Project.configureDetekt() {
+ tasks.withType {
+ jvmTarget = JavaVersion.VERSION_1_8.toString()
+ setSource(files(projectDir))
+ exclude("**/build/**")
+ parallel = true
+ baseline.set(file(path = "${rootProject.projectDir}/config/detekt/detekt-baseline.xml"))
+ autoCorrect = true
+ reports {
+ xml.required.set(true)
+ html.required.set(false)
+ txt.required.set(false)
+ sarif.required.set(false)
+ }
+ }
+}
+
+fun Project.configureKtLint() {
+ this.configure {
+ version.set("0.45.2")
+ android.set(true)
+ outputToConsole.set(true)
+ ignoreFailures.set(true)
+ enableExperimentalRules.set(true)
+ disabledRules.set(
+ setOf(
+ "unused-imports",
+ "final-newline",
+ "max-line-length",
+ "experimental:argument-list-wrapping",
+ "no-wildcard-imports",
+ "experimental:trailing-comma",
+ "experimental:comment-wrapping"
+ )
+ )
+ reporters {
+ reporter(ReporterType.CHECKSTYLE)
+ }
+ filter {
+ exclude("**/generated/**")
+ include("**/build/**")
+ }
+ }
+}
+
+// Tasks for providing dir for generated reports from detekt and ktlint
+tasks.withType {
+ reportsOutputDirectory.set(
+ File(project.buildDir, "reports/ktlint/merged-ktlint-results.xml")
+ )
+}
+
+// Tasks for automatically installing git-hooks and making it executable also.
+tasks.register("installGitHook", Copy::class) {
+ from(file("$rootDir/scripts/pre-push"))
+ into(file("$rootDir/.git/hooks"))
+ fileMode = 0b0111101101 // -rwxr-xr-x
+}
+
+tasks.create(name = "gitExecutableHooks") {
+ doLast {
+ Runtime.getRuntime().exec("chmod -R +x .git/hooks/")
+ }
}
+tasks.getByPath("gitExecutableHooks").dependsOn(tasks.named("installGitHook"))
+tasks.getByPath(":app:clean").dependsOn(tasks.named("gitExecutableHooks"))
tasks.register("clean", Delete::class) {
delete(rootProject.buildDir)
diff --git a/config/detekt/detekt-baseline.xml b/config/detekt/detekt-baseline.xml
new file mode 100644
index 0000000..3dfaf4a
--- /dev/null
+++ b/config/detekt/detekt-baseline.xml
@@ -0,0 +1,67 @@
+
+
+
+
+ EmptyClassBlock:NoteApi.kt$NoteApi${ }
+ FunctionNaming:AddNoteScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun AddNoteScreen( navController: NavController )
+ FunctionNaming:AllNotesComponents.kt$@Composable fun NoteCard(note: Note)
+ FunctionNaming:AllNotesComponents.kt$@Preview @Composable fun NoteCardPrev()
+ FunctionNaming:NoteXApp.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun NoteXApp()
+ FunctionNaming:NoteXNavigation.kt$@Composable fun MainBottomBar( navController: NavController, items: List<Screens>, onFABClick: () -> Unit )
+ FunctionNaming:NoteXNavigation.kt$@Composable fun NoteXNavigation( modifier: Modifier = Modifier, navController: NavHostController, bottomBarState: MutableState<Boolean> )
+ FunctionNaming:NotesScreen.kt$@Composable private fun AllNotesView( modifier: Modifier = Modifier, notes: LazyPagingItems<Note>, onClick: (Note) -> Unit, )
+ FunctionNaming:NotesScreen.kt$@Composable private fun NoteItem( modifier: Modifier = Modifier, note: Note, onClick: (Note) -> Unit, )
+ FunctionNaming:NotesScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun NotesScreen( navController: NavController, notesViewModel: NotesViewModel )
+ FunctionNaming:NotesScreen.kt$@Preview @Composable fun NoteItemPreview()
+ FunctionNaming:Other.kt$@Composable fun EmptyView( modifier: Modifier = Modifier, text: String )
+ FunctionNaming:Other.kt$@Composable fun ErrorText( modifier: Modifier = Modifier, text: String, color: Color = MaterialTheme.colorScheme.error, style: TextStyle = MaterialTheme.typography.labelMedium )
+ FunctionNaming:Other.kt$@Composable fun HorizontalSpacer(size: Int)
+ FunctionNaming:Other.kt$@Composable fun ImageItem( modifier: Modifier = Modifier, data: Any?, crossfadeValue: Int = 300, contentDescription: String? = null, contentScale: ContentScale = ContentScale.Crop, transformation: Transformation? = null, )
+ FunctionNaming:Other.kt$@Composable fun LoadingProgressBar( modifier: Modifier = Modifier, size: Dp = 40.dp, color: Color = MaterialTheme.colorScheme.primary, strokeWidth: Dp = 4.dp )
+ FunctionNaming:Other.kt$@Composable fun RetryItem( modifier: Modifier = Modifier, onRetryClick: () -> Unit )
+ FunctionNaming:Other.kt$@Composable fun SubComposeImageItem( modifier: Modifier = Modifier, data: Any?, crossfadeValue: Int = 300, contentDescription: String? = null, contentScale: ContentScale = ContentScale.Crop, transformation: Transformation? = null, )
+ FunctionNaming:Other.kt$@Composable fun VerticalSpacer(size: Int)
+ FunctionNaming:RemindersScreen.kt$@Composable private fun AllRemindersView( modifier: Modifier = Modifier, reminders: LazyPagingItems<Reminder>, onClick: (Reminder) -> Unit, )
+ FunctionNaming:RemindersScreen.kt$@Composable private fun ReminderItem( modifier: Modifier = Modifier, reminder: Reminder, onClick: (Reminder) -> Unit, )
+ FunctionNaming:RemindersScreen.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun RemindersScreen( navController: NavController, remindersViewModel: RemindersViewModel )
+ FunctionNaming:TextComposables.kt$@Composable fun TextSemiBold( content: String, modifier: Modifier? = Modifier, textStyle: TextStyle? = null, color: Color? = null )
+ FunctionNaming:Theme.kt$@Composable fun NoteXTheme( darkTheme: Boolean = isSystemInDarkTheme(), // Dynamic color is available on Android 12+ dynamicColor: Boolean = true, content: @Composable () -> Unit )
+ LongParameterList:Other.kt$( modifier: Modifier = Modifier, data: Any?, crossfadeValue: Int = 300, contentDescription: String? = null, contentScale: ContentScale = ContentScale.Crop, transformation: Transformation? = null, )
+ MagicNumber:Color.kt$0xFF625b71
+ MagicNumber:Color.kt$0xFF6650a4
+ MagicNumber:Color.kt$0xFF7D5260
+ MagicNumber:Color.kt$0xFFCCC2DC
+ MagicNumber:Color.kt$0xFFD0BCFF
+ MagicNumber:Color.kt$0xFFEFB8C8
+ MatchingDeclarationName:Scope.kt$ApplicationScope
+ MaxLineLength:FolderDao.kt$FolderDao$* This method requires Room to run two queries, so add the @Transaction annotation to this method to ensure that the whole operation is performed atomically.
+ MaxLineLength:NotesScreen.kt$loadState.refresh is LoadState.NotLoading && loadState.append.endOfPaginationReached && notes.itemCount < 1
+ MaxLineLength:RemindersScreen.kt$loadState.refresh is LoadState.NotLoading && loadState.append.endOfPaginationReached && reminders.itemCount < 1
+ NewLineAtEndOfFile:AllNotesComponents.kt$com.hadiyarajesh.notex.ui.component.AllNotesComponents.kt
+ NewLineAtEndOfFile:Color.kt$com.hadiyarajesh.notex.ui.theme.Color.kt
+ NewLineAtEndOfFile:ExampleUnitTest.kt$com.hadiyarajesh.notex.ExampleUnitTest.kt
+ NewLineAtEndOfFile:Screens.kt$com.hadiyarajesh.notex.ui.navigation.Screens.kt
+ NewLineAtEndOfFile:TextComposables.kt$com.hadiyarajesh.notex.ui.component.TextComposables.kt
+ NewLineAtEndOfFile:Type.kt$com.hadiyarajesh.notex.ui.theme.Type.kt
+ TooManyFunctions:NoteDao.kt$NoteDao
+ UnusedPrivateMember:AddNoteScreen.kt$navController: NavController
+ UnusedPrivateMember:NotesScreen.kt$navController: NavController
+ UnusedPrivateMember:NotesScreen.kt$onClick: (Note) -> Unit
+ UnusedPrivateMember:NotesScreen.kt$val context = LocalContext.current
+ UnusedPrivateMember:NotesScreen.kt$val scope = rememberCoroutineScope()
+ UnusedPrivateMember:RemindersScreen.kt$navController: NavController
+ UnusedPrivateMember:RemindersScreen.kt$onClick: (Reminder) -> Unit
+ UnusedPrivateMember:RemindersScreen.kt$val context = LocalContext.current
+ UnusedPrivateMember:RemindersScreen.kt$val scope = rememberCoroutineScope()
+ WildcardImport:AllNotesComponents.kt$import androidx.compose.foundation.layout.*
+ WildcardImport:ExampleUnitTest.kt$import org.junit.Assert.*
+ WildcardImport:FolderDao.kt$import androidx.room.*
+ WildcardImport:InstantAdapter.kt$import com.squareup.moshi.*
+ WildcardImport:NoteDao.kt$import androidx.room.*
+ WildcardImport:NotesScreen.kt$import androidx.compose.foundation.layout.*
+ WildcardImport:Other.kt$import androidx.compose.foundation.layout.*
+ WildcardImport:ReminderDao.kt$import androidx.room.*
+ WildcardImport:RemindersScreen.kt$import androidx.compose.foundation.layout.*
+ WildcardImport:Theme.kt$import androidx.compose.material3.*
+
+
diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml
new file mode 100644
index 0000000..b4e0bb1
--- /dev/null
+++ b/config/detekt/detekt.yml
@@ -0,0 +1,734 @@
+build:
+ maxIssues: 0
+ excludeCorrectable: false
+ weights:
+ # complexity: 2
+ # LongParameterList: 1
+ # style: 1
+ # comments: 1
+
+config:
+ validation: true
+ warningsAsErrors: true
+ checkExhaustiveness: false
+ # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]'
+ excludes: ''
+
+processors:
+ active: true
+ exclude:
+ - 'DetektProgressListener'
+ # - 'KtFileCountProcessor'
+ # - 'PackageCountProcessor'
+ # - 'ClassCountProcessor'
+ # - 'FunctionCountProcessor'
+ # - 'PropertyCountProcessor'
+ # - 'ProjectComplexityProcessor'
+ # - 'ProjectCognitiveComplexityProcessor'
+ # - 'ProjectLLOCProcessor'
+ # - 'ProjectCLOCProcessor'
+ # - 'ProjectLOCProcessor'
+ # - 'ProjectSLOCProcessor'
+ # - 'LicenseHeaderLoaderExtension'
+
+console-reports:
+ active: true
+ exclude:
+ - 'ProjectStatisticsReport'
+ - 'ComplexityReport'
+ - 'NotificationReport'
+ - 'FindingsReport'
+ - 'FileBasedFindingsReport'
+ # - 'LiteFindingsReport'
+
+output-reports:
+ active: true
+ exclude:
+ # - 'TxtOutputReport'
+ # - 'XmlOutputReport'
+ # - 'HtmlOutputReport'
+ # - 'MdOutputReport'
+
+comments:
+ active: true
+ AbsentOrWrongFileLicense:
+ active: false
+ licenseTemplateFile: 'license.template'
+ licenseTemplateIsRegex: false
+ CommentOverPrivateFunction:
+ active: false
+ CommentOverPrivateProperty:
+ active: false
+ DeprecatedBlockTag:
+ active: false
+ EndOfSentenceFormat:
+ active: false
+ endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)'
+ KDocReferencesNonPublicProperty:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ OutdatedDocumentation:
+ active: false
+ matchTypeParameters: true
+ matchDeclarationsOrder: true
+ allowParamOnConstructorProperties: false
+ UndocumentedPublicClass:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ searchInNestedClass: true
+ searchInInnerClass: true
+ searchInInnerObject: true
+ searchInInnerInterface: true
+ searchInProtectedClass: false
+ UndocumentedPublicFunction:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ searchProtectedFunction: false
+ UndocumentedPublicProperty:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ searchProtectedProperty: false
+
+complexity:
+ active: true
+ ComplexCondition:
+ active: true
+ threshold: 4
+ ComplexInterface:
+ active: true
+ threshold: 20
+ includeStaticDeclarations: false
+ includePrivateDeclarations: false
+ ignoreOverloaded: false
+ ComplexMethod:
+ active: true
+ threshold: 15
+ ignoreSingleWhenExpression: false
+ ignoreSimpleWhenEntries: false
+ ignoreNestingFunctions: false
+ nestingFunctions:
+ - 'also'
+ - 'apply'
+ - 'forEach'
+ - 'isNotNull'
+ - 'ifNull'
+ - 'let'
+ - 'run'
+ - 'use'
+ - 'with'
+ LabeledExpression:
+ active: false
+ ignoredLabels: []
+ LargeClass:
+ active: true
+ threshold: 600
+ LongMethod:
+ active: true
+ threshold: 60
+ LongParameterList:
+ active: true
+ functionThreshold: 10
+ constructorThreshold: 10
+ ignoreDefaultParameters: true
+ ignoreDataClasses: true
+ ignoreAnnotatedParameter: [ 'Composable' ]
+ MethodOverloading:
+ active: true
+ threshold: 6
+ NamedArguments:
+ active: true
+ threshold: 3
+ ignoreArgumentsMatchingNames: false
+ NestedBlockDepth:
+ active: true
+ threshold: 4
+ NestedScopeFunctions:
+ active: false
+ threshold: 1
+ functions:
+ - 'kotlin.apply'
+ - 'kotlin.run'
+ - 'kotlin.with'
+ - 'kotlin.let'
+ - 'kotlin.also'
+ ReplaceSafeCallChainWithRun:
+ active: false
+ StringLiteralDuplication:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ threshold: 3
+ ignoreAnnotation: true
+ excludeStringsWithLessThan5Characters: true
+ ignoreStringsRegex: '$^'
+ TooManyFunctions:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ thresholdInFiles: 11
+ thresholdInClasses: 11
+ thresholdInInterfaces: 11
+ thresholdInObjects: 11
+ thresholdInEnums: 11
+ ignoreDeprecated: false
+ ignorePrivate: false
+ ignoreOverridden: false
+
+coroutines:
+ active: true
+ GlobalCoroutineUsage:
+ active: true
+ InjectDispatcher:
+ active: true
+ dispatcherNames:
+ - 'IO'
+ - 'Default'
+ - 'Unconfined'
+ RedundantSuspendModifier:
+ active: true
+ SleepInsteadOfDelay:
+ active: true
+ SuspendFunWithCoroutineScopeReceiver:
+ active: true
+ SuspendFunWithFlowReturnType:
+ active: true
+
+empty-blocks:
+ active: true
+ EmptyCatchBlock:
+ active: true
+ allowedExceptionNameRegex: '_|(ignore|expected).*'
+ EmptyClassBlock:
+ active: true
+ EmptyDefaultConstructor:
+ active: true
+ EmptyDoWhileBlock:
+ active: true
+ EmptyElseBlock:
+ active: true
+ EmptyFinallyBlock:
+ active: true
+ EmptyForBlock:
+ active: true
+ EmptyFunctionBlock:
+ active: true
+ ignoreOverridden: false
+ EmptyIfBlock:
+ active: true
+ EmptyInitBlock:
+ active: true
+ EmptyKtFile:
+ active: true
+ EmptySecondaryConstructor:
+ active: true
+ EmptyTryBlock:
+ active: true
+ EmptyWhenBlock:
+ active: true
+ EmptyWhileBlock:
+ active: true
+
+exceptions:
+ active: true
+ ExceptionRaisedInUnexpectedLocation:
+ active: true
+ methodNames:
+ - 'equals'
+ - 'finalize'
+ - 'hashCode'
+ - 'toString'
+ InstanceOfCheckForException:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ NotImplementedDeclaration:
+ active: true
+ ObjectExtendsThrowable:
+ active: true
+ PrintStackTrace:
+ active: true
+ RethrowCaughtException:
+ active: true
+ ReturnFromFinally:
+ active: true
+ ignoreLabeled: false
+ SwallowedException:
+ active: true
+ ignoredExceptionTypes:
+ - 'InterruptedException'
+ - 'MalformedURLException'
+ - 'NumberFormatException'
+ - 'ParseException'
+ allowedExceptionNameRegex: '_|(ignore|expected).*'
+ ThrowingExceptionFromFinally:
+ active: true
+ ThrowingExceptionInMain:
+ active: true
+ ThrowingExceptionsWithoutMessageOrCause:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ exceptions:
+ - 'ArrayIndexOutOfBoundsException'
+ - 'Exception'
+ - 'IllegalArgumentException'
+ - 'IllegalMonitorStateException'
+ - 'IllegalStateException'
+ - 'IndexOutOfBoundsException'
+ - 'NullPointerException'
+ - 'RuntimeException'
+ - 'Throwable'
+ ThrowingNewInstanceOfSameException:
+ active: true
+ TooGenericExceptionCaught:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ exceptionNames:
+ - 'ArrayIndexOutOfBoundsException'
+ - 'Error'
+ - 'Exception'
+ - 'IllegalMonitorStateException'
+ - 'IndexOutOfBoundsException'
+ - 'NullPointerException'
+ - 'RuntimeException'
+ - 'Throwable'
+ allowedExceptionNameRegex: '_|(ignore|expected).*'
+ TooGenericExceptionThrown:
+ active: true
+ exceptionNames:
+ - 'Error'
+ - 'Exception'
+ - 'RuntimeException'
+ - 'Throwable'
+
+naming:
+ active: true
+ BooleanPropertyNaming:
+ active: true
+ allowedPattern: '^(is|has|are)'
+ ignoreOverridden: true
+ ClassNaming:
+ active: true
+ classPattern: '[A-Z][a-zA-Z0-9]*'
+ ConstructorParameterNaming:
+ active: true
+ parameterPattern: '[a-z][A-Za-z0-9]*'
+ privateParameterPattern: '[a-z][A-Za-z0-9]*'
+ excludeClassPattern: '$^'
+ ignoreOverridden: true
+ EnumNaming:
+ active: true
+ enumEntryPattern: '[A-Z][_a-zA-Z0-9]*'
+ ForbiddenClassName:
+ active: true
+ forbiddenName: []
+ FunctionMaxLength:
+ active: true
+ maximumFunctionNameLength: 50
+ FunctionMinLength:
+ active: true
+ minimumFunctionNameLength: 3
+ FunctionNaming:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ functionPattern: '[a-z][a-zA-Z0-9]*'
+ excludeClassPattern: '$^'
+ ignoreOverridden: true
+ ignoreAnnotated: [ 'Composable' ]
+ FunctionParameterNaming:
+ active: true
+ parameterPattern: '[a-z][A-Za-z0-9]*'
+ excludeClassPattern: '$^'
+ ignoreOverridden: true
+ InvalidPackageDeclaration:
+ active: true
+ rootPackage: ''
+ requireRootInDeclaration: false
+ LambdaParameterNaming:
+ active: true
+ parameterPattern: '[a-z][A-Za-z0-9]*|_'
+ MatchingDeclarationName:
+ active: true
+ mustBeFirst: true
+ MemberNameEqualsClassName:
+ active: true
+ ignoreOverridden: true
+ NoNameShadowing:
+ active: true
+ NonBooleanPropertyPrefixedWithIs:
+ active: true
+ ObjectPropertyNaming:
+ active: true
+ constantPattern: '[A-Za-z][_A-Za-z0-9]*'
+ propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
+ privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
+ PackageNaming:
+ active: true
+ packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*'
+ TopLevelPropertyNaming:
+ active: true
+ constantPattern: '[A-Z][_A-Z0-9]*'
+ propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
+ privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*'
+ VariableMaxLength:
+ active: true
+ maximumVariableNameLength: 64
+ VariableMinLength:
+ active: true
+ minimumVariableNameLength: 1
+ VariableNaming:
+ active: true
+ variablePattern: '[a-z][A-Za-z0-9]*'
+ privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
+ excludeClassPattern: '$^'
+ ignoreOverridden: true
+
+performance:
+ active: true
+ ArrayPrimitive:
+ active: true
+ CouldBeSequence:
+ active: false
+ threshold: 3
+ ForEachOnRange:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ SpreadOperator:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ UnnecessaryPartOfBinaryExpression:
+ active: false
+ UnnecessaryTemporaryInstantiation:
+ active: true
+
+potential-bugs:
+ active: true
+ AvoidReferentialEquality:
+ active: true
+ forbiddenTypePatterns:
+ - 'kotlin.String'
+ CastToNullableType:
+ active: true
+ Deprecation:
+ active: true
+ DontDowncastCollectionTypes:
+ active: true
+ DoubleMutabilityForCollection:
+ active: true
+ mutableTypes:
+ - 'kotlin.collections.MutableList'
+ - 'kotlin.collections.MutableMap'
+ - 'kotlin.collections.MutableSet'
+ - 'java.util.ArrayList'
+ - 'java.util.LinkedHashSet'
+ - 'java.util.HashSet'
+ - 'java.util.LinkedHashMap'
+ - 'java.util.HashMap'
+ DuplicateCaseInWhenExpression:
+ active: true
+ ElseCaseInsteadOfExhaustiveWhen:
+ active: true
+ EqualsAlwaysReturnsTrueOrFalse:
+ active: true
+ EqualsWithHashCodeExist:
+ active: true
+ ExitOutsideMain:
+ active: true
+ ExplicitGarbageCollectionCall:
+ active: true
+ HasPlatformType:
+ active: true
+ IgnoredReturnValue:
+ active: true
+ restrictToConfig: true
+ returnValueAnnotations:
+ - '*.CheckResult'
+ - '*.CheckReturnValue'
+ ignoreReturnValueAnnotations:
+ - '*.CanIgnoreReturnValue'
+ returnValueTypes:
+ - 'kotlin.sequences.Sequence'
+ - 'kotlinx.coroutines.flow.*Flow'
+ - 'java.util.stream.*Stream'
+ ignoreFunctionCall: []
+ ImplicitDefaultLocale:
+ active: true
+ ImplicitUnitReturnType:
+ active: true
+ allowExplicitReturnType: true
+ InvalidRange:
+ active: true
+ IteratorHasNextCallsNextMethod:
+ active: true
+ IteratorNotThrowingNoSuchElementException:
+ active: true
+ LateinitUsage:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ ignoreOnClassesPattern: ''
+ MapGetWithNotNullAssertionOperator:
+ active: true
+ MissingPackageDeclaration:
+ active: false
+ excludes: ['**/*.kts']
+ MissingWhenCase:
+ active: true
+ allowElseExpression: true
+ NullCheckOnMutableProperty:
+ active: true
+ NullableToStringCall:
+ active: true
+ RedundantElseInWhen:
+ active: true
+ UnconditionalJumpStatementInLoop:
+ active: true
+ UnnecessaryNotNullOperator:
+ active: true
+ UnnecessarySafeCall:
+ active: true
+ UnreachableCatchBlock:
+ active: true
+ UnreachableCode:
+ active: true
+ UnsafeCallOnNullableType:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ UnsafeCast:
+ active: true
+ UnusedUnaryOperator:
+ active: true
+ UselessPostfixExpression:
+ active: true
+ WrongEqualsTypeParameter:
+ active: true
+
+style:
+ active: true
+ CanBeNonNullable:
+ active: false
+ CascadingCallWrapping:
+ active: false
+ includeElvis: true
+ ClassOrdering:
+ active: false
+ CollapsibleIfStatements:
+ active: true
+ DataClassContainsFunctions:
+ active: false
+ conversionFunctionPrefix:
+ - 'to'
+ DataClassShouldBeImmutable:
+ active: true
+ DestructuringDeclarationWithTooManyEntries:
+ active: true
+ maxDestructuringEntries: 3
+ EqualsNullCall:
+ active: true
+ EqualsOnSignatureLine:
+ active: false
+ ExplicitCollectionElementAccessMethod:
+ active: false
+ ExplicitItLambdaParameter:
+ active: true
+ ExpressionBodySyntax:
+ active: false
+ includeLineWrapping: false
+ ForbiddenComment:
+ active: true
+ values:
+ - 'FIXME:'
+ - 'STOPSHIP:'
+ - 'TODO:'
+ allowedPatterns: ''
+ customMessage: ''
+ ForbiddenImport:
+ active: true
+ imports: []
+ forbiddenPatterns: ''
+ ForbiddenMethodCall:
+ active: true
+ methods:
+ - reason: 'print does not allow you to configure the output stream. Use a logger instead.'
+ value: 'kotlin.io.print'
+ - reason: 'println does not allow you to configure the output stream. Use a logger instead.'
+ value: 'kotlin.io.println'
+ ForbiddenPublicDataClass:
+ active: true
+ excludes: ['**']
+ ignorePackages:
+ - '*.internal'
+ - '*.internal.*'
+ ForbiddenSuppress:
+ active: true
+ rules: []
+ ForbiddenVoid:
+ active: true
+ ignoreOverridden: false
+ ignoreUsageInGenerics: false
+ FunctionOnlyReturningConstant:
+ active: true
+ ignoreOverridableFunction: true
+ ignoreActualFunction: true
+ excludedFunctions: []
+ LibraryCodeMustSpecifyReturnType:
+ active: true
+ excludes: ['**']
+ LibraryEntitiesShouldNotBePublic:
+ active: false
+ excludes: ['**']
+ LoopWithTooManyJumpStatements:
+ active: false
+ maxJumpCount: 1
+ MagicNumber:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**', '**/*.kts']
+ ignoreNumbers:
+ - '-1'
+ - '0'
+ - '1'
+ - '2'
+ ignoreHashCodeFunction: true
+ ignorePropertyDeclaration: false
+ ignoreLocalVariableDeclaration: false
+ ignoreConstantDeclaration: true
+ ignoreCompanionObjectPropertyDeclaration: true
+ ignoreAnnotation: false
+ ignoreNamedArgument: true
+ ignoreEnums: true
+ ignoreRanges: false
+ ignoreExtensionFunctions: true
+ MandatoryBracesIfStatements:
+ active: true
+ MandatoryBracesLoops:
+ active: true
+ MaxChainedCallsOnSameLine:
+ active: false
+ maxChainedCalls: 5
+ MaxLineLength:
+ active: true
+ maxLineLength: 160
+ excludePackageStatements: true
+ excludeImportStatements: true
+ excludeCommentStatements: false
+ excludeRawStrings: true
+ MayBeConst:
+ active: true
+ ModifierOrder:
+ active: true
+ MultilineLambdaItParameter:
+ active: false
+ MultilineRawStringIndentation:
+ active: false
+ indentSize: 4
+ NestedClassesVisibility:
+ active: true
+ NewLineAtEndOfFile:
+ active: true
+ NoTabs:
+ active: false
+ NullableBooleanCheck:
+ active: false
+ ObjectLiteralToLambda:
+ active: false
+ OptionalAbstractKeyword:
+ active: true
+ OptionalUnit:
+ active: false
+ OptionalWhenBraces:
+ active: true
+ PreferToOverPairSyntax:
+ active: true
+ ProtectedMemberInFinalClass:
+ active: true
+ RedundantExplicitType:
+ active: true
+ RedundantHigherOrderMapUsage:
+ active: true
+ RedundantVisibilityModifierRule:
+ active: true
+ ReturnCount:
+ active: true
+ max: 5
+ excludedFunctions:
+ - 'equals'
+ excludeLabeled: false
+ excludeReturnFromLambda: true
+ excludeGuardClauses: false
+ SafeCast:
+ active: true
+ SerialVersionUIDInSerializableClass:
+ active: true
+ SpacingBetweenPackageAndImports:
+ active: false
+ ThrowsCount:
+ active: true
+ max: 2
+ excludeGuardClauses: false
+ TrailingWhitespace:
+ active: true
+ TrimMultilineRawString:
+ active: false
+ UnderscoresInNumericLiterals:
+ active: false
+ acceptableLength: 5
+ allowNonStandardGrouping: false
+ UnnecessaryAbstractClass:
+ active: true
+ UnnecessaryAnnotationUseSiteTarget:
+ active: true
+ UnnecessaryApply:
+ active: true
+ UnnecessaryBackticks:
+ active: false
+ UnnecessaryFilter:
+ active: true
+ UnnecessaryInheritance:
+ active: true
+ UnnecessaryInnerClass:
+ active: true
+ UnnecessaryLet:
+ active: true
+ UnnecessaryParentheses:
+ active: true
+ allowForUnclearPrecedence: false
+ UntilInsteadOfRangeTo:
+ active: true
+ UnusedImports:
+ active: true
+ UnusedPrivateClass:
+ active: true
+ UnusedPrivateMember:
+ active: true
+ allowedNames: '(_|ignored|expected|serialVersionUID)'
+ UseAnyOrNoneInsteadOfFind:
+ active: true
+ UseArrayLiteralsInAnnotations:
+ active: true
+ UseCheckNotNull:
+ active: true
+ UseCheckOrError:
+ active: true
+ UseDataClass:
+ active: true
+ allowVars: false
+ UseEmptyCounterpart:
+ active: true
+ UseIfEmptyOrIfBlank:
+ active: true
+ UseIfInsteadOfWhen:
+ active: false
+ UseIsNullOrEmpty:
+ active: true
+ UseOrEmpty:
+ active: true
+ UseRequire:
+ active: true
+ UseRequireNotNull:
+ active: true
+ UselessCallOnNotNull:
+ active: true
+ UtilityClassWithPublicConstructor:
+ active: true
+ VarCouldBeVal:
+ active: true
+ ignoreLateinitVar: false
+ WildcardImport:
+ active: true
+ excludeImports:
+ - 'java.util.*'
diff --git a/git-hooks/pre-push b/git-hooks/pre-push
new file mode 100644
index 0000000..17df2dd
--- /dev/null
+++ b/git-hooks/pre-push
@@ -0,0 +1,39 @@
+#!/bin/sh
+echo "*********************************************************"
+echo "Running git pre-push hook. Running Static analysis... "
+echo "*********************************************************"
+
+./gradlew detekt ktlintCheck --daemon
+
+status=$?
+
+if [ "$status" = 0 ] ; then
+ echo "Static analysis found no problems."
+ exit 0
+else
+ echo "*********************************************************"
+ echo " ******************************************** "
+ echo 1>&2 "Static analysis found violations it could not fix."
+ echo "Run ./gradlew ktlintFormat to fix formatting related issues."
+ echo " ******************************************** "
+ echo "*********************************************************"
+ exit 1
+fi
+
+
+
+#echo "Running static code analysis..."
+#
+## Run Detekt, KtLint and Checkstyle static analysis
+#./gradlew detekt ktlintCheck --daemon
+#
+#status=$?
+#
+#if [ "$status" = 0 ] ; then
+# echo "Static analysis found no problems."
+# exit 0
+#else
+# echo 1>&2 "Static analysis found violations! Fix then before pushing your code!"
+# echo "See generated reports above or in /app/build/reports folder"
+# exit 1
+#fi
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 0a8d0a1..3d0addc 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Sun Aug 21 14:09:45 IST 2022
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
old mode 100644
new mode 100755
diff --git a/scripts/pre-push b/scripts/pre-push
new file mode 100644
index 0000000..02aff6f
--- /dev/null
+++ b/scripts/pre-push
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+echo "*********************************************************"
+echo "Running git pre-push hook. Running Static analysis... "
+echo "*********************************************************"
+
+git stash -q --keep-index
+
+./gradlew detekt ktlintCheck --daemon
+
+status=$?
+
+git stash pop -q
+
+if [ "$status" = 0 ] ; then
+ echo "Static analysis found no problems."
+ exit 0
+else
+ echo "*********************************************************"
+ echo " ******************************************** "
+ echo 1>&2 "Static analysis found violations it could not fix."
+ echo "Run ./gradlew ktlintFormat to fix formatting related issues."
+ echo " ******************************************** "
+ echo "*********************************************************"
+ exit 1
+fi