From 8e16342292508517b69a0df1689630de0ff9d387 Mon Sep 17 00:00:00 2001 From: rasel Date: Mon, 27 Nov 2023 03:11:28 +0600 Subject: [PATCH 01/15] Empty title and description field restricted --- .../4.json | 79 ++++++++++++++++++ .../com/thatsmanmeet/taskyapp/MainActivity.kt | 1 + .../taskyapp/components/AddTodoDialog.kt | 81 ++++++++++--------- gradle.properties | 3 +- 4 files changed, 126 insertions(+), 38 deletions(-) create mode 100644 app/schemas/com.thatsmanmeet.taskyapp.room.TodoDatabase/4.json diff --git a/app/schemas/com.thatsmanmeet.taskyapp.room.TodoDatabase/4.json b/app/schemas/com.thatsmanmeet.taskyapp.room.TodoDatabase/4.json new file mode 100644 index 0000000..76ed0c7 --- /dev/null +++ b/app/schemas/com.thatsmanmeet.taskyapp.room.TodoDatabase/4.json @@ -0,0 +1,79 @@ +{ + "formatVersion": 1, + "database": { + "version": 4, + "identityHash": "ba7463236f237b93f55f7180888e06e3", + "entities": [ + { + "tableName": "todo_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`ID` INTEGER PRIMARY KEY AUTOINCREMENT, `title` TEXT, `completed` INTEGER NOT NULL, `date` TEXT, `time` TEXT, `notificationID` INTEGER NOT NULL DEFAULT 0, `is_Recurring` INTEGER NOT NULL DEFAULT false, `description` TEXT DEFAULT '')", + "fields": [ + { + "fieldPath": "ID", + "columnName": "ID", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isCompleted", + "columnName": "completed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notificationID", + "columnName": "notificationID", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "isRecurring", + "columnName": "is_Recurring", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "false" + }, + { + "fieldPath": "todoDescription", + "columnName": "description", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "''" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "ID" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ba7463236f237b93f55f7180888e06e3')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt index b01322d..67fd991 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt @@ -185,6 +185,7 @@ class MainActivity : ComponentActivity() { } } + @Throws(IOException::class) private fun copyStream(input: InputStream, output: OutputStream) { val buffer = ByteArray(1024 * 8) diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/components/AddTodoDialog.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/components/AddTodoDialog.kt index c508132..3d2f9d4 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/components/AddTodoDialog.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/components/AddTodoDialog.kt @@ -1,6 +1,7 @@ package com.thatsmanmeet.taskyapp.components import android.content.Context +import android.widget.Toast import androidx.compose.foundation.layout.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.DateRange @@ -159,39 +160,41 @@ fun addTodoDialog( }, confirmButton = { Button(onClick = { - val todo = Todo( - ID = null, - enteredText1.ifEmpty { "No Name" }, - isCompleted = false, - date = dateText.value.ifEmpty { - SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH).format( + if (enteredText1.isNotEmpty() && descriptionText.isNotEmpty()){ + val todo = Todo( + ID = null, + title = enteredText1, + //enteredText1.ifEmpty { "No Name" }, + isCompleted = false, + date = dateText.value.ifEmpty { + SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH).format( + Calendar.getInstance().time + ).toString() + }, + time = timeText.value, + notificationID = ((0..2000).random() - (0..50).random()), + isRecurring = isRepeating, + todoDescription = descriptionText + ) + todoViewModel.insertTodo( + todo + ) + if(dateText.value.isEmpty()){ + dateText.value = SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH).format( Calendar.getInstance().time ).toString() - }, - time = timeText.value, - notificationID = ((0..2000).random() - (0..50).random()), - isRecurring = isRepeating, - todoDescription = descriptionText - ) - todoViewModel.insertTodo( - todo - ) - if(dateText.value.isEmpty()){ - dateText.value = SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH).format( - Calendar.getInstance().time - ).toString() - } - if (dateText.value.isNotEmpty() && timeText.value.isNotEmpty()) { - // SCHEDULE NOTIFICATION - val format = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()) - val parsedDate = format.parse(dateText.value) - val calendar = Calendar.getInstance().apply { - time = parsedDate!! - set(Calendar.HOUR_OF_DAY, todo.time!!.substringBefore(":").toInt()) - set(Calendar.MINUTE, todo.time!!.substringAfter(":").toInt()) - set(Calendar.SECOND, 0) } - if(calendar.timeInMillis > Calendar.getInstance().timeInMillis){ + if (dateText.value.isNotEmpty() && timeText.value.isNotEmpty()) { + // SCHEDULE NOTIFICATION + val format = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()) + val parsedDate = format.parse(dateText.value) + val calendar = Calendar.getInstance().apply { + time = parsedDate!! + set(Calendar.HOUR_OF_DAY, todo.time!!.substringBefore(":").toInt()) + set(Calendar.MINUTE, todo.time!!.substringAfter(":").toInt()) + set(Calendar.SECOND, 0) + } + if(calendar.timeInMillis > Calendar.getInstance().timeInMillis){ scheduleNotification( context, titleText = enteredText1, @@ -199,15 +202,19 @@ fun addTodoDialog( time = "${dateText.value} ${timeText.value}", todo = todo ) + } + if(isRepeating){ + setRepeatingAlarm(context = context) + } } - if(isRepeating){ - setRepeatingAlarm(context = context) - } + openDialog.value = false + enteredText1 = "" + descriptionText = "" + isRepeating = false + } + else{ + Toast.makeText(context,"Fill in all fields",Toast.LENGTH_SHORT).show() } - openDialog.value = false - enteredText1 = "" - descriptionText = "" - isRepeating = false } ) { Text(text = stringResource(R.string.add_edit_dialog_add_button_text)) diff --git a/gradle.properties b/gradle.properties index a2e90d8..88e53ba 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,4 +22,5 @@ kotlin.code.style=official # thereby reducing the size of the R class for that library android.nonTransitiveRClass=true android.defaults.buildfeatures.buildconfig=true -android.nonFinalResIds=false \ No newline at end of file +android.nonFinalResIds=false +android.enableR8.fullMode=false \ No newline at end of file From 753b444991b2e6484cc8c181bf3e44695690f005 Mon Sep 17 00:00:00 2001 From: rasel Date: Mon, 27 Nov 2023 03:21:46 +0600 Subject: [PATCH 02/15] App exit AlertDialog added --- .../java/com/thatsmanmeet/taskyapp/MainActivity.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt index 67fd991..d5fc49d 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt @@ -4,6 +4,7 @@ package com.thatsmanmeet.taskyapp import android.Manifest import android.annotation.SuppressLint import android.app.Activity +import android.app.AlertDialog import android.content.Context import android.content.Intent import android.content.pm.PackageManager @@ -185,6 +186,18 @@ class MainActivity : ComponentActivity() { } } + override fun onBackPressed() { + AlertDialog.Builder(this) + .setTitle("Confirm Exit ?") + .setMessage("Are you sure you want to exit?") + .setPositiveButton("Yes") { _, _ -> + super.onBackPressed() + } + .setNegativeButton("No") { dialog, _ -> + dialog.cancel() + } + .show() + } @Throws(IOException::class) private fun copyStream(input: InputStream, output: OutputStream) { From ec0904f51e6d9a93cfa2de0833dac470a3e30143 Mon Sep 17 00:00:00 2001 From: rasel Date: Mon, 27 Nov 2023 03:52:58 +0600 Subject: [PATCH 03/15] Add Task dialog - Outside click dismiss fixed --- .../java/com/thatsmanmeet/taskyapp/components/AddTodoDialog.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/components/AddTodoDialog.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/components/AddTodoDialog.kt index 3d2f9d4..f0b3c5a 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/components/AddTodoDialog.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/components/AddTodoDialog.kt @@ -52,7 +52,7 @@ fun addTodoDialog( if (openDialog.value) { AlertDialog( onDismissRequest = { - openDialog.value = false + //openDialog.value = false enteredText1 = "" }, title = { Text(text = stringResource(R.string.add_task_dialog_title)) }, From 154a098deb1b48c17a42a38cc11988e91970a14f Mon Sep 17 00:00:00 2001 From: rasel Date: Mon, 27 Nov 2023 03:56:53 +0600 Subject: [PATCH 04/15] Edit Task dialog - Outside click dismiss fixed --- .../com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt index dd0d4e9..ee90e2c 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt @@ -90,7 +90,7 @@ fun OpenEditTodoDialog( var todo : Todo AlertDialog( onDismissRequest = { - openEditDialog.value = false + //openEditDialog.value = false }, title = { Text(text = stringResource(R.string.edit_task_dialog_title)) }, text = { From 298ee83b4850c8bdc85f914f357379ff0d2e6f97 Mon Sep 17 00:00:00 2001 From: rasel Date: Tue, 28 Nov 2023 22:59:29 +0600 Subject: [PATCH 05/15] Search feature added --- app/build.gradle | 1 + .../com/thatsmanmeet/taskyapp/MainActivity.kt | 25 ++-- .../taskyapp/components/OpenEditTodoDialog.kt | 60 ++++---- .../taskyapp/components/SearchBarTop.kt | 132 ++++++++++++++++++ .../taskyapp/components/TaskList.kt | 26 +++- .../com/thatsmanmeet/taskyapp/screens/App.kt | 32 +++-- .../thatsmanmeet/taskyapp/screens/Screen.kt | 1 + app/src/main/res/drawable/cross_icon.png | Bin 0 -> 852 bytes app/src/main/res/drawable/search_icon32.png | Bin 0 -> 697 bytes app/src/main/res/drawable/search_icon64.png | Bin 0 -> 1307 bytes 10 files changed, 229 insertions(+), 48 deletions(-) create mode 100644 app/src/main/java/com/thatsmanmeet/taskyapp/components/SearchBarTop.kt create mode 100644 app/src/main/res/drawable/cross_icon.png create mode 100644 app/src/main/res/drawable/search_icon32.png create mode 100644 app/src/main/res/drawable/search_icon64.png diff --git a/app/build.gradle b/app/build.gradle index c146c7e..66c4baa 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -64,6 +64,7 @@ dependencies { implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" implementation 'androidx.compose.material3:material3:1.2.0-alpha06' + implementation 'androidx.media3:media3-common:1.2.0' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt index d5fc49d..23361be 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt @@ -186,18 +186,19 @@ class MainActivity : ComponentActivity() { } } - override fun onBackPressed() { - AlertDialog.Builder(this) - .setTitle("Confirm Exit ?") - .setMessage("Are you sure you want to exit?") - .setPositiveButton("Yes") { _, _ -> - super.onBackPressed() - } - .setNegativeButton("No") { dialog, _ -> - dialog.cancel() - } - .show() - } +// override fun onBackPressed() { +// //navController. +// AlertDialog.Builder(this) +// .setTitle("Confirm Exit ?") +// .setMessage("Are you sure you want to exit?") +// .setPositiveButton("Yes") { _, _ -> +// super.onBackPressed() +// } +// .setNegativeButton("No") { dialog, _ -> +// dialog.cancel() +// } +// .show() +// } @Throws(IOException::class) private fun copyStream(input: InputStream, output: OutputStream) { diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt index ee90e2c..155e805 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt @@ -267,29 +267,7 @@ fun OpenEditTodoDialog( dismissButton = { Button( onClick = { - openEditDialog.value = false - todo = Todo( - currentTodoID.value, - currentTodoTitle.value, - currentTodoChecked.value, - currentTodoDateValue.value, - currentTodoTimeValue.value, - currentTodoNotificationId.value, - isRepeating, - currentDescriptionText - ) - todoViewModel.deleteTodo( - todo - ) - cancelNotification( - context = context, - todo = todo - ) - enteredText1 = "" - currentDescriptionText = "" - if(savedSoundKey.value == true){ - todoViewModel.playDeletedSound(context) - } + openEditDialog.value = false }, colors = ButtonDefaults.buttonColors( containerColor = Color( @@ -297,8 +275,42 @@ fun OpenEditTodoDialog( ), contentColor = Color.White ) ) { - Text(text = stringResource(R.string.add_edit_dialog_delete_button_text)) + Text(text = "Cancel") } +// Button( +// onClick = { +// openEditDialog.value = false +// todo = Todo( +// currentTodoID.value, +// currentTodoTitle.value, +// currentTodoChecked.value, +// currentTodoDateValue.value, +// currentTodoTimeValue.value, +// currentTodoNotificationId.value, +// isRepeating, +// currentDescriptionText +// ) +// todoViewModel.deleteTodo( +// todo +// ) +// cancelNotification( +// context = context, +// todo = todo +// ) +// enteredText1 = "" +// currentDescriptionText = "" +// if(savedSoundKey.value == true){ +// todoViewModel.playDeletedSound(context) +// } +// }, +// colors = ButtonDefaults.buttonColors( +// containerColor = Color( +// 0xFFF1574C +// ), contentColor = Color.White +// ) +// ) { +// Text(text = stringResource(R.string.add_edit_dialog_delete_button_text)) +// } } ) } \ No newline at end of file diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/components/SearchBarTop.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/components/SearchBarTop.kt new file mode 100644 index 0000000..402a879 --- /dev/null +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/components/SearchBarTop.kt @@ -0,0 +1,132 @@ +package com.thatsmanmeet.taskyapp.components + +import android.annotation.SuppressLint +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.expandHorizontally +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.shrinkHorizontally +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.slideOutHorizontally +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Clear +import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.SearchBar +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalWindowInfo +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.style.TextMotion +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.thatsmanmeet.taskyapp.R + +@Composable +fun SearchBarTop(searchText: String, onValueChange: (String)->Unit) { + var searchBarVisible by rememberSaveable { + mutableStateOf(false) + } + //Auto keyboard focus + val windowInfo = LocalWindowInfo.current + val focusRequester = remember { FocusRequester() } + + Row( + horizontalArrangement = Arrangement.End, + modifier = Modifier.padding(horizontal = 10.dp) + .fillMaxWidth(0.8f) + ) { + if(!searchBarVisible){ + IconButton( + onClick = { + searchBarVisible = true + } + ) { + Icon(Icons.Default.Search, contentDescription = "Search") + } + } + else{ + + TextField( + value = searchText,//searchText , + onValueChange = onValueChange, + modifier = Modifier + .fillMaxWidth() + .align(Alignment.CenterVertically) + .padding(top = 10.dp, bottom = 10.dp) + .focusRequester(focusRequester), + textStyle = TextStyle( + textMotion = TextMotion.Animated, + fontFamily = FontFamily.Monospace, + fontSize = 18.sp + ), + shape = RoundedCornerShape(20.dp), + leadingIcon = { + Icon( + Icons.Default.Search, + contentDescription = "Search", + modifier = Modifier.height(20.dp).width(20.dp) + ) + }, + trailingIcon = { + IconButton(onClick = { + if (searchText.isNotEmpty()) { + onValueChange("") + } else { + searchBarVisible = false + } + }) { + Icon( + Icons.Default.Clear, + contentDescription = "Clear", + modifier = Modifier.height(20.dp).width(20.dp) + ) + } + }, + placeholder = { Text(text = "Search") }, + singleLine = true + ) + LaunchedEffect(windowInfo) { + snapshotFlow { windowInfo.isWindowFocused }.collect { isWindowFocused -> + if (isWindowFocused) { + focusRequester.requestFocus() + } + } + } + } + } +} +@Preview +@Composable +fun SearchBarTopPreview() { + SearchBarTop(" ", {}) +} diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt index 4087020..22d951a 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt @@ -1,5 +1,6 @@ package com.thatsmanmeet.taskyapp.components +import android.widget.Toast import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.ExperimentalFoundationApi @@ -8,6 +9,7 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn @@ -16,22 +18,30 @@ import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material3.AlertDialog import androidx.compose.material3.DismissDirection import androidx.compose.material3.DismissValue import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SwipeToDismiss +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.TextField import androidx.compose.material3.rememberDismissState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.movableContentOf +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.scale import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import com.thatsmanmeet.taskyapp.room.Todo import com.thatsmanmeet.taskyapp.room.TodoViewModel @@ -46,11 +56,22 @@ fun TaskList( state: LazyListState, list : List, todoViewModel: TodoViewModel, - onClick : (Int) -> Unit + onClick : (Int) -> Unit, + searchText: String ) { - val grouped = list.groupBy { + val context = LocalContext.current + //Filter list for search operation. + val regex = Regex(searchText, RegexOption.IGNORE_CASE) + val searchedList = if(searchText.isEmpty()) list + else list.filter { + regex.containsMatchIn(it.title.toString()) + || regex.containsMatchIn(it.todoDescription.toString()) + } + + val grouped = searchedList.groupBy { SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()).parse(it.date!!) }.entries.sortedByDescending { it.key } + LazyColumn( state = state, modifier = modifier.padding(16.dp) @@ -68,6 +89,7 @@ fun TaskList( val currentItem by rememberUpdatedState(item) val dismissState = rememberDismissState() + if(dismissState.isDismissed(direction = DismissDirection.EndToStart)){ todoViewModel.deleteTodo(currentItem) } diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/screens/App.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/screens/App.kt index 2b64c47..151d627 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/screens/App.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/screens/App.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.unit.sp import androidx.navigation.NavHostController import com.thatsmanmeet.taskyapp.R import com.thatsmanmeet.taskyapp.components.OpenEditTodoDialog +import com.thatsmanmeet.taskyapp.components.SearchBarTop import com.thatsmanmeet.taskyapp.components.TaskCompleteAnimations import com.thatsmanmeet.taskyapp.components.TaskList import com.thatsmanmeet.taskyapp.components.addTodoDialog @@ -86,6 +87,11 @@ fun MyApp( val isLottiePlaying = remember { mutableStateOf(true) } + + var searchText by rememberSaveable { + mutableStateOf("") + } + createNotificationChannel(context.applicationContext) // setup settings store val settingsStore = SettingsStore(context) @@ -111,17 +117,22 @@ fun MyApp( text = stringResource(id = R.string.app_name), fontSize = 30.sp ) - IconButton(onClick = { - // Implement Navigation to settings - navHostController.navigate(route = Screen.SettingsScreen.route) - }) { - Icon( - imageVector = Icons.Default.Settings, - contentDescription = null - ) - } + SearchBarTop(searchText,{searchText = it}) + } }, + actions = { + IconButton(onClick = { + // Implement Navigation to settings + navHostController.navigate(route = Screen.SettingsScreen.route) + }) { + Icon( + imageVector = Icons.Default.Settings, + contentDescription = null, + tint = MaterialTheme.colorScheme.onPrimary + ) + } + }, colors = topAppBarColors.topAppBarColors( containerColor = MaterialTheme.colorScheme.primary, titleContentColor = MaterialTheme.colorScheme.onPrimary @@ -191,7 +202,8 @@ fun MyApp( onClick = {index-> selectedItem.intValue = index openEditDialog.value = true - } + }, + searchText = searchText ) } if (openEditDialog.value){ diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/screens/Screen.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/screens/Screen.kt index 9575698..b887282 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/screens/Screen.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/screens/Screen.kt @@ -4,4 +4,5 @@ sealed class Screen(val route:String){ object MyApp: Screen(route = "myapp_screen") object PermissionScreen: Screen("permission_screen") object SettingsScreen : Screen("settings_screen") + object SearchScreen: Screen("search_screen") } diff --git a/app/src/main/res/drawable/cross_icon.png b/app/src/main/res/drawable/cross_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..733c27879eaa45de1e6af2f0be4381abd31ac0f3 GIT binary patch literal 852 zcmV-a1FQUrP)JT>Q%j_%^!;QUY+58 z?mff3Z$NhpV0E37RAaT!B(N{2$> zgSlCG6-2#&W{T^5{DRC`^)?ZK2(plz54-N_o+B0LYSGx^J5 z#gN(C;B+eFPg{bR2%QX98g%w6>TCh*P_*3A4vw3G=%NG56t%F8G%_<>Yg$%d98W^` zFCTYFnY=2i!;R2A4cFtxFD*t*LG()wM?-%~$>FXJhSeZmNGD-GtFOiPRf0TH-n{@y-vm|`qAxaKPW{{x08;P^K;U?n3hs7j6ZbWh0*x@XApPe zM^g*dkuM424OlM~;le(G$hq#4CQ_F+>{ac(TC@&%8~zI0{l9}~xndA)<>OJjufb?G zqxfor&Vi6mE3SI~O~Yr>flu6o#bhQhrpfokQ|IR8bHH@Lv*16ntP~e342)uu&A06cO=(q9XV= z7)Y1GN?Qf7OXZ`m42Xq@qQ=xd+C&Sr&?YJxF`{TTF=#B#E{1#0y?b^y$p?qs9nQ@6 zojY@9&eAyAu&N2arqP2zjN$|4Fpuwei6I_Qbn&&yxAB>Q`Mh{IM3s;(BaC&_ZN_Vk~o%Y zxLF`#8aIR+&vtCW9-NcUd3H?*V>t<3!pv{wGE%f|$v3y+VBy3AnSAbn2(z=gxQ!n#<+dh zj?b_K?_y78UmBwitMDO4O4_iLgk?CtS@+8`4Xbfb`M}4reRvEPG#5%Sda(k($Q$^{ z_7m=x|1qt^biAeBz)!Yy#(s}9&OGebZ164n9yin<;`l7WpSlx#%MRhTx&s}HB{){l z*}CiqZmBoi6PZ6M+U>yG?KfPXE5OseVQ1h|X}K%PKD>^N*onQ;s&VnCU5N#wc43M6 zci=`GDDm46y?9UYtFvCZYt9MkOu#~HDxvjKl>j@jqM$S1;ns?pcVTxy-6a(@$7Gsf zMb0|(XVRXI_X}w6!o*D4|FXJ(1JBC8qnwCW3;6I*PVqkR+J2q-w$qcOU1G$KAEkFm z7FGpCcgT0!kH-x0%JZIFq8|kXZ_g#zfd_*Aypls~mej53B(^?>Sdbok9PsG~rc@P+ zW_x~Bp@Q5S^lL#?p=Sajd&P^KTujE%fNzhNnY=xip}_d{(qqyjhoyE|C$1|qd3yq8 z2gYwKBa(r)17of#GjS^c6UDkNyUK`UAfh|H%*3q(MBkv@YF+ORjOkaRf7pHpM5t~1 z*@0gJV@g$}tppqij_+1;GSZV6mR5s8v=VSAIR28N0+|>LjEUSOr_f5k-_jc2i7U#8 zWFVp&c~6`|`!e`#V0`6jt0a-V$2VmrZco7HK}WJ}J%jXPZeZLOWhQS=z^1_X0nP0M zTP(r3fpJ@^3Jv=2O?o=pTvez5z4$WV(;w0~gvz`V5X;(t=s>hN`$8_kWkJC`VoOsM zW=kJIBK|DQB|1r34kMzQN5|JezjS&e;>R|bx9E=sg-7t3{7L9ntTnzov%V=(r#0%p z2bh^jd$#zMACX>=wJgi`6>wq%KT9pOvZpzEu@b)*)ZMP8uf;uhxu7#6_(V!xbbCF3 z2gR1U@$5LBSF#^)x+`{(6ws^^^Zq40#~Y-t!+WI7%2Zs6%cZhc3E<2;B+}qjX+S1wa~5@P>Q|IHsAu3uR@dRN>`HLaEJDw^=G* z0_y5!)y{LO2j8vF{8^YHtsqqr@KoboXJ(G{EuicR@Y?2G&d(y{{>ZVW8_w3nO2ta$ zH#Prlvvw9sP1#Pj{TgRs@GbhJ@^-5eAB&xM64!Livo6pp{VgZ^{|L@X{sEr~zkLqR R5+wit002ovPDHLkV1oO~eg^;m literal 0 HcmV?d00001 From 0b7bf2c57ff76708f6d747ded2c37dc385132186 Mon Sep 17 00:00:00 2001 From: rasel Date: Wed, 29 Nov 2023 00:02:58 +0600 Subject: [PATCH 06/15] App exit confirmation dialog added --- .../com/thatsmanmeet/taskyapp/MainActivity.kt | 63 +++++++++++++++---- 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt index 23361be..a69a405 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt @@ -12,6 +12,7 @@ import android.net.Uri import android.os.Build import android.os.Bundle import android.provider.DocumentsContract +import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.setContent @@ -186,19 +187,55 @@ class MainActivity : ComponentActivity() { } } -// override fun onBackPressed() { -// //navController. -// AlertDialog.Builder(this) -// .setTitle("Confirm Exit ?") -// .setMessage("Are you sure you want to exit?") -// .setPositiveButton("Yes") { _, _ -> -// super.onBackPressed() -// } -// .setNegativeButton("No") { dialog, _ -> -// dialog.cancel() -// } -// .show() -// } + override fun onDestroy() { + Log.d("func","onDestroy") + super.onDestroy() + } + + override fun onRestart() { + Log.d("func","onRestart()") + super.onRestart() + } + + override fun onPause() { + Log.d("func","onPause()") + super.onPause() + } + + override fun onStop() { + Log.d("func","onStop()") + super.onStop() + } + + override fun onContentChanged() { + Log.d("func","onContentChanged()") + super.onContentChanged() + } + + override fun onDetachedFromWindow() { + Log.d("func","onDetachedFromWindow()") + super.onDetachedFromWindow() + } + + override fun onBackPressed() { + val currentRoute = navController.currentBackStackEntry?.destination?.route + if(currentRoute == Screen.MyApp.route){ + AlertDialog.Builder(this) + .setTitle("Confirm Exit ?") + .setMessage("Are you sure you want to exit?") + .setPositiveButton("Yes") { _, _ -> + //super.onBackPressed() + finish() + } + .setNegativeButton("No") { dialog, _ -> + dialog.cancel() + } + .show() + } + else{ + super.onBackPressed() + } + } @Throws(IOException::class) private fun copyStream(input: InputStream, output: OutputStream) { From c84aa18b816673dad3b0771031fd0d3c5982f043 Mon Sep 17 00:00:00 2001 From: rasel Date: Wed, 29 Nov 2023 00:19:45 +0600 Subject: [PATCH 07/15] Some TextField related bug fixed --- .../com/thatsmanmeet/taskyapp/components/AddTodoDialog.kt | 7 +++++-- .../taskyapp/components/OpenEditTodoDialog.kt | 8 ++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/components/AddTodoDialog.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/components/AddTodoDialog.kt index f0b3c5a..271c85c 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/components/AddTodoDialog.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/components/AddTodoDialog.kt @@ -64,7 +64,8 @@ fun addTodoDialog( onValueChange = { textChange -> enteredText1 = textChange }, - maxLines = 1 + maxLines = 1, + singleLine = true ) Spacer(modifier = modifier.height(10.dp)) OutlinedTextField( @@ -72,7 +73,9 @@ fun addTodoDialog( placeholder = { Text(text = "Description")}, onValueChange = {descriptionTextChange-> descriptionText = descriptionTextChange - }) + }, + maxLines = 4 + ) Spacer(modifier = modifier.height(10.dp)) Text(text = stringResource(R.string.add_edit_dialog_set_reminder_title)) Spacer(modifier = modifier.height(10.dp)) diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt index 155e805..4d95661 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt @@ -100,7 +100,9 @@ fun OpenEditTodoDialog( placeholder = { Text(text = stringResource(id = R.string.add_edit_text_placeholder)) }, onValueChange = { textChange -> currentTodoTitle.value = textChange - } + }, + maxLines = 1, + singleLine = true ) Spacer(modifier = modifier.height(10.dp)) OutlinedTextField( @@ -108,7 +110,9 @@ fun OpenEditTodoDialog( placeholder = { Text(text = "Description")}, onValueChange = {descriptionTextChange-> currentDescriptionText = descriptionTextChange - }) + }, + maxLines = 4 + ) Spacer(modifier = modifier.height(12.dp)) Text(text = stringResource(R.string.add_edit_dialog_edit_reminder_title)) Spacer(modifier = modifier.height(10.dp)) From a259f82ba38ac946078c83d3371f3e32fe77bcba Mon Sep 17 00:00:00 2001 From: rasel Date: Wed, 29 Nov 2023 00:33:03 +0600 Subject: [PATCH 08/15] Some unnecessary files are deleted --- .../com/thatsmanmeet/taskyapp/MainActivity.kt | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt index a69a405..9f4e76c 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt @@ -187,36 +187,6 @@ class MainActivity : ComponentActivity() { } } - override fun onDestroy() { - Log.d("func","onDestroy") - super.onDestroy() - } - - override fun onRestart() { - Log.d("func","onRestart()") - super.onRestart() - } - - override fun onPause() { - Log.d("func","onPause()") - super.onPause() - } - - override fun onStop() { - Log.d("func","onStop()") - super.onStop() - } - - override fun onContentChanged() { - Log.d("func","onContentChanged()") - super.onContentChanged() - } - - override fun onDetachedFromWindow() { - Log.d("func","onDetachedFromWindow()") - super.onDetachedFromWindow() - } - override fun onBackPressed() { val currentRoute = navController.currentBackStackEntry?.destination?.route if(currentRoute == Screen.MyApp.route){ From 4917236f902be6f02741cc9a3a2dcc2fe9a7b2d3 Mon Sep 17 00:00:00 2001 From: thatsmanmeet <120583077+thatsmanmeet@users.noreply.github.com> Date: Thu, 30 Nov 2023 11:31:27 +0530 Subject: [PATCH 09/15] Restore delete button from edit screen and changed esit app button dialog theme --- .../com/thatsmanmeet/taskyapp/MainActivity.kt | 12 ++-- .../taskyapp/components/OpenEditTodoDialog.kt | 60 ++++++++----------- .../taskyapp/components/TaskList.kt | 3 +- .../com/thatsmanmeet/taskyapp/screens/App.kt | 2 +- 4 files changed, 32 insertions(+), 45 deletions(-) diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt index 9f4e76c..5619c97 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/MainActivity.kt @@ -12,16 +12,17 @@ import android.net.Uri import android.os.Build import android.os.Bundle import android.provider.DocumentsContract -import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.setContent import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.RequiresApi +import androidx.compose.foundation.layout.padding import androidx.compose.material3.Scaffold import androidx.compose.material3.ScaffoldDefaults import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.core.content.ContextCompat import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen @@ -56,8 +57,7 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) installSplashScreen() setContent { - Scaffold(contentWindowInsets = ScaffoldDefaults.contentWindowInsets) { - it; + Scaffold(contentWindowInsets = ScaffoldDefaults.contentWindowInsets) {paddingValues-> val context = LocalContext.current val viewModel = MainViewModel() val pageState = remember { @@ -91,7 +91,7 @@ class MainActivity : ComponentActivity() { } else { PermissionRequestScreen(navHostController = navController, requestOnClick = { notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) - }) + }, modifier = Modifier.padding(paddingValues)) } } // If permissions are already accepted. @@ -186,11 +186,11 @@ class MainActivity : ComponentActivity() { } } } - + @Deprecated("Deprecated in Java") override fun onBackPressed() { val currentRoute = navController.currentBackStackEntry?.destination?.route if(currentRoute == Screen.MyApp.route){ - AlertDialog.Builder(this) + AlertDialog.Builder(this,android.R.style.Theme_DeviceDefault_Dialog) .setTitle("Confirm Exit ?") .setMessage("Are you sure you want to exit?") .setPositiveButton("Yes") { _, _ -> diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt index 4d95661..95dbab0 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt @@ -271,7 +271,29 @@ fun OpenEditTodoDialog( dismissButton = { Button( onClick = { - openEditDialog.value = false + openEditDialog.value = false + todo = Todo( + currentTodoID.value, + currentTodoTitle.value, + currentTodoChecked.value, + currentTodoDateValue.value, + currentTodoTimeValue.value, + currentTodoNotificationId.value, + isRepeating, + currentDescriptionText + ) + todoViewModel.deleteTodo( + todo + ) + cancelNotification( + context = context, + todo = todo + ) + enteredText1 = "" + currentDescriptionText = "" + if(savedSoundKey.value == true){ + todoViewModel.playDeletedSound(context) + } }, colors = ButtonDefaults.buttonColors( containerColor = Color( @@ -279,42 +301,8 @@ fun OpenEditTodoDialog( ), contentColor = Color.White ) ) { - Text(text = "Cancel") + Text(text = stringResource(R.string.add_edit_dialog_delete_button_text)) } -// Button( -// onClick = { -// openEditDialog.value = false -// todo = Todo( -// currentTodoID.value, -// currentTodoTitle.value, -// currentTodoChecked.value, -// currentTodoDateValue.value, -// currentTodoTimeValue.value, -// currentTodoNotificationId.value, -// isRepeating, -// currentDescriptionText -// ) -// todoViewModel.deleteTodo( -// todo -// ) -// cancelNotification( -// context = context, -// todo = todo -// ) -// enteredText1 = "" -// currentDescriptionText = "" -// if(savedSoundKey.value == true){ -// todoViewModel.playDeletedSound(context) -// } -// }, -// colors = ButtonDefaults.buttonColors( -// containerColor = Color( -// 0xFFF1574C -// ), contentColor = Color.White -// ) -// ) { -// Text(text = stringResource(R.string.add_edit_dialog_delete_button_text)) -// } } ) } \ No newline at end of file diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt index 59896ef..e6d1751 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt @@ -64,7 +64,7 @@ fun TaskList( list : List, todoViewModel: TodoViewModel, onClick : (Int) -> Unit, - searchText: String + searchText: String, coroutineScope: CoroutineScope ) { val context = LocalContext.current @@ -82,7 +82,6 @@ fun TaskList( val isSwipeDeleteDialogShowing = remember { mutableStateOf(false) } - val context = LocalContext.current LazyColumn( state = state, modifier = modifier.padding(16.dp) diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/screens/App.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/screens/App.kt index 9f06b2b..2ff33b7 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/screens/App.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/screens/App.kt @@ -200,7 +200,7 @@ fun MyApp( selectedItem.intValue = index openEditDialog.value = true }, - searchText = searchText + searchText = searchText, coroutineScope = rememberCoroutineScope() ) } From adef03d2a7b0c5b9f9d2e88bdee9d82e6f3cef45 Mon Sep 17 00:00:00 2001 From: thatsmanmeet <120583077+thatsmanmeet@users.noreply.github.com> Date: Thu, 30 Nov 2023 13:33:11 +0530 Subject: [PATCH 10/15] Snackbar Testing --- .../taskyapp/components/OpenEditTodoDialog.kt | 2 +- .../taskyapp/components/TaskList.kt | 71 ++++++++++++------- .../taskyapp/room/TodoViewModel.kt | 14 ++++ .../com/thatsmanmeet/taskyapp/screens/App.kt | 10 ++- 4 files changed, 67 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt index 95dbab0..945d524 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/components/OpenEditTodoDialog.kt @@ -90,7 +90,7 @@ fun OpenEditTodoDialog( var todo : Todo AlertDialog( onDismissRequest = { - //openEditDialog.value = false + openEditDialog.value = false }, title = { Text(text = stringResource(R.string.edit_task_dialog_title)) }, text = { diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt index e6d1751..8316e7b 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt @@ -1,6 +1,5 @@ package com.thatsmanmeet.taskyapp.components -import android.widget.Toast import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.ExperimentalFoundationApi @@ -9,7 +8,6 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn @@ -19,25 +17,23 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Check import androidx.compose.material.icons.outlined.Delete -import androidx.compose.material3.AlertDialog import androidx.compose.material3.DismissDirection import androidx.compose.material3.DismissValue import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.SnackbarResult import androidx.compose.material3.SwipeToDismiss -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.material3.TextField import androidx.compose.material3.rememberDismissState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.movableContentOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -65,7 +61,8 @@ fun TaskList( todoViewModel: TodoViewModel, onClick : (Int) -> Unit, searchText: String, - coroutineScope: CoroutineScope + coroutineScope: CoroutineScope, + snackbarHostState: SnackbarHostState ) { val context = LocalContext.current //Filter list for search operation. @@ -101,25 +98,45 @@ fun TaskList( if(dismissState.isDismissed(direction = DismissDirection.EndToStart)){ - isSwipeDeleteDialogShowing.value = true - ActionDialogBox( - isDialogShowing = isSwipeDeleteDialogShowing, - title = "Delete Task?", - message = "Do you want to delete this task?", - confirmButtonText = "Delete", - dismissButtonText = "Cancel", - onConfirmClick = { - todoViewModel.deleteTodo(currentItem) - }, - onDismissClick = { - isSwipeDeleteDialogShowing.value = false - coroutineScope.launch { - dismissState.reset() - } - }, - confirmButtonColor = Color(0xFFF75F5F), - confirmButtonContentColor = Color.White +// isSwipeDeleteDialogShowing.value = true +// ActionDialogBox( +// isDialogShowing = isSwipeDeleteDialogShowing, +// title = "Delete Task?", +// message = "Do you want to delete this task?", +// confirmButtonText = "Delete", +// dismissButtonText = "Cancel", +// onConfirmClick = { +// todoViewModel.deleteTodo(currentItem) +// }, +// onDismissClick = { +// isSwipeDeleteDialogShowing.value = false +// coroutineScope.launch { +// dismissState.reset() +// snackbarHostState. +// showSnackbar( +// "Operation Cancelled", +// actionLabel = "OK" +// ) +// } +// }, +// confirmButtonColor = Color(0xFFF75F5F), +// confirmButtonContentColor = Color.White +// ) + LaunchedEffect(Unit){ + val result = snackbarHostState.showSnackbar( + message = "Todo Deleted", + actionLabel = "Undo", + duration = SnackbarDuration.Short ) + when(result){ + SnackbarResult.Dismissed -> { + todoViewModel.deleteTodo(currentItem) + } + SnackbarResult.ActionPerformed -> { + dismissState.reset() + } + } + } } if(dismissState.isDismissed(direction = DismissDirection.StartToEnd)){ diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/room/TodoViewModel.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/room/TodoViewModel.kt index d2a52cd..fa42aea 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/room/TodoViewModel.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/room/TodoViewModel.kt @@ -51,6 +51,20 @@ class TodoViewModel(application: Application) : ViewModel() { } } + private var lastDeletedTodo = Todo() + + fun setLastDeletedTodo(todo: Todo){ + lastDeletedTodo = todo + } + + fun getLastDeletedTodo():Todo{ + return lastDeletedTodo + } + + fun resetLastDeletedTodo(){ + lastDeletedTodo = Todo() + } + fun playDeletedSound(context: Context) { mediaPlayer?.release() mediaPlayer = MediaPlayer.create(context, R.raw.deleted) diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/screens/App.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/screens/App.kt index 2ff33b7..7688878 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/screens/App.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/screens/App.kt @@ -92,6 +92,10 @@ fun MyApp( mutableStateOf("") } + val snackBarHostState = remember { + SnackbarHostState() + } + createNotificationChannel(context.applicationContext) // setup settings store val settingsStore = SettingsStore(context) @@ -105,6 +109,7 @@ fun MyApp( } ) { Scaffold( + snackbarHost = {SnackbarHost(hostState = snackBarHostState)}, topBar = { TopAppBar( title = { @@ -117,7 +122,7 @@ fun MyApp( text = stringResource(id = R.string.app_name), fontSize = 25.sp ) - SearchBarTop(searchText,{searchText = it}) + SearchBarTop(searchText) { searchText = it } Spacer(modifier = modifier.width(10.dp)) IconButton(onClick = { // Implement Navigation to settings @@ -201,7 +206,8 @@ fun MyApp( openEditDialog.value = true }, searchText = searchText, - coroutineScope = rememberCoroutineScope() + coroutineScope = rememberCoroutineScope(), + snackbarHostState = snackBarHostState ) } if (openEditDialog.value){ From 8f150e7c952bf61c10c8247cc41c6f450c842b84 Mon Sep 17 00:00:00 2001 From: thatsmanmeet <120583077+thatsmanmeet@users.noreply.github.com> Date: Thu, 30 Nov 2023 17:05:48 +0530 Subject: [PATCH 11/15] fixed a crash bug --- .../taskyapp/components/TaskList.kt | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt index 8316e7b..cf9f8d9 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt @@ -141,20 +141,25 @@ fun TaskList( if(dismissState.isDismissed(direction = DismissDirection.StartToEnd)){ todoViewModel.updateTodo(currentItem.copy(isCompleted = !currentItem.isCompleted)) - if(!currentItem.isCompleted){ - cancelNotification(LocalContext.current,currentItem) - }else{ - CurrentDateTimeComparator( - inputDate = currentItem.date!!, - inputTime = currentItem.time!!) { - scheduleNotification( - context = context, - titleText = currentItem.title, - messageText = currentItem.todoDescription, - time = "${currentItem.date} ${currentItem.time}", - todo = currentItem - ) + val currentCompletionCondition = !currentItem.isCompleted + if(!currentCompletionCondition){ + if(currentItem.time!! != ""){ + CurrentDateTimeComparator( + inputDate = currentItem.date!!, + inputTime = currentItem.time!!) { + scheduleNotification( + context = context, + titleText = currentItem.title, + messageText = currentItem.todoDescription, + time = "${currentItem.date} ${currentItem.time}", + todo = currentItem + ) + } } + }else{ + // cancel notification + println("Current Status completed:${currentItem.isCompleted}") + cancelNotification(context,currentItem) } } From db35147adf352bf0fa8f7bb19822d1af728268d7 Mon Sep 17 00:00:00 2001 From: thatsmanmeet <120583077+thatsmanmeet@users.noreply.github.com> Date: Thu, 30 Nov 2023 17:08:40 +0530 Subject: [PATCH 12/15] fixed a crash bug and updated android.yml file --- .github/workflows/android.yml | 3 +++ .../com/thatsmanmeet/taskyapp/components/TaskList.kt | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 3940ae9..3bc5124 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -24,3 +24,6 @@ jobs: run: chmod +x gradlew - name: Build with Gradle run: ./gradlew build + - name: Build Debug APK + run: ./gradlew assembleDebug + diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt index cf9f8d9..5ae8c7a 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt @@ -68,10 +68,10 @@ fun TaskList( //Filter list for search operation. val regex = Regex(searchText, RegexOption.IGNORE_CASE) val searchedList = if(searchText.isEmpty()) list - else list.filter { - regex.containsMatchIn(it.title.toString()) - || regex.containsMatchIn(it.todoDescription.toString()) - } + else list.filter { + regex.containsMatchIn(it.title.toString()) + || regex.containsMatchIn(it.todoDescription.toString()) + } val grouped = searchedList.groupBy { SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()).parse(it.date!!) @@ -133,7 +133,7 @@ fun TaskList( todoViewModel.deleteTodo(currentItem) } SnackbarResult.ActionPerformed -> { - dismissState.reset() + dismissState.reset() } } } From 79105e2489fc738ec7ce9c0b67c9b0d81b0c57aa Mon Sep 17 00:00:00 2001 From: Manmeet Singh <120583077+thatsmanmeet@users.noreply.github.com> Date: Thu, 30 Nov 2023 17:20:33 +0530 Subject: [PATCH 13/15] Update android.yml --- .github/workflows/android.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 3bc5124..6538bfe 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -26,4 +26,7 @@ jobs: run: ./gradlew build - name: Build Debug APK run: ./gradlew assembleDebug - + - name: Create APK directory + run: mkdir -p apk + - name: Copy APK to directory + run: cp app/build/outputs/apk/debug/*.apk apk/ From a4b71df84751749028a81e07900024196d49e1b0 Mon Sep 17 00:00:00 2001 From: thatsmanmeet <120583077+thatsmanmeet@users.noreply.github.com> Date: Fri, 1 Dec 2023 10:55:50 +0530 Subject: [PATCH 14/15] crash bug fix and snackbar disabled as for now. --- app/build.gradle | 4 +- .../taskyapp/components/TaskList.kt | 73 ++++++++----------- 2 files changed, 34 insertions(+), 43 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 9eaac2b..f2b11eb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,8 +12,8 @@ android { applicationId "com.thatsmanmeet.taskyapp" minSdk 26 targetSdk 34 - versionCode 24 - versionName "2.4.0" + versionCode 25 + versionName "2.4.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { useSupportLibrary true diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt index 5ae8c7a..03b8611 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskList.kt @@ -22,13 +22,10 @@ import androidx.compose.material3.DismissValue import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.SnackbarResult import androidx.compose.material3.SwipeToDismiss import androidx.compose.material3.rememberDismissState import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.movableContentOf import androidx.compose.runtime.mutableStateOf @@ -98,45 +95,40 @@ fun TaskList( if(dismissState.isDismissed(direction = DismissDirection.EndToStart)){ -// isSwipeDeleteDialogShowing.value = true -// ActionDialogBox( -// isDialogShowing = isSwipeDeleteDialogShowing, -// title = "Delete Task?", -// message = "Do you want to delete this task?", -// confirmButtonText = "Delete", -// dismissButtonText = "Cancel", -// onConfirmClick = { -// todoViewModel.deleteTodo(currentItem) -// }, -// onDismissClick = { -// isSwipeDeleteDialogShowing.value = false -// coroutineScope.launch { -// dismissState.reset() -// snackbarHostState. -// showSnackbar( -// "Operation Cancelled", -// actionLabel = "OK" -// ) -// } -// }, -// confirmButtonColor = Color(0xFFF75F5F), -// confirmButtonContentColor = Color.White -// ) - LaunchedEffect(Unit){ - val result = snackbarHostState.showSnackbar( - message = "Todo Deleted", - actionLabel = "Undo", - duration = SnackbarDuration.Short - ) - when(result){ - SnackbarResult.Dismissed -> { - todoViewModel.deleteTodo(currentItem) - } - SnackbarResult.ActionPerformed -> { + isSwipeDeleteDialogShowing.value = true + ActionDialogBox( + isDialogShowing = isSwipeDeleteDialogShowing, + title = "Delete Task?", + message = "Do you want to delete this task?", + confirmButtonText = "Delete", + dismissButtonText = "Cancel", + onConfirmClick = { + todoViewModel.deleteTodo(currentItem) + }, + onDismissClick = { + isSwipeDeleteDialogShowing.value = false + coroutineScope.launch { dismissState.reset() } - } - } + }, + confirmButtonColor = Color(0xFFF75F5F), + confirmButtonContentColor = Color.White + ) +// LaunchedEffect(Unit){ +// val result = snackbarHostState.showSnackbar( +// message = "Task will be deleted soon!", +// actionLabel = "Undo", +// duration = SnackbarDuration.Short +// ) +// when(result){ +// SnackbarResult.Dismissed -> { +// todoViewModel.deleteTodo(currentItem) +// } +// SnackbarResult.ActionPerformed -> { +// dismissState.reset() +// } +// } +// } } if(dismissState.isDismissed(direction = DismissDirection.StartToEnd)){ @@ -158,7 +150,6 @@ fun TaskList( } }else{ // cancel notification - println("Current Status completed:${currentItem.isCompleted}") cancelNotification(context,currentItem) } } From 380bdcb2ff559066f7ba5afe6b48e15ceb07c5a3 Mon Sep 17 00:00:00 2001 From: thatsmanmeet <120583077+thatsmanmeet@users.noreply.github.com> Date: Fri, 1 Dec 2023 11:05:31 +0530 Subject: [PATCH 15/15] little design changes --- .../taskyapp/components/SearchBarTop.kt | 28 ++++--------------- .../com/thatsmanmeet/taskyapp/screens/App.kt | 2 +- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/components/SearchBarTop.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/components/SearchBarTop.kt index 402a879..258b87d 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/components/SearchBarTop.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/components/SearchBarTop.kt @@ -1,17 +1,8 @@ package com.thatsmanmeet.taskyapp.components -import android.annotation.SuppressLint -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.expandHorizontally -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.animation.shrinkHorizontally -import androidx.compose.animation.slideInHorizontally -import androidx.compose.animation.slideOutHorizontally -import androidx.compose.foundation.background + import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -20,10 +11,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Clear import androidx.compose.material.icons.filled.Search -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.SearchBar import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.runtime.Composable @@ -38,18 +27,13 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.focus.onFocusChanged -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalWindowInfo -import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.style.TextMotion import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.thatsmanmeet.taskyapp.R @Composable fun SearchBarTop(searchText: String, onValueChange: (String)->Unit) { @@ -80,16 +64,16 @@ fun SearchBarTop(searchText: String, onValueChange: (String)->Unit) { value = searchText,//searchText , onValueChange = onValueChange, modifier = Modifier - .fillMaxWidth() + .fillMaxWidth(1f) .align(Alignment.CenterVertically) - .padding(top = 10.dp, bottom = 10.dp) + .padding(top = 5.dp, bottom = 5.dp) .focusRequester(focusRequester), textStyle = TextStyle( textMotion = TextMotion.Animated, fontFamily = FontFamily.Monospace, fontSize = 18.sp ), - shape = RoundedCornerShape(20.dp), + shape = RoundedCornerShape(30.dp), leadingIcon = { Icon( Icons.Default.Search, @@ -125,8 +109,8 @@ fun SearchBarTop(searchText: String, onValueChange: (String)->Unit) { } } } -@Preview +@Preview(showBackground = true) @Composable fun SearchBarTopPreview() { - SearchBarTop(" ", {}) + SearchBarTop(" ") {} } diff --git a/app/src/main/java/com/thatsmanmeet/taskyapp/screens/App.kt b/app/src/main/java/com/thatsmanmeet/taskyapp/screens/App.kt index 7688878..aff9679 100644 --- a/app/src/main/java/com/thatsmanmeet/taskyapp/screens/App.kt +++ b/app/src/main/java/com/thatsmanmeet/taskyapp/screens/App.kt @@ -123,7 +123,7 @@ fun MyApp( fontSize = 25.sp ) SearchBarTop(searchText) { searchText = it } - Spacer(modifier = modifier.width(10.dp)) + Spacer(modifier = modifier.width(0.dp)) IconButton(onClick = { // Implement Navigation to settings navHostController.navigate(route = Screen.SettingsScreen.route)