Skip to content

Commit

Permalink
Merge pull request #115 from SecUSo/task-notification
Browse files Browse the repository at this point in the history
Refactored reminder service to job for JobScheduler
  • Loading branch information
0xC0FFEEC0DE authored Apr 12, 2024
2 parents 0b33ab6 + f70b0ff commit c6921eb
Show file tree
Hide file tree
Showing 44 changed files with 799 additions and 628 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ style="float: right; margin-right: 10px;" />

This application allows to create lists of tasks which can be individually managed. Each list contains a set of tasks. Each task can have a deadline, a reminder time and a list of subtasks. By setting a reminder time the user will be informed by a notification. It is possible to display all tasks in a calendar, to protect the app with a secret pin and to sort and prioritze tasks within a list. Colors indicate the urgency of a task (taking the deadline into account).

The application requires the permission "RECEIVE_ON_BOOT_COMPLETE" in order to start a background service which triggers notifications.
The application requires the permission "RECEIVE_BOOT_COMPLETED" in order to start a background service which triggers notifications.

This app belongs to the group of Privacy Friendly Apps developed by the Karlsruhe Institute of Technology.

Expand Down
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ android {

defaultConfig {
applicationId "org.secuso.privacyfriendlytodolist"
minSdkVersion 19
minSdkVersion 21
targetSdkVersion 34
compileSdk 34
versionCode 9
Expand Down
100 changes: 55 additions & 45 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="org.secuso.privacyfriendlytodolist.TODO_PERMISSION"/>
<uses-permission android:name="org.secuso.privacyfriendlyproductivitytimer.TODO_RE_PERMISSION"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="org.secuso.privacyfriendlytodolist.TODO_PERMISSION" />
<uses-permission android:name="org.secuso.privacyfriendlyproductivitytimer.TODO_RE_PERMISSION" />

<!-- Permission for TodoReceiver: -->
<permission android:name="org.secuso.privacyfriendlytodolist.TODO_PERMISSION"
<permission
android:name="org.secuso.privacyfriendlytodolist.TODO_PERMISSION"
android:protectionLevel="signature"
android:label="@string/permission_todo_name"
android:description="@string/permission_todo_description" />
Expand All @@ -28,105 +31,112 @@
android:supportsRtl="true"
android:name=".PFAApplication"
android:theme="@style/AppTheme">

<activity
android:name=".view.SplashActivity"
android:theme="@style/SplashTheme"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".view.TutorialActivity"
android:parentActivityName="view.MainActivity"
android:theme="@style/AppTheme.NoActionBar">
</activity>
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".view.PinActivity"
android:label="@string/enter_pin"
android:autoRemoveFromRecents="true"
android:launchMode="singleTop"
android:exported="true"/>
android:exported="true" />
<activity
android:name=".view.MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
</activity>
<activity
android:name=".view.AboutActivity"
android:label="@string/about" />
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".view.RecyclerActivity"
android:label="@string/bin" />
<activity
android:name=".view.HelpActivity"
android:label="@string/help" />
<activity
android:name=".view.AboutActivity"
android:label="@string/about" />
<activity
android:name=".view.calendar.CalendarActivity"
android:label="@string/calendar" />
<activity
android:name=".view.calendar.CalendarPopup"
android:label="@string/app_name"/>
<activity
android:name=".view.HelpActivity"
android:label="@string/help" />
android:name=".view.calendar.CalendarPopup" />
<activity
android:name=".view.Settings"
android:label="@string/settings" />
<service
android:name=".view.widget.ListViewWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS"
/>
<activity
android:name=".view.widget.TodoListWidgetConfigureActivity"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>

<service android:name=".service.ReminderService" />
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />

<receiver android:name=".service.AutoStartReceiver"
<receiver
android:name=".receiver.AutoStartReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".view.widget.TodoListWidget"
<receiver
android:name=".receiver.AlarmReceiver"
android:exported="false">
<intent-filter>
<action android:name="org.secuso.privacyfriendlytodolist.ALARM" />
</intent-filter>
</receiver>
<receiver
android:name=".view.widget.TodoListWidget"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>

<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/todo_list_widget_info" />
</receiver>

<activity android:name=".view.widget.TodoListWidgetConfigureActivity"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>


<receiver android:name="org.secuso.privacyfriendlytodolist.receiver.TodoReReceiver"
<receiver
android:name="org.secuso.privacyfriendlytodolist.receiver.TodoReReceiver"
android:exported="true"
android:permission="org.secuso.privacyfriendlyproductivitytimer.TODO_RE_PERMISSION">
<intent-filter>
<action android:name="org.secuso.privacyfriendlyproductivitytimer.TODO_RE_ACTION" />
</intent-filter>
</receiver>

<service
android:name=".service.AutoStartJob"
android:permission="android.permission.BIND_JOB_SERVICE" />
<service
android:name=".service.AlarmJob"
android:permission="android.permission.BIND_JOB_SERVICE" />
<service
android:name=".view.widget.ListViewWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<service
android:name=".backup.PFABackupService"
android:enabled="true"
android:exported="true"
android:process=":backup"
tools:ignore="ExportedService">
<intent-filter>
<action
android:name="org.secuso.privacyfriendlybackup.api.pfa.PFAAuthService" />
<action android:name="org.secuso.privacyfriendlybackup.api.pfa.PFAAuthService" />
</intent-filter>
</service>
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />

</application>

</manifest>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ import org.secuso.privacyfriendlytodolist.backup.BackupRestorer

class PFAApplication : Application(), Configuration.Provider {

override val workManagerConfiguration = Configuration.Builder().setMinimumLoggingLevel(Log.INFO).build()

override fun onCreate() {
super.onCreate()
BackupManager.backupCreator = BackupCreator()
BackupManager.backupRestorer = BackupRestorer()
}

override val workManagerConfiguration = Configuration.Builder().setMinimumLoggingLevel(Log.INFO).build()
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ import org.secuso.privacyfriendlybackup.api.backup.PreferenceUtil.writePreferenc
import org.secuso.privacyfriendlybackup.api.pfa.IBackupCreator
import org.secuso.privacyfriendlytodolist.model.database.TodoListDatabase
import org.secuso.privacyfriendlytodolist.util.PinUtil.hasPin
import org.secuso.privacyfriendlytodolist.util.PrefDataType
import org.secuso.privacyfriendlytodolist.util.PrefManager
import org.secuso.privacyfriendlytodolist.util.PreferenceMgr
import org.secuso.privacyfriendlytodolist.view.PinActivity
import java.io.OutputStream
import java.io.OutputStreamWriter
Expand Down Expand Up @@ -92,7 +91,7 @@ class BackupCreator : IBackupCreator {
val pref = PreferenceManager.getDefaultSharedPreferences(context)
val excludedPreferences = ArrayList<String>()
for (name in pref.all.keys) {
val prefMetaData = PrefManager.ALL_PREFERENCES[name]
val prefMetaData = PreferenceMgr.ALL_PREFERENCES[name]
?: throw RuntimeException("Unknown preference $name")
if (prefMetaData.excludeFromBackup) {
excludedPreferences.add(name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import org.secuso.privacyfriendlybackup.api.backup.FileUtil.copyFile
import org.secuso.privacyfriendlybackup.api.pfa.IBackupRestorer
import org.secuso.privacyfriendlytodolist.model.database.TodoListDatabase
import org.secuso.privacyfriendlytodolist.util.PrefDataType
import org.secuso.privacyfriendlytodolist.util.PrefManager
import org.secuso.privacyfriendlytodolist.util.PreferenceMgr
import java.io.File
import java.io.IOException
import java.io.InputStream
Expand Down Expand Up @@ -85,7 +85,7 @@ class BackupRestorer : IBackupRestorer {
val pref = PreferenceManager.getDefaultSharedPreferences(context).edit()
while (reader.hasNext()) {
val name = reader.nextName()
val prefMetaData = PrefManager.ALL_PREFERENCES[name]
val prefMetaData = PreferenceMgr.ALL_PREFERENCES[name]
?: throw RuntimeException("Unknown preference $name")
when (prefMetaData.dataType) {
PrefDataType.BOOLEAN -> pref.putBoolean(name, reader.nextBoolean())
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,21 @@ package org.secuso.privacyfriendlytodolist.model
*/
interface ModelServices {
fun getTaskById(todoTaskId: Int, resultConsumer: ResultConsumer<TodoTask?>)
fun getNextDueTask(today: Long, resultConsumer: ResultConsumer<TodoTask?>)
fun getNextDueTask(now: Long, resultConsumer: ResultConsumer<TodoTask?>)

/**
* returns a list of tasks
* Returns a list of tasks
*
* - which are not fulfilled and whose reminder time is prior to the current time
* - the task which is next due
*
* @param now Current time.
* @param lockedIds Tasks for which the user was just notified (these tasks are locked).
* They will be excluded from the result.
* @param resultConsumer Result consumer that will be notified when the asynchronous database
* access has finished.
*/
fun getTasksToRemind(today: Long, lockedIds: Set<Int>?, resultConsumer: ResultConsumer<MutableList<TodoTask>>)
fun getTasksToRemind(now: Long, lockedIds: Set<Int>?, resultConsumer: ResultConsumer<MutableList<TodoTask>>)
fun deleteTodoList(todoListId: Int, resultConsumer: ResultConsumer<Int>?)
fun deleteTodoTask(todoTask: TodoTask, resultConsumer: ResultConsumer<Int>?)
fun deleteTodoSubtask(subtask: TodoSubtask, resultConsumer: ResultConsumer<Int>?)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ interface TodoSubtaskDao {
suspend fun delete(todoSubtaskData: TodoSubtaskData): Int
@Query("SELECT * FROM todoSubtasks WHERE taskId = :taskId")
suspend fun getAllOfTask(taskId: Int): Array<TodoSubtaskData>
@Query("SELECT * FROM todoSubtasks WHERE taskId = :taskId AND isInRecycleBin = 0")
@Query("SELECT * FROM todoSubtasks WHERE isInRecycleBin = 0 AND taskId = :taskId")
suspend fun getAllOfTaskNotInRecycleBin(taskId: Int): Array<TodoSubtaskData>
@Query("DELETE FROM todoSubtasks")
suspend fun deleteAll(): Int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ interface TodoTaskDao {
suspend fun getCountNotInRecycleBin(): Int
@Query("SELECT * FROM todoTasks WHERE id = :todoTaskId LIMIT 1")
suspend fun getById(todoTaskId: Int): TodoTaskData?
@Query("SELECT * FROM todoTasks WHERE isDone = 0 AND reminderTime > 0 AND isInRecycleBin = 0 AND reminderTime - :today > 0 ORDER BY ABS(reminderTime - :today) LIMIT 1")
suspend fun getNextDueTask(today: Long): TodoTaskData?
@Query("SELECT * FROM todoTasks WHERE isDone = 0 AND isInRecycleBin = 0 AND reminderTime > 0 AND reminderTime <= :today AND id NOT IN (:lockedIds)")
suspend fun getAllToRemind(today: Long, lockedIds: Set<Int>?): Array<TodoTaskData>
@Query("SELECT * FROM todoTasks WHERE isInRecycleBin = 0 AND isDone = 0 AND reminderTime > 0 AND reminderTime > :now ORDER BY ABS(reminderTime - :now) LIMIT 1")
suspend fun getNextDueTask(now: Long): TodoTaskData?
@Query("SELECT * FROM todoTasks WHERE isInRecycleBin = 0 AND isDone = 0 AND reminderTime > 0 AND reminderTime <= :now AND id NOT IN (:lockedIds)")
suspend fun getAllToRemind(now: Long, lockedIds: Set<Int>?): Array<TodoTaskData>
@Query("SELECT * FROM todoTasks WHERE isInRecycleBin = 0")
suspend fun getAllNotInRecycleBin(): Array<TodoTaskData>
@Query("SELECT * FROM todoTasks WHERE isInRecycleBin <> 0")
suspend fun getAllInRecycleBin(): Array<TodoTaskData>
@Query("SELECT * FROM todoTasks WHERE listId = :listId AND isInRecycleBin = 0")
@Query("SELECT * FROM todoTasks WHERE isInRecycleBin = 0 AND listId = :listId")
suspend fun getAllOfListNotInRecycleBin(listId: Int): Array<TodoTaskData>
@Query("UPDATE todoTasks SET name = :name, progress = :progress, isDone = :isDone WHERE id = :id")
suspend fun updateValuesFromPomodoro(id: Int, name: String, progress: Int, isDone: Boolean): Int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,39 +58,29 @@ class ModelServicesImpl(
}
}

override fun getNextDueTask(today: Long, resultConsumer: ResultConsumer<TodoTask?>) {
override fun getNextDueTask(now: Long, resultConsumer: ResultConsumer<TodoTask?>) {
coroutineScope.launch(Dispatchers.IO) {
val data = getNextDueTaskBlocking(today)
val data = getNextDueTaskBlocking(now)
dispatchResult(resultConsumer, data)
}
}

private suspend fun getNextDueTaskBlocking(today: Long): TodoTask? {
val nextDueTaskData = db.getTodoTaskDao().getNextDueTask(today)
private suspend fun getNextDueTaskBlocking(now: Long): TodoTask? {
val nextDueTaskData = db.getTodoTaskDao().getNextDueTask(now)
var nextDueTask: TodoTask? = null
if (null != nextDueTaskData) {
nextDueTask = loadTasksSubtasks(false, nextDueTaskData)[0]
}
return nextDueTask
}

/**
* returns a list of tasks
*
* - which are not fulfilled and whose reminder time is prior to the current time
* - the task which is next due
*
* @param today Date of today.
* @param lockedIds Tasks for which the user was just notified (these tasks are locked).
* They will be excluded.
*/
override fun getTasksToRemind(today: Long, lockedIds: Set<Int>?, resultConsumer: ResultConsumer<MutableList<TodoTask>>) {
override fun getTasksToRemind(now: Long, lockedIds: Set<Int>?, resultConsumer: ResultConsumer<MutableList<TodoTask>>) {
coroutineScope.launch(Dispatchers.IO) {
val dataArray = db.getTodoTaskDao().getAllToRemind(today, lockedIds)
val dataArray = db.getTodoTaskDao().getAllToRemind(now, lockedIds)
val tasksToRemind = loadTasksSubtasks(false, *dataArray)

// get task that is next due
val nextDueTask = getNextDueTaskBlocking(today)
val nextDueTask = getNextDueTaskBlocking(now)
if (nextDueTask != null) {
tasksToRemind.add(nextDueTask as TodoTaskImpl)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ class TodoListImpl : BaseTodoImpl, TodoList {
return 0
}

override fun toString(): String {
return "'${getName()}' (id ${getId()})"
}

companion object CREATOR : Creator<TodoListImpl> {
override fun createFromParcel(parcel: Parcel): TodoListImpl {
return TodoListImpl(parcel)
Expand Down
Loading

0 comments on commit c6921eb

Please sign in to comment.