Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #113 #115

Draft
wants to merge 20 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 15 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
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,7 @@ class MainActivity : SimpleActivity() {
setupTabs()
updateWidgets()

getEnabledAlarms { enabledAlarms ->
if (enabledAlarms.isNullOrEmpty()) {
ensureBackgroundThread {
rescheduleEnabledAlarms()
}
}
}
rescheduleEnabledAlarms()
}

override fun onResume() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,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 {
Expand Down Expand Up @@ -267,23 +268,23 @@ class ReminderActivity : SimpleActivity() {
private fun snoozeAlarm(overrideSnoozeDuration: Int? = null) {
destroyEffects()
if (overrideSnoozeDuration != null) {
setupAlarmClock(alarm!!, overrideSnoozeDuration * MINUTE_SECONDS)
wasAlarmSnoozed = true
finishActivity()
snoozeAlarm { add(Calendar.MINUTE, overrideSnoozeDuration) }
} else if (config.useSameSnooze) {
setupAlarmClock(alarm!!, config.snoozeTime * MINUTE_SECONDS)
wasAlarmSnoozed = true
finishActivity()
snoozeAlarm { add(Calendar.MINUTE, config.snoozeTime) }
} else {
showPickSecondsDialog(config.snoozeTime * MINUTE_SECONDS, true, cancelCallback = { finishActivity() }) {
config.snoozeTime = it / MINUTE_SECONDS
setupAlarmClock(alarm!!, it)
wasAlarmSnoozed = true
finishActivity()
snoozeAlarm { add(Calendar.SECOND, it) }
}
}
}

private fun snoozeAlarm(block: Calendar.() -> Unit) {
setupAlarmClock(alarm!!, block.run { Calendar.getInstance() })
wasAlarmSnoozed = true
finishActivity()
}

private fun finishActivity() {
if (!wasAlarmSnoozed && alarm != null) {
cancelAlarmClock(alarm!!)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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?) {
Expand All @@ -18,7 +19,7 @@ class SnoozeReminderActivity : AppCompatActivity() {
hideNotification(id)
showPickSecondsDialog(config.snoozeTime * MINUTE_SECONDS, true, cancelCallback = { dialogCancelled() }) {
config.snoozeTime = it / MINUTE_SECONDS
setupAlarmClock(alarm, it)
setupAlarmClock(alarm, Calendar.getInstance().apply { add(Calendar.SECOND, it) })
finishActivity()
}
}
Expand Down
83 changes: 26 additions & 57 deletions app/src/main/kotlin/org/fossify/clock/extensions/Context.kt
Original file line number Diff line number Diff line change
Expand Up @@ -100,40 +100,13 @@ fun Context.createNewTimer(): Timer {
}

fun Context.scheduleNextAlarm(alarm: Alarm, showToast: Boolean) {
val calendar = Calendar.getInstance()
calendar.firstDayOfWeek = Calendar.MONDAY
val currentTimeInMinutes = getCurrentDayMinutes()
val triggerTime = getTimeOfNextAlarm(alarm) ?: return
setupAlarmClock(alarm, triggerTime)

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) {
showRemainingTimeMessage(triggerInMinutes)
}
} 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)
}
}
if (showToast) {
val now = Calendar.getInstance()
val triggerInMillis = triggerTime.timeInMillis - now.timeInMillis
showRemainingTimeMessage((triggerInMillis / (1000 * 60)).toInt())
}
}

Expand All @@ -142,9 +115,10 @@ fun Context.showRemainingTimeMessage(totalMinutes: Int) {
toast(fullString, Toast.LENGTH_LONG)
}

fun Context.setupAlarmClock(alarm: Alarm, triggerInSeconds: Int) {
fun Context.setupAlarmClock(alarm: Alarm, triggerTime: Calendar) {
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
val targetMS = System.currentTimeMillis() + triggerInSeconds * 1000

val targetMS = triggerTime.timeInMillis
try {
AlarmManagerCompat.setAlarmClock(alarmManager, targetMS, getOpenAlarmTabIntent(), getAlarmIntent(alarm))

Expand Down Expand Up @@ -272,45 +246,37 @@ fun Context.formatTo12HourFormat(showSeconds: Boolean, hours: Int, minutes: Int,

fun Context.getClosestEnabledAlarmString(callback: (result: String) -> Unit) {
getEnabledAlarms { enabledAlarms ->
if (enabledAlarms.isNullOrEmpty()) {
if (enabledAlarms.isEmpty()) {
callback("")
return@getEnabledAlarms
}

val now = Calendar.getInstance()
val nextAlarmList = enabledAlarms
.mapNotNull { getTimeUntilNextAlarm(it.timeInMinutes, it.days) }

if (nextAlarmList.isEmpty()) {
callback("")
}
.mapNotNull(::getTimeOfNextAlarm)
.filter { it > now }

var closestAlarmTime = Int.MAX_VALUE
nextAlarmList.forEach { time ->
if (time < closestAlarmTime) {
closestAlarmTime = time
}
}
val closestAlarmTime = nextAlarmList.minOrNull()

if (closestAlarmTime == Int.MAX_VALUE) {
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 (DateFormat.is24HourFormat(this)) {
"HH:mm"
} else {
"h:mm a"
}

val formattedTime = SimpleDateFormat(pattern, Locale.getDefault()).format(calendar.time)
val formattedTime = SimpleDateFormat(pattern, Locale.getDefault()).format(closestAlarmTime.time)
callback("$dayOfWeek $formattedTime")
}
}

fun Context.getEnabledAlarms(callback: (result: List<Alarm>?) -> Unit) {
fun Context.getEnabledAlarms(callback: (result: List<Alarm>) -> Unit) {
ensureBackgroundThread {
val alarms = dbHelper.getEnabledAlarms()
Handler(Looper.getMainLooper()).post {
Expand All @@ -320,9 +286,12 @@ fun Context.getEnabledAlarms(callback: (result: List<Alarm>?) -> Unit) {
}

fun Context.rescheduleEnabledAlarms() {
dbHelper.getEnabledAlarms().forEach {
if (it.days != TODAY_BIT || it.timeInMinutes > getCurrentDayMinutes()) {
scheduleNextAlarm(it, false)
getEnabledAlarms { alarms ->
val now = Calendar.getInstance();
alarms.forEach {
if (getTimeOfNextAlarm(it)?.after(now) == true) {
scheduleNextAlarm(it, false)
}
}
}
}
Expand Down Expand Up @@ -359,7 +328,7 @@ fun Context.getTimerNotification(timer: Timer, pendingIntent: PendingIntent, add
if (isOreoPlus()) {
try {
notificationManager.deleteNotificationChannel(channelId)
} catch (e: Exception) {
} catch (_: Exception) {
}

val audioAttributes = AudioAttributes.Builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class AlarmFragment : Fragment(), ToggleAlarmInterface {
})
}
context?.getEnabledAlarms { enabledAlarms ->
if (enabledAlarms.isNullOrEmpty()) {
if (enabledAlarms.isEmpty()) {
val removedAlarms = mutableListOf<Alarm>()
alarms.forEach {
if (it.days == TODAY_BIT && it.isEnabled && it.timeInMinutes <= getCurrentDayMinutes()) {
Expand All @@ -95,6 +95,7 @@ class AlarmFragment : Fragment(), ToggleAlarmInterface {
if (it.oneShot) {
it.isEnabled = false
context?.dbHelper?.deleteAlarms(arrayListOf(it))
// TODO: Fix race condition. `alarms.removeAll(removedAlarms)` could be called before this
removedAlarms.add(it)
} else {
context?.dbHelper?.updateAlarmEnabledState(it.id, false)
Expand Down
71 changes: 37 additions & 34 deletions app/src/main/kotlin/org/fossify/clock/helpers/Constants.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
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 java.util.Calendar
Expand Down Expand Up @@ -111,14 +112,14 @@ fun formatTime(showSeconds: Boolean, use24HourFormat: Boolean, hours: Int, minut
fun getTomorrowBit(): Int {
val calendar = Calendar.getInstance()
calendar.add(Calendar.DAY_OF_WEEK, 1)
val dayOfWeek = (calendar.get(Calendar.DAY_OF_WEEK) + 5) % 7
return 2.0.pow(dayOfWeek).toInt()
val day = calendar.get(Calendar.DAY_OF_WEEK)
return getBitForCalendarDay(day)
}

fun getTodayBit(): Int {
val calendar = Calendar.getInstance()
val dayOfWeek = (calendar.get(Calendar.DAY_OF_WEEK) + 5) % 7
return 2.0.pow(dayOfWeek).toInt()
val day = calendar.get(Calendar.DAY_OF_WEEK)
return getBitForCalendarDay(day)
}

fun getBitForCalendarDay(day: Int): Int {
Expand Down Expand Up @@ -222,39 +223,41 @@ 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()
nextAlarmTime.firstDayOfWeek = Calendar.MONDAY

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 if (minTimeDifferenceInMinutes != Int.MAX_VALUE) {
minTimeDifferenceInMinutes
} else {
null
}
}
val hour = alarmTimeInMinutes / 60
val minute = alarmTimeInMinutes % 60

fun isAlarmEnabledForDay(day: Int, alarmDays: Int) = alarmDays.isBitSet(day)
nextAlarmTime.set(Calendar.HOUR_OF_DAY, hour)
nextAlarmTime.set(Calendar.MINUTE, minute)
nextAlarmTime.set(Calendar.SECOND, 0)
nextAlarmTime.set(Calendar.MILLISECOND, 0)

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)
return when (days) {
TODAY_BIT -> {
// do nothing, alarm is today
nextAlarmTime
}
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
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import org.fossify.commons.helpers.isOreoPlus
class AlarmReceiver : BroadcastReceiver() {

override fun onReceive(context: Context, intent: Intent) {
context.rescheduleEnabledAlarms();
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's the important line. The rest is improvements. There's a lot of weird shit in this code base.


val id = intent.getIntExtra(ALARM_ID, -1)
val alarm = context.dbHelper.getAlarmWithId(id) ?: return

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ 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, Calendar.getInstance().apply { add(Calendar.MINUTE, config.snoozeTime) })
}
}