Skip to content

Commit f6676ec

Browse files
ericli3690david-allison
authored andcommitted
refactor: prepare BootService for review reminders
GSoC 2025: Review Reminders This is the class that runs when the phone initially starts up. It listens to the system boot-completed intent and was previously used for the old studying notification system. However, it is messy and needs to be refactored. Most of its functionality will be replaced by my code. Here, I'm cleaning up the code I plan to keep and marking the code I plan to eventually delete. - Add missing docstring and proper attribution for file creation to BootService -- I checked who created the file using `git blame` - Use conditionals to gate where new review reminder code will be run - Made it so that old notification code will only run if review reminders are disabled - Renamed `sWasRun` Hungarian notation variable to be `wasRun` - Used Intent constant instead of hard-coding `BOOT_COMPLETED_INTENT` - Annotated methods that will not be used for the review reminders system (i.e., they can be deleted after August) with `@LegacyNotifications`, a new annotation
1 parent 94bbd5c commit f6676ec

File tree

2 files changed

+88
-13
lines changed

2 files changed

+88
-13
lines changed

AnkiDroid/src/main/java/com/ichi2/anki/services/BootService.kt

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,20 @@
1-
//noinspection MissingCopyrightHeader #8659
1+
/*
2+
* Copyright (c) 2016 Siarhei Krukau <siarhei.krukau@gmail.com>
3+
* Copyright (c) 2025 Eric Li <ericli3690@gmail.com>
4+
*
5+
* This program is free software; you can redistribute it and/or modify it under
6+
* the terms of the GNU General Public License as published by the Free Software
7+
* Foundation; either version 3 of the License, or (at your option) any later
8+
* version.
9+
*
10+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
11+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12+
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License along with
15+
* this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
218
package com.ichi2.anki.services
319

420
import android.app.AlarmManager
@@ -9,48 +25,61 @@ import androidx.core.app.PendingIntentCompat
925
import com.ichi2.anki.CollectionManager
1026
import com.ichi2.anki.IntentHandler.Companion.grantedStoragePermissions
1127
import com.ichi2.anki.R
28+
import com.ichi2.anki.common.annotations.LegacyNotifications
1229
import com.ichi2.anki.common.annotations.NeedsTest
1330
import com.ichi2.anki.common.time.Time
1431
import com.ichi2.anki.common.time.TimeManager
1532
import com.ichi2.anki.libanki.Collection
1633
import com.ichi2.anki.preferences.PENDING_NOTIFICATIONS_ONLY
1734
import com.ichi2.anki.preferences.sharedPrefs
35+
import com.ichi2.anki.settings.Prefs
1836
import com.ichi2.anki.showThemedToast
1937
import timber.log.Timber
2038
import java.util.Calendar
2139

40+
/**
41+
* BroadcastReceiver which listens to the Android system-level intent that fires when the device starts up.
42+
* Schedules notifications for review reminders.
43+
*/
2244
@NeedsTest("Check on various Android versions that this can execute")
2345
class BootService : BroadcastReceiver() {
46+
@LegacyNotifications("Notifications will be scheduled rather than instantly shown on boot or app launch")
2447
private var failedToShowNotifications = false
2548

2649
override fun onReceive(
2750
context: Context,
2851
intent: Intent,
2952
) {
30-
if (!intent.action.equals("android.intent.action.BOOT_COMPLETED")) {
53+
if (!intent.action.equals(Intent.ACTION_BOOT_COMPLETED)) {
3154
Timber.w("BootService - unexpected action received, ignoring: %s", intent.action)
3255
return
3356
}
34-
if (sWasRun) {
57+
if (wasRun) {
3558
Timber.d("BootService - Already run")
3659
return
3760
}
3861
if (!grantedStoragePermissions(context, showToast = false)) {
3962
Timber.w("Boot Service did not execute - no permissions")
4063
return
4164
}
42-
// There are cases where the app is installed, and we have access, but nothing exist yet
43-
val col = getColSafe()
44-
if (col == null) {
45-
Timber.w("Boot Service did not execute - error loading collection")
46-
return
65+
if (Prefs.newReviewRemindersEnabled) {
66+
Timber.i("Executing Boot Service - Review reminders")
67+
// TODO: GSoC 2025: Run schedule all notifications method
68+
} else {
69+
// There are cases where the app is installed, and we have access, but nothing exist yet
70+
val col = getColSafe()
71+
if (col == null) {
72+
Timber.w("Boot Service did not execute - error loading collection")
73+
return
74+
}
75+
Timber.i("Executing Boot Service")
76+
catchAlarmManagerErrors(context) { scheduleNotification(TimeManager.time, context) }
77+
failedToShowNotifications = false
4778
}
48-
Timber.i("Executing Boot Service")
49-
catchAlarmManagerErrors(context) { scheduleNotification(TimeManager.time, context) }
50-
failedToShowNotifications = false
51-
sWasRun = true
79+
wasRun = true
5280
}
5381

82+
@LegacyNotifications("Will be moved to AlarmManagerService")
5483
private fun catchAlarmManagerErrors(
5584
context: Context,
5685
runnable: Runnable,
@@ -75,6 +104,7 @@ class BootService : BroadcastReceiver() {
75104
}
76105
}
77106

107+
@LegacyNotifications("Can use withCol instead")
78108
private fun getColSafe(): Collection? {
79109
// #6239 - previously would crash if ejecting, we don't want a report if this happens so don't use
80110
// getInstance().getColSafe
@@ -94,8 +124,9 @@ class BootService : BroadcastReceiver() {
94124
* This service is also run when the app is started (from [com.ichi2.anki.AnkiDroidApp],
95125
* so we need to make sure that it isn't run twice.
96126
*/
97-
private var sWasRun = false
127+
private var wasRun = false
98128

129+
@LegacyNotifications("Replaced by new review reminder scheduling logic")
99130
fun scheduleNotification(
100131
time: Time,
101132
context: Context,
@@ -137,6 +168,7 @@ class BootService : BroadcastReceiver() {
137168
}
138169

139170
/** Returns the hour of day when rollover to the next day occurs */
171+
@LegacyNotifications("Notifications will be sent at customizable times rather than the beginning of every day")
140172
private fun getRolloverHourOfDay(context: Context): Int {
141173
// TODO; We might want to use the BootService retry code here when called from preferences.
142174
val defValue = 4
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (c) 2025 Eric Li <ericli3690@gmail.com>
3+
*
4+
* This program is free software; you can redistribute it and/or modify it under
5+
* the terms of the GNU General Public License as published by the Free Software
6+
* Foundation; either version 3 of the License, or (at your option) any later
7+
* version.
8+
*
9+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
10+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
11+
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
12+
*
13+
* You should have received a copy of the GNU General Public License along with
14+
* this program. If not, see <http://www.gnu.org/licenses/>.
15+
*/
16+
17+
package com.ichi2.anki.common.annotations
18+
19+
/**
20+
* Indicates that a section of code is part of the legacy notifications system in place before
21+
* August 2025. Code flagged with this annotation is slated to be eventually deleted once the
22+
* review reminders system becomes stable.
23+
*
24+
* Also see all conditional points gated by Prefs.newReviewRemindersEnabled.
25+
*
26+
* Once all occurrences of both this annotation and Prefs.newReviewRemindersEnabled are no longer
27+
* present in the code base, the migration from the legacy notifications system to the new review
28+
* reminders system will be complete.
29+
*/
30+
@Target(
31+
AnnotationTarget.CLASS,
32+
AnnotationTarget.FUNCTION,
33+
AnnotationTarget.VALUE_PARAMETER,
34+
AnnotationTarget.EXPRESSION,
35+
AnnotationTarget.FIELD,
36+
AnnotationTarget.PROPERTY,
37+
)
38+
@Repeatable
39+
@Retention(AnnotationRetention.SOURCE)
40+
@MustBeDocumented
41+
annotation class LegacyNotifications(
42+
val optionalReason: String = "",
43+
)

0 commit comments

Comments
 (0)