Skip to content

Commit 81fd1e4

Browse files
committed
v0.2.0
- Can Delete Note on long press in main screen - Added note time created at, and last updated at - Added Note Info such as number of words, characters
1 parent 2d9662b commit 81fd1e4

9 files changed

Lines changed: 236 additions & 32 deletions

File tree

.idea/gradle.xml

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/inspectionProfiles/Project_Default.xml

Lines changed: 41 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/src/main/java/com/manandhiman/quicknote/database/AppDatabase.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import androidx.room.Database
44
import androidx.room.RoomDatabase
55
import com.manandhiman.quicknote.model.Note
66

7-
@Database(entities = [Note::class], version = 1)
7+
@Database(entities = [Note::class], version = 2)
88
abstract class AppDatabase: RoomDatabase() {
99
abstract fun noteDao(): NoteDao
1010
}

app/src/main/java/com/manandhiman/quicknote/database/NoteDao.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ interface NoteDao {
1818
fun createNewNote(note: Note)
1919

2020
// manual update query to prevent conflict with create new note
21-
@Query("UPDATE notes SET note_content = :newBody, note_title=:newTitle WHERE note_id=:id")
22-
fun updateNote(id: Int, newTitle: String, newBody: String)
21+
@Query("UPDATE notes SET note_content = :newBody, note_title=:newTitle, note_updated_at=:updatedAt WHERE note_id=:id")
22+
fun updateNote(id: Int, newTitle: String, newBody: String, updatedAt: Long)
2323

2424
@Delete
2525
fun deleteNote(note: Note)

app/src/main/java/com/manandhiman/quicknote/model/Note.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@ data class Note(
1010
@ColumnInfo(name="note_id")
1111
val id: Int = 0,
1212
@ColumnInfo(name="note_title")
13-
val title: String = "New Note",
13+
val title: String = "New Note Title",
1414
@ColumnInfo(name="note_content")
15-
val note: String = "Enter Note Here"
15+
val content: String = "Note the details here...",
16+
@ColumnInfo(name="note_created_at")
17+
val createdAt: Long = System.currentTimeMillis(),
18+
@ColumnInfo(name="note_updated_at")
19+
val updatedAt: Long = System.currentTimeMillis(),
1620
)
1721

1822
val note = Note(0, "This is title", "This is body")

app/src/main/java/com/manandhiman/quicknote/screen/MainScreen.kt

Lines changed: 79 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,33 @@
11
package com.manandhiman.quicknote.screen
22

3-
import androidx.compose.foundation.clickable
3+
import androidx.compose.foundation.ExperimentalFoundationApi
4+
import androidx.compose.foundation.combinedClickable
5+
import androidx.compose.foundation.layout.Arrangement
46
import androidx.compose.foundation.layout.Column
57
import androidx.compose.foundation.layout.Spacer
68
import androidx.compose.foundation.layout.fillMaxSize
9+
import androidx.compose.foundation.layout.fillMaxWidth
710
import androidx.compose.foundation.layout.height
811
import androidx.compose.foundation.layout.padding
912
import androidx.compose.foundation.layout.size
1013
import androidx.compose.foundation.lazy.LazyColumn
1114
import androidx.compose.material.icons.Icons
1215
import androidx.compose.material.icons.filled.Add
16+
import androidx.compose.material3.AlertDialog
1317
import androidx.compose.material3.Button
1418
import androidx.compose.material3.ButtonDefaults
1519
import androidx.compose.material3.Divider
1620
import androidx.compose.material3.Icon
1721
import androidx.compose.material3.Scaffold
1822
import androidx.compose.material3.Text
1923
import androidx.compose.runtime.Composable
24+
import androidx.compose.runtime.MutableState
25+
import androidx.compose.runtime.mutableStateOf
26+
import androidx.compose.runtime.remember
27+
import androidx.compose.ui.Alignment
2028
import androidx.compose.ui.Modifier
29+
import androidx.compose.ui.graphics.Color
30+
import androidx.compose.ui.text.style.TextOverflow
2131
import androidx.compose.ui.tooling.preview.Preview
2232
import androidx.compose.ui.unit.dp
2333
import androidx.compose.ui.unit.sp
@@ -49,29 +59,87 @@ fun MainScreen(
4959
) {
5060
val x = it
5161

52-
LazyColumn(
53-
Modifier
54-
.fillMaxSize()
55-
.padding(16.dp)
56-
) {
5762

58-
items(viewModel.notes.value.size) { index ->
59-
PostUI(viewModel.notes.value[index], index, navController)
63+
if(viewModel.notes.value.isEmpty()) {
64+
Column(
65+
Modifier.fillMaxSize().padding(32.dp),
66+
horizontalAlignment = Alignment.CenterHorizontally,
67+
verticalArrangement = Arrangement.Center
68+
) {
69+
Text(text = "No Notes Added. Use the add new (+) button to create a new note.",
70+
fontSize = 28.sp, lineHeight = 36.sp)
6071
}
72+
6173
}
74+
else {
75+
LazyColumn(
76+
Modifier
77+
.fillMaxSize()
78+
.padding(16.dp)
79+
) {
80+
81+
items(viewModel.notes.value.size) { index ->
82+
PostUI(viewModel.notes.value[index], index, navController, viewModel::deleteNote)
83+
}
84+
}
85+
}
86+
87+
6288
}
6389
}
6490

91+
@OptIn(ExperimentalFoundationApi::class)
6592
@Composable
66-
fun PostUI(note: Note, index: Int, navController: NavHostController) {
93+
fun PostUI(note: Note, index: Int, navController: NavHostController, deleteNote: (Note) -> Unit) {
94+
95+
val deleteNoteConfirmDialog = remember{ mutableStateOf(false) }
96+
6797
Column(
68-
Modifier.clickable { navController.navigate("note/$index") } // clicking item takes to note detail
98+
Modifier
99+
.combinedClickable(
100+
onClick = { navController.navigate("note/$index") }, // clicking item takes to note detail
101+
onLongClick = { deleteNoteConfirmDialog.value = true } // long click for delete
102+
)
69103
) {
70-
Text(text = note.title, fontSize = 24.sp)
104+
Text(text = note.title, fontSize = 24.sp, maxLines = 3, overflow = TextOverflow.Ellipsis)
71105
Divider()
72106
Spacer(modifier = Modifier.height(8.dp))
73107
}
74108

109+
if(deleteNoteConfirmDialog.value) deleteNotDialog(deleteNoteConfirmDialog, note, deleteNote)
110+
111+
}
112+
113+
@Composable
114+
private fun deleteNotDialog(
115+
deleteNoteConfirmDialog: MutableState<Boolean>,
116+
note: Note,
117+
deleteNote: (Note) -> Unit
118+
) {
119+
AlertDialog(
120+
onDismissRequest = { deleteNoteConfirmDialog.value = false },
121+
title = { Text(text = "Delete Note?") },
122+
text = {
123+
Text(
124+
"Are you sure you want to delete note:\n${note.title}",
125+
maxLines = 2,
126+
overflow = TextOverflow.Ellipsis
127+
)
128+
},
129+
confirmButton = {
130+
Button(
131+
onClick = { deleteNote(note) },
132+
colors = ButtonDefaults.buttonColors(
133+
containerColor = Color.Red
134+
)
135+
) { Text("Yes, Delete") }
136+
},
137+
dismissButton = {
138+
Button(
139+
onClick = { deleteNoteConfirmDialog.value = false }
140+
) { Text("No, don't delete") }
141+
}
142+
)
75143
}
76144

77145
@Preview(showBackground = true)

app/src/main/java/com/manandhiman/quicknote/screen/NoteScreen.kt

Lines changed: 98 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
11
package com.manandhiman.quicknote.screen
22

3+
import android.content.Context
34
import android.widget.Toast
45
import androidx.compose.foundation.background
56
import androidx.compose.foundation.clickable
67
import androidx.compose.foundation.layout.Arrangement
78
import androidx.compose.foundation.layout.Column
89
import androidx.compose.foundation.layout.Row
10+
import androidx.compose.foundation.layout.Spacer
11+
import androidx.compose.foundation.layout.fillMaxSize
912
import androidx.compose.foundation.layout.fillMaxWidth
13+
import androidx.compose.foundation.layout.height
1014
import androidx.compose.foundation.layout.padding
15+
import androidx.compose.foundation.layout.width
1116
import androidx.compose.material.icons.Icons
1217
import androidx.compose.material.icons.filled.ArrowBack
1318
import androidx.compose.material.icons.filled.Done
19+
import androidx.compose.material.icons.filled.Info
20+
import androidx.compose.material3.AlertDialog
21+
import androidx.compose.material3.Button
1422
import androidx.compose.material3.Icon
1523
import androidx.compose.material3.Scaffold
24+
import androidx.compose.material3.Text
1625
import androidx.compose.material3.TextField
1726
import androidx.compose.runtime.Composable
27+
import androidx.compose.runtime.MutableState
1828
import androidx.compose.runtime.getValue
1929
import androidx.compose.runtime.mutableStateOf
2030
import androidx.compose.runtime.remember
@@ -29,6 +39,8 @@ import androidx.navigation.NavHostController
2939
import androidx.navigation.compose.rememberNavController
3040
import com.manandhiman.quicknote.model.Note
3141
import com.manandhiman.quicknote.viewmodel.MainViewModel
42+
import java.text.SimpleDateFormat
43+
import java.util.Date
3244

3345
@Preview(showBackground = true)
3446
@Composable
@@ -47,18 +59,31 @@ fun NoteScreen(navController: NavHostController, viewModel: MainViewModel, index
4759
}
4860

4961
var noteTitle by remember { mutableStateOf(note.title) }
50-
var noteBody by remember { mutableStateOf(note.note) }
51-
62+
var noteBody by remember { mutableStateOf(note.content) }
5263

5364
Scaffold(
5465
topBar = { TopAppBar(index, viewModel, noteTitle, noteBody, navController, note) } // top bar for back and save button
5566
) {
5667
Column(
5768
Modifier
5869
.fillMaxWidth()
59-
.padding(top = it.calculateTopPadding())) {
60-
TextField(value = noteTitle, onValueChange = { title -> noteTitle = title})
61-
TextField(value = noteBody, onValueChange = {body -> noteBody = body})
70+
.padding(top = it.calculateTopPadding() + 16.dp, start = 16.dp, end = 16.dp, bottom = 32.dp)
71+
// .verticalScroll(rememberScrollState())
72+
,
73+
verticalArrangement = Arrangement.Center,
74+
75+
) {
76+
77+
TextField(value = noteTitle, onValueChange = { title -> noteTitle = title}, modifier = Modifier.fillMaxWidth())
78+
79+
Spacer(modifier = Modifier.height(32.dp))
80+
81+
TextField(
82+
value = noteBody,
83+
onValueChange = {body -> noteBody = body},
84+
modifier = Modifier.fillMaxSize(),
85+
86+
)
6287
}
6388
}
6489

@@ -82,16 +107,75 @@ fun TopAppBar(
82107
{
83108

84109
val context = LocalContext.current // used to display toast
110+
val openInfoDialog = remember { mutableStateOf(false) }
85111

86112
Icon(imageVector = Icons.Default.ArrowBack, contentDescription = "Go Back", Modifier.clickable { navController.popBackStack() })
87-
Icon(imageVector = Icons.Default.Done, contentDescription = "Save Note", Modifier.clickable {
88-
if(index == -1) {
89-
viewModel.addNote(Note(title = noteTitle, note = noteBody))
90-
Toast.makeText(context, "Note Created", Toast.LENGTH_SHORT).show()
91-
} else {
92-
viewModel.updateNote(Note(note.id, noteTitle, noteBody))
93-
Toast.makeText(context, "Note Updated", Toast.LENGTH_SHORT).show()
94-
}
95-
})
113+
Row {
114+
Icon(imageVector = Icons.Default.Info, contentDescription = "Note Information", Modifier.clickable { openInfoDialog.value = true })
115+
Spacer(modifier = Modifier.width(4.dp))
116+
Icon(imageVector = Icons.Default.Done, contentDescription = "Save Note", Modifier.clickable {
117+
doneNoteAction(index, viewModel, noteTitle, noteBody, context, navController, note)
118+
})
119+
120+
}
121+
122+
if(openInfoDialog.value) NoteInfoDialog(openInfoDialog, noteBody, note.createdAt, note.updatedAt)
123+
96124
}
125+
}
126+
127+
private fun doneNoteAction(
128+
index: Int,
129+
viewModel: MainViewModel,
130+
noteTitle: String,
131+
noteBody: String,
132+
context: Context,
133+
navController: NavHostController,
134+
note: Note
135+
) {
136+
if (index == -1) {
137+
viewModel.addNote(Note(title = noteTitle, content = noteBody))
138+
Toast.makeText(context, "Note Created", Toast.LENGTH_SHORT).show()
139+
navController.popBackStack()
140+
} else {
141+
viewModel.updateNote(Note(note.id, noteTitle, noteBody))
142+
Toast.makeText(context, "Note Updated", Toast.LENGTH_SHORT).show()
143+
navController.popBackStack()
144+
}
145+
}
146+
147+
@Composable
148+
fun NoteInfoDialog(
149+
openDialog: MutableState<Boolean>,
150+
noteBody: String,
151+
createdAt: Long,
152+
updatedAt: Long
153+
) {
154+
155+
val formattedCreatedAtTime = SimpleDateFormat("dd/MM/yyyy h:mm a")
156+
.format(Date(createdAt))
157+
val formattedLastUpdatedAtTime = SimpleDateFormat("dd/MM/yyyy h:mm a")
158+
.format(Date(updatedAt))
159+
160+
AlertDialog(
161+
onDismissRequest = { openDialog.value = false },
162+
title = { Text(text = "Note Details") },
163+
text = {
164+
Text("Characters: ${noteBody.length}" +
165+
"\nWords: ${noteBody.split(" ").size}" +
166+
"\nCreated At: $formattedCreatedAtTime" +
167+
"\nLast Modified: $formattedLastUpdatedAtTime"
168+
)
169+
},
170+
confirmButton = {
171+
Button(
172+
onClick = { openDialog.value = false }
173+
) { Text("Ok") }
174+
},
175+
// dismissButton = {
176+
// Button(
177+
// onClick = { openDialog.value = false }
178+
// ) { Text("Dismiss Button") }
179+
// }
180+
)
97181
}

0 commit comments

Comments
 (0)