Skip to content

Commit b7f395f

Browse files
authored
Add pin to status bar note action (#962)
1 parent 6dcea19 commit b7f395f

48 files changed

Lines changed: 1270 additions & 471 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
{
2+
"formatVersion": 1,
3+
"database": {
4+
"version": 10,
5+
"identityHash": "8cb21a4c0f6224c35dc6038a4feb74d0",
6+
"entities": [
7+
{
8+
"tableName": "BaseNote",
9+
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` TEXT NOT NULL, `folder` TEXT NOT NULL, `color` TEXT NOT NULL, `title` TEXT NOT NULL, `pinned` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `modifiedTimestamp` INTEGER NOT NULL, `labels` TEXT NOT NULL, `body` TEXT NOT NULL, `spans` TEXT NOT NULL, `items` TEXT NOT NULL, `images` TEXT NOT NULL, `files` TEXT NOT NULL, `audios` TEXT NOT NULL, `reminders` TEXT NOT NULL, `viewMode` TEXT NOT NULL, `isPinnedToStatus` INTEGER NOT NULL)",
10+
"fields": [
11+
{
12+
"fieldPath": "id",
13+
"columnName": "id",
14+
"affinity": "INTEGER",
15+
"notNull": true
16+
},
17+
{
18+
"fieldPath": "type",
19+
"columnName": "type",
20+
"affinity": "TEXT",
21+
"notNull": true
22+
},
23+
{
24+
"fieldPath": "folder",
25+
"columnName": "folder",
26+
"affinity": "TEXT",
27+
"notNull": true
28+
},
29+
{
30+
"fieldPath": "color",
31+
"columnName": "color",
32+
"affinity": "TEXT",
33+
"notNull": true
34+
},
35+
{
36+
"fieldPath": "title",
37+
"columnName": "title",
38+
"affinity": "TEXT",
39+
"notNull": true
40+
},
41+
{
42+
"fieldPath": "pinned",
43+
"columnName": "pinned",
44+
"affinity": "INTEGER",
45+
"notNull": true
46+
},
47+
{
48+
"fieldPath": "timestamp",
49+
"columnName": "timestamp",
50+
"affinity": "INTEGER",
51+
"notNull": true
52+
},
53+
{
54+
"fieldPath": "modifiedTimestamp",
55+
"columnName": "modifiedTimestamp",
56+
"affinity": "INTEGER",
57+
"notNull": true
58+
},
59+
{
60+
"fieldPath": "labels",
61+
"columnName": "labels",
62+
"affinity": "TEXT",
63+
"notNull": true
64+
},
65+
{
66+
"fieldPath": "body",
67+
"columnName": "body",
68+
"affinity": "TEXT",
69+
"notNull": true
70+
},
71+
{
72+
"fieldPath": "spans",
73+
"columnName": "spans",
74+
"affinity": "TEXT",
75+
"notNull": true
76+
},
77+
{
78+
"fieldPath": "items",
79+
"columnName": "items",
80+
"affinity": "TEXT",
81+
"notNull": true
82+
},
83+
{
84+
"fieldPath": "images",
85+
"columnName": "images",
86+
"affinity": "TEXT",
87+
"notNull": true
88+
},
89+
{
90+
"fieldPath": "files",
91+
"columnName": "files",
92+
"affinity": "TEXT",
93+
"notNull": true
94+
},
95+
{
96+
"fieldPath": "audios",
97+
"columnName": "audios",
98+
"affinity": "TEXT",
99+
"notNull": true
100+
},
101+
{
102+
"fieldPath": "reminders",
103+
"columnName": "reminders",
104+
"affinity": "TEXT",
105+
"notNull": true
106+
},
107+
{
108+
"fieldPath": "viewMode",
109+
"columnName": "viewMode",
110+
"affinity": "TEXT",
111+
"notNull": true
112+
},
113+
{
114+
"fieldPath": "isPinnedToStatus",
115+
"columnName": "isPinnedToStatus",
116+
"affinity": "INTEGER",
117+
"notNull": true
118+
}
119+
],
120+
"primaryKey": {
121+
"autoGenerate": true,
122+
"columnNames": [
123+
"id"
124+
]
125+
},
126+
"indices": [
127+
{
128+
"name": "index_BaseNote_id_folder_pinned_timestamp_labels",
129+
"unique": false,
130+
"columnNames": [
131+
"id",
132+
"folder",
133+
"pinned",
134+
"timestamp",
135+
"labels"
136+
],
137+
"orders": [],
138+
"createSql": "CREATE INDEX IF NOT EXISTS `index_BaseNote_id_folder_pinned_timestamp_labels` ON `${TABLE_NAME}` (`id`, `folder`, `pinned`, `timestamp`, `labels`)"
139+
}
140+
],
141+
"foreignKeys": []
142+
},
143+
{
144+
"tableName": "Label",
145+
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`value` TEXT NOT NULL, PRIMARY KEY(`value`))",
146+
"fields": [
147+
{
148+
"fieldPath": "value",
149+
"columnName": "value",
150+
"affinity": "TEXT",
151+
"notNull": true
152+
}
153+
],
154+
"primaryKey": {
155+
"autoGenerate": false,
156+
"columnNames": [
157+
"value"
158+
]
159+
},
160+
"indices": [],
161+
"foreignKeys": []
162+
}
163+
],
164+
"views": [],
165+
"setupQueries": [
166+
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
167+
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8cb21a4c0f6224c35dc6038a4feb74d0')"
168+
]
169+
}
170+
}

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@
170170

171171
</receiver>
172172

173-
<receiver android:name=".presentation.activity.note.reminders.ReminderReceiver" android:enabled="true" android:exported="true" >
173+
<receiver android:name=".presentation.activity.note.reminders.ReminderReceiver" android:enabled="true" android:exported="false" >
174174
<intent-filter>
175175
<action android:name="android.intent.action.BOOT_COMPLETED" />
176176
<action android:name="android.app.action.SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED" />

app/src/main/java/com/philkes/notallyx/NotallyXApplication.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import androidx.work.WorkManager
1818
import com.google.android.material.color.DynamicColors
1919
import com.philkes.notallyx.NotallyXApplication.Companion.AUTO_REMOVE_DELETED_NOTES
2020
import com.philkes.notallyx.NotallyXApplication.Companion.TAG
21+
import com.philkes.notallyx.data.NotallyDatabase
2122
import com.philkes.notallyx.presentation.setEnabledSecureFlag
2223
import com.philkes.notallyx.presentation.view.misc.NotNullLiveData
2324
import com.philkes.notallyx.presentation.viewmodel.preference.BiometricLock
@@ -26,6 +27,7 @@ import com.philkes.notallyx.presentation.viewmodel.preference.NotallyXPreference
2627
import com.philkes.notallyx.presentation.viewmodel.preference.Theme
2728
import com.philkes.notallyx.presentation.widget.WidgetProvider
2829
import com.philkes.notallyx.utils.AutoRemoveDeletedNotesWorker
30+
import com.philkes.notallyx.utils.PinnedNotificationManager
2931
import com.philkes.notallyx.utils.backup.AUTO_BACKUP_WORK_NAME
3032
import com.philkes.notallyx.utils.backup.autoBackupOnSave
3133
import com.philkes.notallyx.utils.backup.autoBackupOnSaveFileExists
@@ -67,6 +69,7 @@ class NotallyXApplication : Application(), Application.ActivityLifecycleCallback
6769
} else {
6870
setTheme(R.style.AppTheme)
6971
}
72+
restorePinnedNotifications()
7073
preferences.theme.observeForeverWithPrevious { (oldTheme, theme) ->
7174
when (theme) {
7275
Theme.DARK,
@@ -159,6 +162,20 @@ class NotallyXApplication : Application(), Application.ActivityLifecycleCallback
159162
return folderBefore != folderAfter
160163
}
161164

165+
private fun restorePinnedNotifications() {
166+
runOnIODispatcher {
167+
NotallyDatabase.getDatabase(this@NotallyXApplication, false)
168+
.value
169+
.getBaseNoteDao()
170+
.getAllPinnedToStatusNotes()
171+
.forEach { note ->
172+
if (note.isPinnedToStatus) {
173+
PinnedNotificationManager.notify(this@NotallyXApplication, note)
174+
}
175+
}
176+
}
177+
}
178+
162179
private fun checkUpdatePeriodicBackup(
163180
backupFolderBefore: String?,
164181
backupFolder: String,

app/src/main/java/com/philkes/notallyx/data/NotallyDatabase.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import java.io.File
3333
import net.zetetic.database.sqlcipher.SupportOpenHelperFactory
3434

3535
@TypeConverters(Converters::class)
36-
@Database(entities = [BaseNote::class, Label::class], version = 9)
36+
@Database(entities = [BaseNote::class, Label::class], version = 10)
3737
abstract class NotallyDatabase : RoomDatabase() {
3838

3939
abstract fun getLabelDao(): LabelDao
@@ -161,6 +161,7 @@ abstract class NotallyDatabase : RoomDatabase() {
161161
Migration7,
162162
Migration8,
163163
Migration9,
164+
Migration10,
164165
)
165166
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
166167
System.loadLibrary("sqlcipher")
@@ -300,5 +301,14 @@ abstract class NotallyDatabase : RoomDatabase() {
300301
)
301302
}
302303
}
304+
305+
object Migration10 : Migration(9, 10) {
306+
307+
override fun migrate(db: SupportSQLiteDatabase) {
308+
db.execSQL(
309+
"ALTER TABLE `BaseNote` ADD COLUMN `isPinnedToStatus` INTEGER NOT NULL DEFAULT 0"
310+
)
311+
}
312+
}
303313
}
304314
}

app/src/main/java/com/philkes/notallyx/data/dao/BaseNoteDao.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ interface BaseNoteDao {
122122
@Query("SELECT * FROM BaseNote WHERE folder = 'NOTES' ORDER BY pinned DESC, timestamp DESC")
123123
suspend fun getAllNotes(): List<BaseNote>
124124

125+
@Query(
126+
"SELECT * FROM BaseNote WHERE folder = 'NOTES' AND isPinnedToStatus = 1 ORDER BY timestamp DESC"
127+
)
128+
suspend fun getAllPinnedToStatusNotes(): List<BaseNote>
129+
125130
@Query("SELECT * FROM BaseNote") fun getAllAsync(): LiveData<List<BaseNote>>
126131

127132
@Query("SELECT * FROM BaseNote") fun getAll(): List<BaseNote>
@@ -155,6 +160,11 @@ interface BaseNoteDao {
155160
@Query("SELECT id, reminders FROM BaseNote WHERE reminders IS NOT NULL AND reminders != '[]'")
156161
suspend fun getAllReminders(): List<NoteIdReminder>
157162

163+
@Query(
164+
"SELECT * FROM BaseNote WHERE folder = 'NOTES' AND ((reminders IS NOT NULL AND reminders != '[]') OR isPinnedToStatus == 1)"
165+
)
166+
suspend fun getAllWithRemindersOrPinned(): List<BaseNote>
167+
158168
@Query("SELECT color FROM BaseNote WHERE id = :id ") fun getColorOfNote(id: Long): String?
159169

160170
@Query(
@@ -201,6 +211,12 @@ interface BaseNoteDao {
201211
@Query("UPDATE BaseNote SET pinned = :pinned WHERE id IN (:ids)")
202212
suspend fun updatePinned(ids: LongArray, pinned: Boolean)
203213

214+
@Query("UPDATE BaseNote SET isPinnedToStatus = :isPinnedToStatus WHERE id = :id")
215+
fun updatePinnedToStatus(id: Long, isPinnedToStatus: Boolean)
216+
217+
@Query("UPDATE BaseNote SET isPinnedToStatus = :isPinnedToStatus WHERE id IN (:ids)")
218+
fun updatePinnedToStatus(ids: LongArray, isPinnedToStatus: Boolean)
219+
204220
@Query("UPDATE BaseNote SET labels = :labels WHERE id = :id")
205221
suspend fun updateLabels(id: Long, labels: List<String>)
206222

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.philkes.notallyx.data.dao
2+
3+
import android.content.Context
4+
import com.philkes.notallyx.data.model.Folder
5+
import com.philkes.notallyx.utils.cancelPinAndReminders
6+
import com.philkes.notallyx.utils.pinAndScheduleReminders
7+
8+
suspend fun Context.moveBaseNotes(baseNoteDao: BaseNoteDao, ids: LongArray, folder: Folder) {
9+
// Only reminders of notes in NOTES folder are active
10+
if (folder == Folder.DELETED) {
11+
baseNoteDao.move(ids, folder, System.currentTimeMillis())
12+
} else {
13+
baseNoteDao.move(ids, folder)
14+
}
15+
val notes = baseNoteDao.getByIds(ids)
16+
// Only reminders of notes in NOTES folder are active
17+
when (folder) {
18+
Folder.NOTES -> pinAndScheduleReminders(notes)
19+
else -> cancelPinAndReminders(notes)
20+
}
21+
}

app/src/main/java/com/philkes/notallyx/data/imports/evernote/EvernoteImporter.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ fun EvernoteNote.mapToBaseNote(): BaseNote {
157157
audios = audios,
158158
reminders = mutableListOf(),
159159
NoteViewMode.EDIT,
160+
false,
160161
)
161162
}
162163

app/src/main/java/com/philkes/notallyx/data/imports/google/GoogleKeepImporter.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ class GoogleKeepImporter : ExternalImporter {
164164
audios = audios,
165165
reminders = mutableListOf(),
166166
NoteViewMode.EDIT,
167+
false,
167168
)
168169
}
169170

app/src/main/java/com/philkes/notallyx/data/imports/txt/PlainTextImporter.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class PlainTextImporter : ExternalImporter {
8888
audios = listOf(),
8989
reminders = listOf(),
9090
NoteViewMode.EDIT,
91+
false,
9192
)
9293
)
9394
}

app/src/main/java/com/philkes/notallyx/data/model/BaseNote.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ data class BaseNote(
2626
val audios: List<Audio>,
2727
val reminders: List<Reminder>,
2828
val viewMode: NoteViewMode,
29+
val isPinnedToStatus: Boolean,
2930
) : Item {
3031

3132
companion object {
@@ -55,10 +56,20 @@ data class BaseNote(
5556
if (audios != other.audios) return false
5657
if (reminders != other.reminders) return false
5758
if (viewMode != other.viewMode) return false
59+
if (isPinnedToStatus != other.isPinnedToStatus) return false
5860

5961
return true
6062
}
6163

64+
fun equalContents(other: BaseNote?): Boolean {
65+
if (other == null) return false
66+
return type == other.type &&
67+
title == other.title &&
68+
body == other.body &&
69+
spans == other.spans &&
70+
items == other.items
71+
}
72+
6273
override fun hashCode(): Int {
6374
var result = id.hashCode()
6475
result = 31 * result + type.hashCode()
@@ -76,6 +87,7 @@ data class BaseNote(
7687
result = 31 * result + audios.hashCode()
7788
result = 31 * result + reminders.hashCode()
7889
result = 31 * result + viewMode.hashCode()
90+
result = 31 * result + isPinnedToStatus.hashCode()
7991
return result
8092
}
8193
}

0 commit comments

Comments
 (0)