From 4c538f262497a254070df74ec8f4030f9daa842c Mon Sep 17 00:00:00 2001 From: Cam Graff Date: Sat, 23 Jul 2022 16:48:19 -0500 Subject: [PATCH] Make notifications and task IDs persistant --- app/src/main/AndroidManifest.xml | 3 + .../simpletask/NotificationService.kt | 93 +++++++++++++++++++ .../nl/mpcjanssen/simpletask/Simpletask.kt | 29 +----- .../mpcjanssen/simpletask/TodoApplication.kt | 20 ++-- .../nl/mpcjanssen/simpletask/dao/TaskIdDao.kt | 36 +++++++ .../nl/mpcjanssen/simpletask/task/Task.kt | 8 +- .../nl/mpcjanssen/simpletask/task/TodoList.kt | 22 ++++- .../nl/mpcjanssen/simpletask/util/Config.kt | 14 ++- 8 files changed, 180 insertions(+), 45 deletions(-) create mode 100644 app/src/main/java/nl/mpcjanssen/simpletask/NotificationService.kt create mode 100644 app/src/main/java/nl/mpcjanssen/simpletask/dao/TaskIdDao.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9dd8b1dbf..eaa477a7e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -203,6 +203,9 @@ You should have received a copy of the GNU General Public License along with Sim + ) { + for (id in taskIds) { + val task = TodoApplication.todoList.getTaskWithId(id) + if (task == null) { + Log.e(TAG, "Task with id '$id' not found in todo list") + continue + } + val taskIdHash = task.id.hashCode() + val editTaskIntent = Intent(this, AddTask::class.java).let { + it.putExtra(Constants.EXTRA_TASK_ID, task.id) + PendingIntent.getActivity(this, taskIdHash, it, PendingIntent.FLAG_IMMUTABLE) + } + val markDoneIntent = Intent(this, MarkTaskDone::class.java).let { + it.putExtra(Constants.EXTRA_TASK_ID, task.id) + PendingIntent.getService(this, taskIdHash, it, PendingIntent.FLAG_IMMUTABLE) + } + var builder = NotificationCompat.Builder(this, "pin-notifications") + .setSmallIcon(R.drawable.ic_done_white_24dp) + .setContentTitle(task.text) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setContentIntent(editTaskIntent) + .addAction(R.drawable.ic_done_white_24dp, getString(R.string.done), markDoneIntent) + .addExtras(Bundle().apply { putString(Constants.EXTRA_TASK_ID, task.id) }) + .setGroup("group") + + with(NotificationManagerCompat.from(this)) { + notify(taskIdHash, builder.build()) + } + if (!TodoApplication.config.hasKeepSelection) { + TodoApplication.todoList.clearSelection() + } + } + } + + public override fun onBind(intent: Intent?): IBinder? { + return null + } + + companion object { + val TAG = "NotificationService" + + fun removeNotifications(taskIds: List) { + taskIds.forEach{ + TodoApplication.notificationManager.cancel(it.hashCode()) + } + } + } +} diff --git a/app/src/main/java/nl/mpcjanssen/simpletask/Simpletask.kt b/app/src/main/java/nl/mpcjanssen/simpletask/Simpletask.kt index 83b0b4338..59e3630b9 100644 --- a/app/src/main/java/nl/mpcjanssen/simpletask/Simpletask.kt +++ b/app/src/main/java/nl/mpcjanssen/simpletask/Simpletask.kt @@ -1120,31 +1120,10 @@ class Simpletask : ThemedNoActionBarActivity() { } private fun pinNotification(checkedTasks: List) { - for (task in checkedTasks) { - val taskIdHash = task.id.hashCode() - val editTaskIntent = Intent(this, AddTask::class.java).let { - it.putExtra(Constants.EXTRA_TASK_ID, task.id) - PendingIntent.getActivity(this, taskIdHash, it, PendingIntent.FLAG_IMMUTABLE) - } - val markDoneIntent = Intent(this, MarkTaskDone::class.java).let { - it.putExtra(Constants.EXTRA_TASK_ID, task.id) - PendingIntent.getService(this, taskIdHash, it, PendingIntent.FLAG_IMMUTABLE) - } - var builder = NotificationCompat.Builder(this, "pin-notifications") - .setSmallIcon(R.drawable.ic_done_white_24dp) - .setContentTitle(task.text) - .setPriority(NotificationCompat.PRIORITY_DEFAULT) - .setContentIntent(editTaskIntent) - .addAction(R.drawable.ic_done_white_24dp, getString(R.string.done), markDoneIntent) - .addExtras(Bundle().apply { putString(Constants.EXTRA_TASK_ID, task.id) }) - - with(NotificationManagerCompat.from(this)) { - notify(taskIdHash, builder.build()) - } - if (!TodoApplication.config.hasKeepSelection) { - TodoApplication.todoList.clearSelection() - } - } + val taskIds = checkedTasks.map { it.id }.toTypedArray() + val intent = Intent(this, NotificationService::class.java) + intent.putExtra(Constants.EXTRA_TASK_ID, taskIds) + startForegroundService(intent) } private inner class UiHandler () { diff --git a/app/src/main/java/nl/mpcjanssen/simpletask/TodoApplication.kt b/app/src/main/java/nl/mpcjanssen/simpletask/TodoApplication.kt index 3a3dc13b1..1cbf18471 100644 --- a/app/src/main/java/nl/mpcjanssen/simpletask/TodoApplication.kt +++ b/app/src/main/java/nl/mpcjanssen/simpletask/TodoApplication.kt @@ -74,6 +74,7 @@ class TodoApplication : Application() { db = Room.databaseBuilder(this, AppDatabase::class.java, DB_FILE).fallbackToDestructiveMigration() .build() + notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager if (config.forceEnglish) { val conf = resources.configuration conf.locale = Locale.ENGLISH @@ -220,15 +221,13 @@ class TodoApplication : Application() { fun updatePinnedNotifications() { Log.i(TAG, "Updating pinned notifications") - val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - notificationManager.getActiveNotifications().forEach { - val taskId = it.notification.extras.getString(Constants.EXTRA_TASK_ID) - if (taskId != null) { - val taskText = TodoApplication.todoList.getTaskWithId(taskId)?.text - val notification = NotificationCompat.Builder(this, it.notification).setContentTitle(taskText).build() - notificationManager.notify(it.id, notification) - } - } + val taskIds = notificationManager.getActiveNotifications().map { it.notification.extras.getString(Constants.EXTRA_TASK_ID) }.filterNotNull().toTypedArray() + val (completedIds, incompleteIds) = taskIds.partition { todoList.getTaskWithId(it)?.isCompleted() ?: false } + NotificationService.removeNotifications(completedIds) + Log.i(TAG, "taskIds: $taskIds") + val intent = Intent(this, NotificationService::class.java) + intent.putExtra(Constants.EXTRA_TASK_ID, incompleteIds.toTypedArray()) + startForegroundService(intent) } fun clearTodoFile() { @@ -268,8 +267,6 @@ class TodoApplication : Application() { description = descriptionText } // Register the channel with the system - val notificationManager: NotificationManager = - getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel) } } @@ -282,6 +279,7 @@ class TodoApplication : Application() { lateinit var config : Config lateinit var todoList: TodoList lateinit var db : AppDatabase + lateinit var notificationManager: NotificationManager } var today: String = todayAsString } diff --git a/app/src/main/java/nl/mpcjanssen/simpletask/dao/TaskIdDao.kt b/app/src/main/java/nl/mpcjanssen/simpletask/dao/TaskIdDao.kt new file mode 100644 index 000000000..260f3ab90 --- /dev/null +++ b/app/src/main/java/nl/mpcjanssen/simpletask/dao/TaskIdDao.kt @@ -0,0 +1,36 @@ +package nl.mpcjanssen.simpletask.dao + +import nl.mpcjanssen.simpletask.TodoApplication +import nl.mpcjanssen.simpletask.task.Task +import android.content.Context.MODE_PRIVATE + +object TaskIdDao { + val sharedPrefs = TodoApplication.app.getSharedPreferences("todolist", MODE_PRIVATE) + val editor = sharedPrefs.edit() + + fun get(taskText: String): String? { + return sharedPrefs.getString(taskText, null) + } + + fun add(tasks: List) { + tasks.forEach { + editor.putString(it.text, it.id) + } + editor.apply() + } + + fun add(task: Task) { + add(listOf(task)) + } + + fun remove(tasks: List) { + tasks.forEach { + editor.remove(it.text) + } + editor.apply() + } + + fun remove(task: Task) { + remove(listOf(task)) + } +} diff --git a/app/src/main/java/nl/mpcjanssen/simpletask/task/Task.kt b/app/src/main/java/nl/mpcjanssen/simpletask/task/Task.kt index 1b9be27bd..1e8232de5 100644 --- a/app/src/main/java/nl/mpcjanssen/simpletask/task/Task.kt +++ b/app/src/main/java/nl/mpcjanssen/simpletask/task/Task.kt @@ -4,7 +4,7 @@ import nl.mpcjanssen.simpletask.util.addInterval import java.util.* import java.util.regex.Pattern -class Task(text: String, defaultPrependedDate: String? = null) { +class Task(text: String, defaultPrependedDate: String? = null, val id: String = UUID.randomUUID().toString()) { var tokens: ArrayList @@ -23,6 +23,10 @@ class Task(text: String, defaultPrependedDate: String? = null) { tokens = parse(rawText) } + fun withId(id: String): Task { + return Task(text, id = id) + } + private inline fun getFirstToken(): T? { tokens.filterIsInstance().forEach { return it @@ -42,8 +46,6 @@ class Task(text: String, defaultPrependedDate: String? = null) { } - var id: String = UUID.randomUUID().toString() - val text: String get() { return tokens.joinToString(" ") { it.text } diff --git a/app/src/main/java/nl/mpcjanssen/simpletask/task/TodoList.kt b/app/src/main/java/nl/mpcjanssen/simpletask/task/TodoList.kt index b9eeac901..f99d838c1 100644 --- a/app/src/main/java/nl/mpcjanssen/simpletask/task/TodoList.kt +++ b/app/src/main/java/nl/mpcjanssen/simpletask/task/TodoList.kt @@ -10,6 +10,7 @@ import nl.mpcjanssen.simpletask.remote.FileStore import nl.mpcjanssen.simpletask.remote.IFileStore import nl.mpcjanssen.simpletask.util.* +import nl.mpcjanssen.simpletask.dao.TaskIdDao import java.io.File import java.util.* import java.util.concurrent.CopyOnWriteArrayList @@ -45,7 +46,7 @@ class TodoList(val config: Config) { } else { todoItems.addAll(0, updatedItems) } - + TaskIdDao.add(items) } fun add(t: Task, atEnd: Boolean) { @@ -57,7 +58,7 @@ class TodoList(val config: Config) { Log.d(tag, "Remove") pendingEdits.removeAll(tasks) todoItems.removeAll(tasks) - + TaskIdDao.remove(tasks) } @@ -160,10 +161,13 @@ class TodoList(val config: Config) { val smallestSize = org.zip(updated) { orgTask, updatedTask -> val idx = todoItems.indexOf(orgTask) if (idx != -1) { - updatedTask.id = orgTask.id - todoItems[idx] = updatedTask + val newTask = updatedTask.withId(orgTask.id) + todoItems[idx] = newTask + TaskIdDao.remove(orgTask) + TaskIdDao.add(newTask) } else { todoItems.add(updatedTask) + TaskIdDao.add(updatedTask) } 1 }.size @@ -263,7 +267,15 @@ class TodoList(val config: Config) { try { val items = FileStore.loadTasksFromFile(file) - val newTodoItems = items.map { Task(it) }.toMutableList() + val newTodoItems = items.map { + val taskId = TaskIdDao.get(it) + if (taskId == null) { + val task = Task(it) + TaskIdDao.add(task) + task + } + else Task(it, id = taskId) + }.toMutableList() synchronized(todoItems) { Log.d(tag, "Fill todolist with ${items.size} items") todoItems = newTodoItems diff --git a/app/src/main/java/nl/mpcjanssen/simpletask/util/Config.kt b/app/src/main/java/nl/mpcjanssen/simpletask/util/Config.kt index f99e91f06..27ca651b3 100644 --- a/app/src/main/java/nl/mpcjanssen/simpletask/util/Config.kt +++ b/app/src/main/java/nl/mpcjanssen/simpletask/util/Config.kt @@ -1,13 +1,16 @@ package nl.mpcjanssen.simpletask.util +import android.content.Context.MODE_PRIVATE import android.os.Build import android.os.Environment +import android.preference.PreferenceManager import android.util.Log import androidx.annotation.RequiresApi import me.smichel.android.KPreferences.Preferences import nl.mpcjanssen.simpletask.* import nl.mpcjanssen.simpletask.remote.FileStore import nl.mpcjanssen.simpletask.task.Task +import nl.mpcjanssen.simpletask.dao.TaskIdDao import org.json.JSONObject import java.io.File import java.util.* @@ -229,7 +232,16 @@ class Config(app: TodoApplication) : Preferences(app) { val lines = it.lines() Log.i(TAG, "Getting ${lines.size} items todoList from cache") ArrayList().apply { - addAll(lines.map { line -> Task(line) }) + addAll(lines.map { line -> + val taskId = TaskIdDao.get(line) + if (taskId == null) { + val task = Task(line) + TaskIdDao.add(task) + task + } else { + Task(line, id = taskId) + } + }) } } set(items) {