Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@ app/.cifuzz-corpus/
gradle/gradle-daemon-jvm.properties

# Kotlin
.kotlin
.kotlin
/caveman.json
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ android {
minSdk = 28
multiDexEnabled = true
targetSdk = 36
versionCode = 167
versionName = "1.22.12"
versionCode = 168
versionName = "1.22.13"
base.archivesName = "MedTimer"
// Use this deprecated setting because Android Lint will not pick up androidResources.localeFilters correctly
@Suppress("DEPRECATION")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import android.content.Context
import android.content.Intent
import android.util.Log
import com.futsch1.medtimer.LogTags
import com.futsch1.medtimer.di.ApplicationScope
import com.futsch1.medtimer.reminders.LocationSnoozeProcessor
import com.google.android.gms.location.Geofence
import com.google.android.gms.location.GeofencingEvent
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import javax.inject.Inject

@AndroidEntryPoint
Expand All @@ -17,6 +20,10 @@ class GeofenceBroadcastReceiver : BroadcastReceiver() {
@Inject
lateinit var locationSnoozeProcessor: LocationSnoozeProcessor

@Inject
@ApplicationScope
lateinit var applicationScope: CoroutineScope

override fun onReceive(context: Context, intent: Intent) {
val event = GeofencingEvent.fromIntent(intent) ?: return
handleGeofencingEvent(event)
Expand All @@ -28,7 +35,9 @@ class GeofenceBroadcastReceiver : BroadcastReceiver() {
return
}
if (event.geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER || event.geofenceTransition == Geofence.GEOFENCE_TRANSITION_DWELL) {
locationSnoozeProcessor.processLocationSnooze()
applicationScope.launch {
locationSnoozeProcessor.processLocationSnooze()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,23 @@ class ReminderAlarmActivity : AppCompatActivity() {
} else {
this@ReminderAlarmActivity
}
mediaPlayer =
MediaPlayer.create(
audioContext,
preferencesDataSource.preferences.value.alarmRingtone ?: Settings.System.DEFAULT_ALARM_ALERT_URI,
null,
AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ALARM).build(),
0
).apply { isLooping = true }
val tmpMediaPlayer = MediaPlayer.create(
audioContext,
preferencesDataSource.preferences.value.alarmRingtone ?: Settings.System.DEFAULT_ALARM_ALERT_URI,
null,
AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ALARM).build(),
0
) ?: MediaPlayer.create(
audioContext,
Settings.System.DEFAULT_ALARM_ALERT_URI,
null,
AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ALARM).build(),
0
)
if (tmpMediaPlayer != null) {
tmpMediaPlayer.isLooping = true
mediaPlayer = tmpMediaPlayer
}
}

private suspend fun startAlarm() {
Expand Down Expand Up @@ -136,6 +145,7 @@ class ReminderAlarmActivity : AppCompatActivity() {

private fun releaseMediaPlayer() {
mediaPlayer?.release()
mediaPlayer = null
Log.d("ReminderAlarm", "Released media player")
}

Expand Down Expand Up @@ -178,7 +188,7 @@ class ReminderAlarmActivity : AppCompatActivity() {
reminderNotificationData: ReminderNotificationData
): Intent {
val intent = Intent(context, ReminderAlarmActivity::class.java).apply {
setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP
}

reminderNotificationData.toIntent(intent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class AlarmProcessor @Inject constructor(
) {
private val exactReminders: Boolean = preferencesDataSource.preferences.value.exactReminders

fun setAlarmForReminderNotification(scheduledReminderNotificationData: ReminderNotificationData) {
suspend fun setAlarmForReminderNotification(scheduledReminderNotificationData: ReminderNotificationData, reminderNotificationProcessor: ReminderNotificationProcessor? = null) {
val originalInstant = scheduledReminderNotificationData.remindInstant
scheduledReminderNotificationData.remindInstant = adjustTimestamp(timeAccess, originalInstant)

Expand All @@ -49,9 +49,13 @@ class AlarmProcessor @Inject constructor(
scheduledReminderNotificationData
)
)
val reminderIntent = getReminderAction(context)
scheduledReminderNotificationData.toIntent(reminderIntent)
context.sendBroadcast(reminderIntent, RECEIVER_PERMISSION)
if (reminderNotificationProcessor == null) {
val reminderIntent = getReminderAction(context)
scheduledReminderNotificationData.toIntent(reminderIntent)
context.sendBroadcast(reminderIntent, RECEIVER_PERMISSION)
} else {
reminderNotificationProcessor.processReminders(scheduledReminderNotificationData)
}
return
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class LocationSnoozeProcessor @Inject constructor(
private val persistentDataDataSource: PersistentDataDataSource,
private val geofenceRegistrar: GeofenceRegistrar
) {
fun processLocationSnooze() {
suspend fun processLocationSnooze() {
val pending = persistentDataDataSource.getPendingLocationSnoozes()
Log.d(LogTags.REMINDER, "In home location, restoring ${pending.size} snoozed reminders")
for (data in pending) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import javax.inject.Inject
class NotificationProcessor @Inject constructor(
private val alarmProcessor: AlarmProcessor,
private val notifications: Notifications,
private val scheduleNextReminderNotificationProcessor: ScheduleNextReminderNotificationProcessor,
private val stockHandlingProcessor: StockHandlingProcessor,
private val repeatProcessor: RepeatProcessor,
private val notificationManager: NotificationManager,
Expand All @@ -56,8 +55,6 @@ class NotificationProcessor @Inject constructor(

setReminderEventStatus(status, reminderEventsToUpdate)

// Reschedule since the trigger condition for a linked reminder might have changed
scheduleNextReminderNotificationProcessor.scheduleNextReminder()
}

fun cancelNotification(notificationId: Int) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ class ReminderNotificationProcessor @Inject constructor(
val notifications: Notifications,
val notificationProcessor: NotificationProcessor,
val repeatProcessor: RepeatProcessor,
val scheduleNextReminderNotificationProcessor: ScheduleNextReminderNotificationProcessor,
private val reminderNotificationFactory: ReminderNotificationFactory,
private val reminderEventRepository: ReminderEventRepository,
private val preferencesDataSource: PreferencesDataSource
Expand All @@ -46,9 +45,6 @@ class ReminderNotificationProcessor @Inject constructor(
notificationAction(nonTakenReminderNotification)
}

val processedEvents = nonTakenReminderNotification.reminderNotificationParts.map { it.reminderEvent }
scheduleNextReminderNotificationProcessor.scheduleNextReminder(processedEvents)

return true
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ package com.futsch1.medtimer.reminders
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import com.futsch1.medtimer.ActivityCodes
import com.futsch1.medtimer.LogTags
import com.futsch1.medtimer.ProcessorCode
import com.futsch1.medtimer.di.ApplicationScope
import com.futsch1.medtimer.model.Reminder
Expand Down Expand Up @@ -61,32 +64,47 @@ class ReminderProcessorBroadcastReceiver : BroadcastReceiver() {
val pendingResult = goAsync()
applicationScope.launch {
try {
Log.d(LogTags.REMINDER, "Received intent $intentAction")
when (intentAction) {
ProcessorCode.Dismissed -> notificationProcessor.processReminderEventsInNotification(
ProcessedNotificationData.fromBundle(intent.extras!!),
ReminderEvent.ReminderStatus.SKIPPED
)

ProcessorCode.Taken -> notificationProcessor.processReminderEventsInNotification(
ProcessedNotificationData.fromBundle(intent.extras!!),
ReminderEvent.ReminderStatus.TAKEN
)

ProcessorCode.Acknowledged -> notificationProcessor.processReminderEventsInNotification(
ProcessedNotificationData.fromBundle(intent.extras!!),
ReminderEvent.ReminderStatus.ACKNOWLEDGED
)
ProcessorCode.Dismissed -> {
notificationProcessor.processReminderEventsInNotification(
ProcessedNotificationData.fromBundle(intent.extras!!),
ReminderEvent.ReminderStatus.SKIPPED
)
scheduleNextReminderNotificationProcessor.scheduleNextReminder()
}

ProcessorCode.Taken -> {
notificationProcessor.processReminderEventsInNotification(
ProcessedNotificationData.fromBundle(intent.extras!!),
ReminderEvent.ReminderStatus.TAKEN
)
scheduleNextReminderNotificationProcessor.scheduleNextReminder()
}

ProcessorCode.Acknowledged -> {
notificationProcessor.processReminderEventsInNotification(
ProcessedNotificationData.fromBundle(intent.extras!!),
ReminderEvent.ReminderStatus.ACKNOWLEDGED
)
scheduleNextReminderNotificationProcessor.scheduleNextReminder()
}

ProcessorCode.Snooze -> processSnooze(intent)
ProcessorCode.Reminder -> reminderNotificationProcessor.processReminders(ReminderNotificationData.fromBundle(intent.extras!!))
ProcessorCode.Reminder -> {
if (reminderNotificationProcessor.processReminders(ReminderNotificationData.fromBundle(intent.extras ?: Bundle()))) {
scheduleNextReminderNotificationProcessor.scheduleNextReminder()
}
}

ProcessorCode.ShowReminderNotification -> showReminderNotificationProcessor.showReminder(
ReminderNotificationData.fromBundle(intent.extras!!)
)

ProcessorCode.Refill -> processRefill(intent)
ProcessorCode.StockHandling -> processStockHandling(intent)
ProcessorCode.Schedule -> scheduleNextReminderNotificationProcessor.scheduleNextReminder()
ProcessorCode.LocationSnooze -> snoozeProcessor.processLocationSnooze(ReminderNotificationData.fromBundle(intent.extras!!))
ProcessorCode.LocationSnooze -> snoozeProcessor.processLocationSnooze(ReminderNotificationData.fromBundle(intent.extras ?: Bundle()))
}
} finally {
pendingResult.finish()
Expand All @@ -102,7 +120,7 @@ class ReminderProcessorBroadcastReceiver : BroadcastReceiver() {
}
}

private fun processSnooze(intent: Intent) {
private suspend fun processSnooze(intent: Intent) {
snoozeProcessor.processSnooze(
ReminderNotificationData.fromBundle(intent.extras!!),
intent.getLongExtra(ActivityCodes.EXTRA_SNOOZE_TIME_SECONDS, 0).toDuration(DurationUnit.SECONDS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,44 @@ import android.util.Log
import com.futsch1.medtimer.LogTags
import com.futsch1.medtimer.database.MedicineRepository
import com.futsch1.medtimer.database.ReminderEventRepository
import com.futsch1.medtimer.model.Medicine
import com.futsch1.medtimer.model.ReminderEvent
import com.futsch1.medtimer.model.ScheduledReminder
import com.futsch1.medtimer.preferences.PreferencesDataSource
import com.futsch1.medtimer.reminders.notificationData.ReminderNotificationData
import com.futsch1.medtimer.reminders.scheduling.ReminderScheduler
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class ScheduleNextReminderNotificationProcessor @Inject constructor(
private val alarmProcessor: AlarmProcessor,
private val reminderNotificationProcessor: ReminderNotificationProcessor,
private val medicineRepository: MedicineRepository,
private val reminderEventRepository: ReminderEventRepository,
private val timeAccess: TimeAccess,
private val preferencesDataSource: PreferencesDataSource
) {
private val mutex = Mutex()

suspend fun scheduleNextReminder(processedEvents: List<ReminderEvent> = emptyList()) {
val medicines = medicineRepository.getAll()
val reminderEvents = reminderEventRepository.getForScheduling(medicines)
val allEvents = (reminderEvents + processedEvents).distinctBy { it.reminderEventId }
suspend fun scheduleNextReminder() {
mutex.withLock {
val medicines = medicineRepository.getAll()
val reminderEvents = reminderEventRepository.getForScheduling(medicines)
Log.d(LogTags.REMINDER, "Schedule next reminders, considering events ${reminderEvents.map { it.reminderEventId }}")

scheduleNextReminderInternal(medicines, allEvents)
}
val scheduledReminders = ReminderScheduler(timeAccess, preferencesDataSource).schedule(medicines, reminderEvents)
if (scheduledReminders.isEmpty()) {
Log.d(LogTags.REMINDER, "No reminders scheduled")
alarmProcessor.cancelNextReminder()
return
}

val data = ReminderNotificationData.fromScheduledReminders(
if (preferencesDataSource.preferences.value.combineNotifications) scheduledReminders
else listOf(scheduledReminders[0])
)

private fun scheduleNextReminderInternal(
medicines: List<Medicine>,
reminderEvents: List<ReminderEvent>
) {
val reminderScheduler = ReminderScheduler(timeAccess, preferencesDataSource)
val scheduledReminders: List<ScheduledReminder> =
reminderScheduler.schedule(medicines, reminderEvents)
if (scheduledReminders.isNotEmpty()) {
val scheduledReminderNotificationData =
ReminderNotificationData.fromScheduledReminders(
if (preferencesDataSource.preferences.value.combineNotifications) scheduledReminders else listOf(
scheduledReminders[0]
)
)
alarmProcessor.setAlarmForReminderNotification(scheduledReminderNotificationData)
} else {
Log.d(LogTags.REMINDER, "No reminders scheduled")
alarmProcessor.cancelNextReminder()
alarmProcessor.setAlarmForReminderNotification(data, reminderNotificationProcessor)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.futsch1.medtimer.reminders

import android.app.NotificationManager
import android.content.Context
import android.util.Log
import com.futsch1.medtimer.LogTags
import com.futsch1.medtimer.reminders.notificationData.ReminderNotificationData
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject

/**
Expand All @@ -14,8 +16,8 @@ import javax.inject.Inject
* the actual notification scheduling to [AlarmProcessor].
*/
class ShowReminderNotificationProcessor @Inject constructor(
@param:ApplicationContext private val context: Context,
private val alarmProcessor: AlarmProcessor,
private val scheduleNextReminderNotificationProcessor: ScheduleNextReminderNotificationProcessor,
private val notificationProcessor: NotificationProcessor,
private val notificationManager: NotificationManager
) {
Expand All @@ -26,8 +28,6 @@ class ShowReminderNotificationProcessor @Inject constructor(
if (!isNotificationActive(reminderNotificationData)) {
alarmProcessor.setAlarmForReminderNotification(reminderNotificationData)
}

scheduleNextReminderNotificationProcessor.scheduleNextReminder()
}

private suspend fun isNotificationActive(reminderNotificationData: ReminderNotificationData): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ open class SnoozeProcessor @Inject constructor(
private val geofenceRegistrar: GeofenceRegistrar
) {

fun processSnooze(reminderNotificationData: ReminderNotificationData, snoozeTime: Duration) {
suspend fun processSnooze(reminderNotificationData: ReminderNotificationData, snoozeTime: Duration) {
reminderNotificationData.remindInstant = Instant.now().plusSeconds(snoozeTime.inWholeSeconds)
Log.d(LogTags.REMINDER, "Snoozing reminder: $reminderNotificationData")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ open class ReminderNotificationFactory @Inject constructor(
reminderNotificationData.remindInstant.epochSecond, medicine, reminder, reminderEventRepository, timeFormatter
)
reminderEvent = reminderEventRepository.create(newEvent.copy(remainingRepeats = numberOfRepeats))
Log.d(LogTags.REMINDER, "Created reminder event rEID [${reminderEvent.reminderEventId}] for reminder rID [${reminder.id}]")
} else {
reminderNotificationData.notificationId = reminderEvent.notificationId
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,11 @@ class ChartsFragment : Fragment() {
viewModel.setDays(viewModel.uiState.value?.days ?: (arguments?.getInt(DAYS_BUNDLE_KEY) ?: 0))
}

fun setDays(days: Int) = viewModel.setDays(days)
fun setDays(days: Int) {
if (isAdded) {
viewModel.setDays(days)
}
}

private inner class DaysSinceEpochFormat : NumberFormat() {
override fun format(
Expand Down
Loading
Loading