diff --git a/app/src/main/kotlin/org/fossify/clock/activities/ReminderActivity.kt b/app/src/main/kotlin/org/fossify/clock/activities/ReminderActivity.kt index 655733cd..cc71aac6 100644 --- a/app/src/main/kotlin/org/fossify/clock/activities/ReminderActivity.kt +++ b/app/src/main/kotlin/org/fossify/clock/activities/ReminderActivity.kt @@ -24,6 +24,7 @@ import org.fossify.commons.helpers.MINUTE_SECONDS import org.fossify.commons.helpers.SILENT import org.fossify.commons.helpers.isOreoMr1Plus import org.fossify.commons.helpers.isOreoPlus +import java.util.Calendar class ReminderActivity : SimpleActivity() { companion object { @@ -273,23 +274,29 @@ class ReminderActivity : SimpleActivity() { private fun snoozeAlarm(overrideSnoozeDuration: Int? = null) { destroyEffects() if (overrideSnoozeDuration != null) { - setupAlarmClock(alarm!!, overrideSnoozeDuration * MINUTE_SECONDS) - wasAlarmSnoozed = true - finishActivity() + scheduleSnoozedAlarm(overrideSnoozeDuration) } else if (config.useSameSnooze) { - setupAlarmClock(alarm!!, config.snoozeTime * MINUTE_SECONDS) - wasAlarmSnoozed = true - finishActivity() + scheduleSnoozedAlarm(config.snoozeTime) } else { showPickSecondsDialog(config.snoozeTime * MINUTE_SECONDS, true, cancelCallback = { finishActivity() }) { config.snoozeTime = it / MINUTE_SECONDS - setupAlarmClock(alarm!!, it) - wasAlarmSnoozed = true - finishActivity() + scheduleSnoozedAlarm(config.snoozeTime) } } } + private fun scheduleSnoozedAlarm(snoozeMinutes: Int) { + setupAlarmClock( + alarm = alarm!!, + triggerTimeMillis = Calendar.getInstance() + .apply { add(Calendar.MINUTE, snoozeMinutes) } + .timeInMillis + ) + + wasAlarmSnoozed = true + finishActivity() + } + private fun finishActivity() { if (!wasAlarmSnoozed && alarm != null) { cancelAlarmClock(alarm!!) diff --git a/app/src/main/kotlin/org/fossify/clock/activities/SnoozeReminderActivity.kt b/app/src/main/kotlin/org/fossify/clock/activities/SnoozeReminderActivity.kt index 17f40d60..7f80b392 100644 --- a/app/src/main/kotlin/org/fossify/clock/activities/SnoozeReminderActivity.kt +++ b/app/src/main/kotlin/org/fossify/clock/activities/SnoozeReminderActivity.kt @@ -9,6 +9,7 @@ import org.fossify.clock.extensions.setupAlarmClock import org.fossify.clock.helpers.ALARM_ID import org.fossify.commons.extensions.showPickSecondsDialog import org.fossify.commons.helpers.MINUTE_SECONDS +import java.util.Calendar class SnoozeReminderActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -18,7 +19,12 @@ class SnoozeReminderActivity : AppCompatActivity() { hideNotification(id) showPickSecondsDialog(config.snoozeTime * MINUTE_SECONDS, true, cancelCallback = { dialogCancelled() }) { config.snoozeTime = it / MINUTE_SECONDS - setupAlarmClock(alarm, it) + setupAlarmClock( + alarm = alarm, + triggerTimeMillis = Calendar.getInstance() + .apply { add(Calendar.SECOND, it) } + .timeInMillis + ) finishActivity() } } diff --git a/app/src/main/kotlin/org/fossify/clock/extensions/Context.kt b/app/src/main/kotlin/org/fossify/clock/extensions/Context.kt index a175392f..8ab7a47b 100644 --- a/app/src/main/kotlin/org/fossify/clock/extensions/Context.kt +++ b/app/src/main/kotlin/org/fossify/clock/extensions/Context.kt @@ -13,7 +13,6 @@ import android.os.Handler import android.os.Looper import android.os.PowerManager import android.text.SpannableString -import android.text.format.DateFormat import android.text.style.RelativeSizeSpan import android.widget.Toast import androidx.core.app.AlarmManagerCompat @@ -36,7 +35,8 @@ import org.fossify.commons.helpers.* import java.text.SimpleDateFormat import java.util.Calendar import java.util.Locale -import kotlin.math.pow +import kotlin.math.ceil +import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.minutes val Context.config: Config get() = Config.newInstance(applicationContext) @@ -100,59 +100,44 @@ fun Context.createNewTimer(): Timer { } fun Context.scheduleNextAlarm(alarm: Alarm, showToast: Boolean) { - val calendar = Calendar.getInstance() - calendar.firstDayOfWeek = Calendar.MONDAY - val currentTimeInMinutes = getCurrentDayMinutes() + val triggerTimeMillis = getTimeOfNextAlarm(alarm)?.timeInMillis ?: return + setupAlarmClock(alarm = alarm, triggerTimeMillis = triggerTimeMillis) - if (alarm.days == TODAY_BIT) { - val triggerInMinutes = alarm.timeInMinutes - currentTimeInMinutes - setupAlarmClock(alarm, triggerInMinutes * 60 - calendar.get(Calendar.SECOND)) - - if (showToast) { - showRemainingTimeMessage(triggerInMinutes) - } - } else if (alarm.days == TOMORROW_BIT) { - val triggerInMinutes = alarm.timeInMinutes - currentTimeInMinutes + DAY_MINUTES - setupAlarmClock(alarm, triggerInMinutes * 60 - calendar.get(Calendar.SECOND)) + if (showToast) { + val now = Calendar.getInstance() + val triggerInMillis = triggerTimeMillis - now.timeInMillis + showRemainingTimeMessage(triggerInMillis) + } +} - if (showToast) { - showRemainingTimeMessage(triggerInMinutes) - } +fun Context.showRemainingTimeMessage(triggerInMillis: Long) { + val totalSeconds = triggerInMillis.milliseconds.inWholeSeconds.toInt() + val remainingTime = if (totalSeconds >= MINUTE_SECONDS) { + val roundedMinutes = ceil(totalSeconds / MINUTE_SECONDS.toFloat()).toInt() + formatMinutesToTimeString(roundedMinutes) } else { - for (i in 0..7) { - val currentDay = (calendar.get(Calendar.DAY_OF_WEEK) + 5) % 7 - val isCorrectDay = alarm.days and 2.0.pow(currentDay).toInt() != 0 - if (isCorrectDay && (alarm.timeInMinutes > currentTimeInMinutes || i > 0)) { - val triggerInMinutes = alarm.timeInMinutes - currentTimeInMinutes + (i * DAY_MINUTES) - setupAlarmClock(alarm, triggerInMinutes * 60 - calendar.get(Calendar.SECOND)) - - if (showToast) { - showRemainingTimeMessage(triggerInMinutes) - } - break - } else { - calendar.add(Calendar.DAY_OF_MONTH, 1) - } - } + formatSecondsToTimeString(totalSeconds) } -} -fun Context.showRemainingTimeMessage(totalMinutes: Int) { - val fullString = String.format(getString(org.fossify.commons.R.string.time_remaining), formatMinutesToTimeString(totalMinutes)) - toast(fullString, Toast.LENGTH_LONG) + toast( + msg = String.format( + getString(org.fossify.commons.R.string.time_remaining), remainingTime + ), + length = Toast.LENGTH_LONG + ) } -fun Context.setupAlarmClock(alarm: Alarm, triggerInSeconds: Int) { +fun Context.setupAlarmClock(alarm: Alarm, triggerTimeMillis: Long) { val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager - val targetMS = System.currentTimeMillis() + triggerInSeconds * 1000 + try { - AlarmManagerCompat.setAlarmClock(alarmManager, targetMS, getOpenAlarmTabIntent(), getAlarmIntent(alarm)) + AlarmManagerCompat.setAlarmClock(alarmManager, triggerTimeMillis, getOpenAlarmTabIntent(), getAlarmIntent(alarm)) // show a notification to allow dismissing the alarm 10 minutes before it actually triggers - val dismissalTriggerTime = if (targetMS - System.currentTimeMillis() < 10.minutes.inWholeMilliseconds) { + val dismissalTriggerTime = if (triggerTimeMillis - System.currentTimeMillis() < 10.minutes.inWholeMilliseconds) { System.currentTimeMillis() + 500 } else { - targetMS - 10.minutes.inWholeMilliseconds + triggerTimeMillis - 10.minutes.inWholeMilliseconds } AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, 0, dismissalTriggerTime, getEarlyAlarmDismissalIntent(alarm)) } catch (e: Exception) { @@ -277,27 +262,18 @@ fun Context.getClosestEnabledAlarmString(callback: (result: String) -> Unit) { return@getEnabledAlarms } + val now = Calendar.getInstance() val nextAlarmList = enabledAlarms - .mapNotNull { getTimeUntilNextAlarm(it.timeInMinutes, it.days) } + .mapNotNull(::getTimeOfNextAlarm) + .filter { it > now } - if (nextAlarmList.isEmpty()) { - callback("") - } - - var closestAlarmTime = Int.MAX_VALUE - nextAlarmList.forEach { time -> - if (time < closestAlarmTime) { - closestAlarmTime = time - } - } - - if (closestAlarmTime == Int.MAX_VALUE) { + val closestAlarmTime = nextAlarmList.minOrNull() + if (closestAlarmTime == null) { callback("") + return@getEnabledAlarms } - val calendar = Calendar.getInstance().apply { firstDayOfWeek = Calendar.MONDAY } - calendar.add(Calendar.MINUTE, closestAlarmTime) - val dayOfWeekIndex = (calendar.get(Calendar.DAY_OF_WEEK) + 5) % 7 + val dayOfWeekIndex = (closestAlarmTime.get(Calendar.DAY_OF_WEEK) + 5) % 7 val dayOfWeek = resources.getStringArray(org.fossify.commons.R.array.week_days_short)[dayOfWeekIndex] val pattern = if (config.use24HourFormat) { FORMAT_24H @@ -305,7 +281,7 @@ fun Context.getClosestEnabledAlarmString(callback: (result: String) -> Unit) { FORMAT_12H } - val formattedTime = SimpleDateFormat(pattern, Locale.getDefault()).format(calendar.time) + val formattedTime = SimpleDateFormat(pattern, Locale.getDefault()).format(closestAlarmTime.time) callback("$dayOfWeek $formattedTime") } } @@ -359,7 +335,7 @@ fun Context.getTimerNotification(timer: Timer, pendingIntent: PendingIntent, add if (isOreoPlus()) { try { notificationManager.deleteNotificationChannel(channelId) - } catch (e: Exception) { + } catch (_: Exception) { } val audioAttributes = AudioAttributes.Builder() @@ -385,10 +361,8 @@ fun Context.getTimerNotification(timer: Timer, pendingIntent: PendingIntent, add } } - val title = if (timer.label.isEmpty()) { + val title = timer.label.ifEmpty { getString(R.string.timer) - } else { - timer.label } val reminderActivityIntent = getReminderActivityIntent() diff --git a/app/src/main/kotlin/org/fossify/clock/helpers/Constants.kt b/app/src/main/kotlin/org/fossify/clock/helpers/Constants.kt index 64ad9ade..bfce9b5e 100644 --- a/app/src/main/kotlin/org/fossify/clock/helpers/Constants.kt +++ b/app/src/main/kotlin/org/fossify/clock/helpers/Constants.kt @@ -1,8 +1,16 @@ package org.fossify.clock.helpers import org.fossify.clock.extensions.isBitSet +import org.fossify.clock.models.Alarm import org.fossify.clock.models.MyTimeZone -import org.fossify.commons.helpers.* +import org.fossify.commons.helpers.FRIDAY_BIT +import org.fossify.commons.helpers.MONDAY_BIT +import org.fossify.commons.helpers.SATURDAY_BIT +import org.fossify.commons.helpers.SUNDAY_BIT +import org.fossify.commons.helpers.THURSDAY_BIT +import org.fossify.commons.helpers.TUESDAY_BIT +import org.fossify.commons.helpers.WEDNESDAY_BIT +import org.fossify.commons.helpers.isPiePlus import java.util.Calendar import java.util.Date import java.util.TimeZone @@ -125,7 +133,13 @@ fun getPassedSeconds(): Int { return ((calendar.timeInMillis + offset) / 1000).toInt() } -fun formatTime(showSeconds: Boolean, use24HourFormat: Boolean, hours: Int, minutes: Int, seconds: Int): String { +fun formatTime( + showSeconds: Boolean, + use24HourFormat: Boolean, + hours: Int, + minutes: Int, + seconds: Int, +): String { val hoursFormat = if (use24HourFormat) "%02d" else "%01d" var format = "$hoursFormat:%02d" @@ -254,39 +268,33 @@ fun getAllTimeZones() = arrayListOf( MyTimeZone(89, "GMT+13:00 Tongatapu", "Pacific/Tongatapu") ) -fun getTimeUntilNextAlarm(alarmTimeInMinutes: Int, days: Int): Int? { - val calendar = Calendar.getInstance() - calendar.firstDayOfWeek = Calendar.MONDAY - val currentTimeInMinutes = calendar.get(Calendar.HOUR_OF_DAY) * 60 + calendar.get(Calendar.MINUTE) - val currentDayOfWeek = calendar.get(Calendar.DAY_OF_WEEK) - Calendar.MONDAY +fun getTimeOfNextAlarm(alarm: Alarm): Calendar? { + return getTimeOfNextAlarm(alarm.timeInMinutes, alarm.days) +} - var minTimeDifferenceInMinutes = Int.MAX_VALUE +fun getTimeOfNextAlarm(alarmTimeInMinutes: Int, days: Int): Calendar? { + val nextAlarmTime = Calendar.getInstance().apply { + firstDayOfWeek = Calendar.MONDAY // why is this here? seems unnecessary + set(Calendar.HOUR_OF_DAY, alarmTimeInMinutes / 60) + set(Calendar.MINUTE, alarmTimeInMinutes % 60) + set(Calendar.SECOND, 0) + set(Calendar.MILLISECOND, 0) + } - for (i in 0..6) { - val alarmDayOfWeek = (currentDayOfWeek + i) % 7 - if (isAlarmEnabledForDay(alarmDayOfWeek, days)) { - val timeDifferenceInMinutes = getTimeDifferenceInMinutes(currentTimeInMinutes, alarmTimeInMinutes, i) - if (timeDifferenceInMinutes < minTimeDifferenceInMinutes) { - minTimeDifferenceInMinutes = timeDifferenceInMinutes + return when (days) { + TODAY_BIT -> nextAlarmTime // do nothing, alarm is today + TOMORROW_BIT -> nextAlarmTime.apply { add(Calendar.DAY_OF_MONTH, 1) } + else -> { + val now = Calendar.getInstance() + repeat(8) { + val currentDay = (nextAlarmTime.get(Calendar.DAY_OF_WEEK) + 5) % 7 + if (days.isBitSet(currentDay) && now < nextAlarmTime) { + return nextAlarmTime + } else { + nextAlarmTime.add(Calendar.DAY_OF_MONTH, 1) + } } + null } } - - return if (minTimeDifferenceInMinutes != Int.MAX_VALUE) { - minTimeDifferenceInMinutes - } else { - null - } -} - -fun isAlarmEnabledForDay(day: Int, alarmDays: Int) = alarmDays.isBitSet(day) - -fun getTimeDifferenceInMinutes(currentTimeInMinutes: Int, alarmTimeInMinutes: Int, daysUntilAlarm: Int): Int { - val minutesInADay = 24 * 60 - val minutesUntilAlarm = daysUntilAlarm * minutesInADay + alarmTimeInMinutes - return if (minutesUntilAlarm > currentTimeInMinutes) { - minutesUntilAlarm - currentTimeInMinutes - } else { - minutesInADay - (currentTimeInMinutes - minutesUntilAlarm) - } } diff --git a/app/src/main/kotlin/org/fossify/clock/services/SnoozeService.kt b/app/src/main/kotlin/org/fossify/clock/services/SnoozeService.kt index 9b4024b4..68f3c473 100644 --- a/app/src/main/kotlin/org/fossify/clock/services/SnoozeService.kt +++ b/app/src/main/kotlin/org/fossify/clock/services/SnoozeService.kt @@ -7,13 +7,18 @@ import org.fossify.clock.extensions.dbHelper import org.fossify.clock.extensions.hideNotification import org.fossify.clock.extensions.setupAlarmClock import org.fossify.clock.helpers.ALARM_ID -import org.fossify.commons.helpers.MINUTE_SECONDS +import java.util.Calendar class SnoozeService : IntentService("Snooze") { override fun onHandleIntent(intent: Intent?) { val id = intent!!.getIntExtra(ALARM_ID, -1) val alarm = dbHelper.getAlarmWithId(id) ?: return hideNotification(id) - setupAlarmClock(alarm, config.snoozeTime * MINUTE_SECONDS) + setupAlarmClock( + alarm = alarm, + triggerTimeMillis = Calendar.getInstance() + .apply { add(Calendar.MINUTE, config.snoozeTime) } + .timeInMillis + ) } }