diff --git a/app/src/main/kotlin/org/fossify/clock/activities/MainActivity.kt b/app/src/main/kotlin/org/fossify/clock/activities/MainActivity.kt index 10a6c12a..b3b326d8 100644 --- a/app/src/main/kotlin/org/fossify/clock/activities/MainActivity.kt +++ b/app/src/main/kotlin/org/fossify/clock/activities/MainActivity.kt @@ -15,6 +15,7 @@ import org.fossify.clock.adapters.ViewPagerAdapter import org.fossify.clock.databinding.ActivityMainBinding import org.fossify.clock.extensions.config import org.fossify.clock.extensions.getEnabledAlarms +import org.fossify.clock.extensions.handleFullScreenNotificationsPermission import org.fossify.clock.extensions.rescheduleEnabledAlarms import org.fossify.clock.extensions.updateWidgets import org.fossify.clock.helpers.* @@ -44,11 +45,16 @@ class MainActivity : SimpleActivity() { setupTabs() updateWidgets() migrateFirstDayOfWeek() + ensureBackgroundThread { + rescheduleEnabledAlarms() + } getEnabledAlarms { enabledAlarms -> - if (enabledAlarms.isNullOrEmpty()) { - ensureBackgroundThread { - rescheduleEnabledAlarms() + if (!enabledAlarms.isNullOrEmpty()) { + handleFullScreenNotificationsPermission { + if (!it) { + toast(org.fossify.commons.R.string.notifications_disabled) + } } } } @@ -87,10 +93,10 @@ class MainActivity : SimpleActivity() { private fun checkShortcuts() { val appIconColor = config.appIconColor if (isNougatMR1Plus() && config.lastHandledShortcutColor != appIconColor) { - val launchDialpad = getLaunchStopwatchShortcut(appIconColor) + val stopWatchShortcutInfo = getLaunchStopwatchShortcut(appIconColor) try { - shortcutManager.dynamicShortcuts = listOf(launchDialpad) + shortcutManager.dynamicShortcuts = listOf(stopWatchShortcutInfo) config.lastHandledShortcutColor = appIconColor } catch (ignored: Exception) { } 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 3f4061a5..f0d7d015 100644 --- a/app/src/main/kotlin/org/fossify/clock/activities/ReminderActivity.kt +++ b/app/src/main/kotlin/org/fossify/clock/activities/ReminderActivity.kt @@ -20,10 +20,10 @@ import org.fossify.clock.databinding.ActivityReminderBinding import org.fossify.clock.extensions.cancelAlarmClock import org.fossify.clock.extensions.config import org.fossify.clock.extensions.dbHelper +import org.fossify.clock.extensions.disableExpiredAlarm import org.fossify.clock.extensions.getFormattedTime import org.fossify.clock.extensions.scheduleNextAlarm import org.fossify.clock.extensions.setupAlarmClock -import org.fossify.clock.extensions.updateWidgets import org.fossify.clock.helpers.ALARM_ID import org.fossify.clock.helpers.ALARM_NOTIF_ID import org.fossify.clock.helpers.getPassedSeconds @@ -363,15 +363,8 @@ class ReminderActivity : SimpleActivity() { if (alarm!!.days > 0) { scheduleNextAlarm(alarm!!, false) } - if (alarm!!.days < 0) { - if (alarm!!.oneShot) { - alarm!!.isEnabled = false - dbHelper.deleteAlarms(arrayListOf(alarm!!)) - } else { - dbHelper.updateAlarmEnabledState(alarm!!.id, false) - } - updateWidgets() - } + + disableExpiredAlarm(alarm!!) } finished = true diff --git a/app/src/main/kotlin/org/fossify/clock/adapters/AlarmsAdapter.kt b/app/src/main/kotlin/org/fossify/clock/adapters/AlarmsAdapter.kt index 3b5ac879..407a7f60 100644 --- a/app/src/main/kotlin/org/fossify/clock/adapters/AlarmsAdapter.kt +++ b/app/src/main/kotlin/org/fossify/clock/adapters/AlarmsAdapter.kt @@ -33,8 +33,8 @@ import org.fossify.commons.views.MyRecyclerView class AlarmsAdapter( activity: SimpleActivity, - var alarms: ArrayList, - val toggleAlarmInterface: ToggleAlarmInterface, + private var alarms: ArrayList, + private val toggleAlarmInterface: ToggleAlarmInterface, recyclerView: MyRecyclerView, itemClick: (Any) -> Unit, ) : MyRecyclerViewAdapter(activity, recyclerView, itemClick), ItemTouchHelperContract { 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 ecb37431..c3e1ab46 100644 --- a/app/src/main/kotlin/org/fossify/clock/extensions/Context.kt +++ b/app/src/main/kotlin/org/fossify/clock/extensions/Context.kt @@ -56,6 +56,7 @@ import org.fossify.clock.helpers.getPassedSeconds import org.fossify.clock.helpers.getTimeOfNextAlarm import org.fossify.clock.interfaces.TimerDao import org.fossify.clock.models.Alarm +import org.fossify.clock.models.AlarmEvent import org.fossify.clock.models.MyTimeZone import org.fossify.clock.models.Timer import org.fossify.clock.models.TimerState @@ -87,6 +88,7 @@ import org.fossify.commons.helpers.TUESDAY_BIT import org.fossify.commons.helpers.WEDNESDAY_BIT import org.fossify.commons.helpers.ensureBackgroundThread import org.fossify.commons.helpers.isOreoPlus +import org.greenrobot.eventbus.EventBus import java.text.SimpleDateFormat import java.util.Calendar import java.util.Locale @@ -600,3 +602,17 @@ fun Context.firstDayOrder(bitMask: Int): Int { return bitMask } + +fun Context.disableExpiredAlarm(alarm: Alarm) { + if (alarm.days < 0) { + if (alarm.oneShot) { + alarm.isEnabled = false + dbHelper.deleteAlarms(arrayListOf(alarm)) + } else { + dbHelper.updateAlarmEnabledState(alarm.id, false) + } + + updateWidgets() + EventBus.getDefault().post(AlarmEvent.Refresh) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/fossify/clock/fragments/AlarmFragment.kt b/app/src/main/kotlin/org/fossify/clock/fragments/AlarmFragment.kt index 6b8c516d..5af7b6e9 100644 --- a/app/src/main/kotlin/org/fossify/clock/fragments/AlarmFragment.kt +++ b/app/src/main/kotlin/org/fossify/clock/fragments/AlarmFragment.kt @@ -11,8 +11,18 @@ import org.fossify.clock.adapters.AlarmsAdapter import org.fossify.clock.databinding.FragmentAlarmBinding import org.fossify.clock.dialogs.ChangeAlarmSortDialog import org.fossify.clock.dialogs.EditAlarmDialog -import org.fossify.clock.extensions.* -import org.fossify.clock.helpers.* +import org.fossify.clock.extensions.cancelAlarmClock +import org.fossify.clock.extensions.config +import org.fossify.clock.extensions.createNewAlarm +import org.fossify.clock.extensions.dbHelper +import org.fossify.clock.extensions.firstDayOrder +import org.fossify.clock.extensions.handleFullScreenNotificationsPermission +import org.fossify.clock.extensions.scheduleNextAlarm +import org.fossify.clock.extensions.updateWidgets +import org.fossify.clock.helpers.DEFAULT_ALARM_MINUTES +import org.fossify.clock.helpers.SORT_BY_ALARM_TIME +import org.fossify.clock.helpers.SORT_BY_DATE_AND_TIME +import org.fossify.clock.helpers.getTomorrowBit import org.fossify.clock.interfaces.ToggleAlarmInterface import org.fossify.clock.models.Alarm import org.fossify.clock.models.AlarmEvent @@ -34,7 +44,11 @@ class AlarmFragment : Fragment(), ToggleAlarmInterface { private lateinit var binding: FragmentAlarmBinding - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { binding = FragmentAlarmBinding.inflate(inflater, container, false) return binding.root } @@ -74,71 +88,76 @@ class AlarmFragment : Fragment(), ToggleAlarmInterface { setupAlarms() } - private fun setupAlarms() { - alarms = context?.dbHelper?.getAlarms() ?: return - - when (requireContext().config.alarmSort) { - SORT_BY_ALARM_TIME -> alarms.sortBy { it.timeInMinutes } - SORT_BY_DATE_CREATED -> alarms.sortBy { it.id } - SORT_BY_DATE_AND_TIME -> alarms.sortWith(compareBy { - requireContext().firstDayOrder(it.days) - }.thenBy { - it.timeInMinutes - }) - - SORT_BY_CUSTOM -> { - val customAlarmsSortOrderString = activity?.config?.alarmsCustomSorting - if (customAlarmsSortOrderString == "") { - alarms.sortBy { it.id } - } else { - val customAlarmsSortOrder: List = customAlarmsSortOrderString?.split(", ")?.map { it.toInt() }!! - val alarmsIdValueMap = alarms.associateBy { it.id } - - val sortedAlarms: ArrayList = ArrayList() - customAlarmsSortOrder.map { id -> - if (alarmsIdValueMap[id] != null) { - sortedAlarms.add(alarmsIdValueMap[id] as Alarm) - } - } - - alarms = (sortedAlarms + alarms.filter { it !in sortedAlarms }) as ArrayList + private fun getSortedAlarms(callback: (alarms: ArrayList) -> Unit) { + val safeContext = context ?: return + ensureBackgroundThread { + var newAlarms = context?.dbHelper?.getAlarms() + if (newAlarms == null) { + activity?.runOnUiThread { + callback(arrayListOf()) } + return@ensureBackgroundThread } - } - context?.getEnabledAlarms { enabledAlarms -> - if (enabledAlarms.isNullOrEmpty()) { - val removedAlarms = mutableListOf() - alarms.forEach { - if (it.days == TODAY_BIT && it.isEnabled && it.timeInMinutes <= getCurrentDayMinutes()) { - it.isEnabled = false - ensureBackgroundThread { - if (it.oneShot) { - it.isEnabled = false - context?.dbHelper?.deleteAlarms(arrayListOf(it)) - removedAlarms.add(it) - } else { - context?.dbHelper?.updateAlarmEnabledState(it.id, false) + + when (safeContext.config.alarmSort) { + SORT_BY_ALARM_TIME -> newAlarms.sortBy { it.timeInMinutes } + SORT_BY_DATE_CREATED -> newAlarms.sortBy { it.id } + SORT_BY_DATE_AND_TIME -> newAlarms.sortWith(compareBy { + safeContext.firstDayOrder(it.days) + }.thenBy { + it.timeInMinutes + }) + + SORT_BY_CUSTOM -> { + val customAlarmsSortOrderString = activity?.config?.alarmsCustomSorting + if (customAlarmsSortOrderString == "") { + newAlarms.sortBy { it.id } + } else { + val customAlarmsSortOrder: List = + customAlarmsSortOrderString?.split(", ")?.map { it.toInt() }!! + val alarmsIdValueMap = newAlarms.associateBy { it.id } + + val sortedAlarms: ArrayList = ArrayList() + customAlarmsSortOrder.map { id -> + if (alarmsIdValueMap[id] != null) { + sortedAlarms.add(alarmsIdValueMap[id] as Alarm) } } + + newAlarms = + (sortedAlarms + newAlarms.filter { it !in sortedAlarms }) as ArrayList } } - alarms.removeAll(removedAlarms) } - } - val currAdapter = binding.alarmsList.adapter - if (currAdapter == null) { - AlarmsAdapter(activity as SimpleActivity, alarms, this, binding.alarmsList) { - openEditAlarm(it as Alarm) - }.apply { - binding.alarmsList.adapter = this + activity?.runOnUiThread { + callback(newAlarms) } - } else { - (currAdapter as AlarmsAdapter).apply { - updatePrimaryColor() - updateBackgroundColor(requireContext().getProperBackgroundColor()) - updateTextColor(requireContext().getProperTextColor()) - updateItems(this@AlarmFragment.alarms) + } + } + + private fun setupAlarms() { + getSortedAlarms { sortedAlarms -> + alarms = sortedAlarms + var currAdapter = binding.alarmsList.adapter as? AlarmsAdapter + if (currAdapter == null) { + currAdapter = AlarmsAdapter( + activity = activity as SimpleActivity, + alarms = alarms, + toggleAlarmInterface = this, + recyclerView = binding.alarmsList + ) { + openEditAlarm(it as Alarm) + }.apply { + binding.alarmsList.adapter = this + } + } else { + currAdapter.apply { + updatePrimaryColor() + updateBackgroundColor(requireContext().getProperBackgroundColor()) + updateTextColor(requireContext().getProperTextColor()) + updateItems(alarms) + } } } } @@ -156,7 +175,8 @@ class AlarmFragment : Fragment(), ToggleAlarmInterface { (activity as SimpleActivity).handleFullScreenNotificationsPermission { granted -> if (granted) { if (requireContext().dbHelper.updateAlarmEnabledState(id, isEnabled)) { - val alarm = alarms.firstOrNull { it.id == id } ?: return@handleFullScreenNotificationsPermission + val alarm = alarms.firstOrNull { it.id == id } + ?: return@handleFullScreenNotificationsPermission alarm.isEnabled = isEnabled checkAlarmState(alarm) if (!alarm.isEnabled && alarm.oneShot) { diff --git a/app/src/main/kotlin/org/fossify/clock/fragments/ClockFragment.kt b/app/src/main/kotlin/org/fossify/clock/fragments/ClockFragment.kt index 67470fa8..c3e1432c 100644 --- a/app/src/main/kotlin/org/fossify/clock/fragments/ClockFragment.kt +++ b/app/src/main/kotlin/org/fossify/clock/fragments/ClockFragment.kt @@ -2,7 +2,7 @@ package org.fossify.clock.fragments import android.os.Bundle import android.os.Handler -import android.text.format.DateFormat +import android.os.Looper import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -13,7 +13,11 @@ import org.fossify.clock.adapters.TimeZonesAdapter import org.fossify.clock.databinding.FragmentClockBinding import org.fossify.clock.dialogs.AddTimeZonesDialog import org.fossify.clock.dialogs.EditTimeZoneDialog -import org.fossify.clock.extensions.* +import org.fossify.clock.extensions.colorCompoundDrawable +import org.fossify.clock.extensions.config +import org.fossify.clock.extensions.getAllTimeZonesModified +import org.fossify.clock.extensions.getClosestEnabledAlarmString +import org.fossify.clock.extensions.getFormattedDate import org.fossify.clock.helpers.FORMAT_12H_WITH_SECONDS import org.fossify.clock.helpers.FORMAT_24H_WITH_SECONDS import org.fossify.clock.helpers.getPassedSeconds @@ -29,11 +33,15 @@ class ClockFragment : Fragment() { private var passedSeconds = 0 private var calendar = Calendar.getInstance() - private val updateHandler = Handler() + private val updateHandler = Handler(Looper.getMainLooper()) private lateinit var binding: FragmentClockBinding - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { binding = FragmentClockBinding.inflate(inflater, container, false) return binding.root } @@ -42,7 +50,8 @@ class ClockFragment : Fragment() { super.onResume() setupDateTime() - binding.clockDate.setTextColor(requireContext().getProperTextColor()) + val safeContext = context ?: return + binding.clockDate.setTextColor(safeContext.getProperTextColor()) } override fun onPause() { @@ -60,10 +69,16 @@ class ClockFragment : Fragment() { } private fun setupViews() { + val safeContext = context ?: return binding.apply { - requireContext().updateTextColors(clockFragment) - clockTime.setTextColor(requireContext().getProperTextColor()) - val clockFormat = if (requireActivity().config.use24HourFormat) FORMAT_24H_WITH_SECONDS else FORMAT_12H_WITH_SECONDS + safeContext.updateTextColors(clockFragment) + clockTime.setTextColor(safeContext.getProperTextColor()) + val clockFormat = if (safeContext.config.use24HourFormat) { + FORMAT_24H_WITH_SECONDS + } else { + FORMAT_12H_WITH_SECONDS + } + clockTime.format24Hour = clockFormat clockTime.format12Hour = clockFormat clockFab.setOnClickListener { @@ -79,8 +94,10 @@ class ClockFragment : Fragment() { val minutes = (passedSeconds / 60) % 60 val seconds = passedSeconds % 60 - if (!requireActivity().config.use24HourFormat) { - binding.clockTime.textSize = resources.getDimension(R.dimen.clock_text_size_smaller) / resources.displayMetrics.density + val safeContext = context ?: return + if (!safeContext.config.use24HourFormat) { + binding.clockTime.textSize = + resources.getDimension(R.dimen.clock_text_size_smaller) / resources.displayMetrics.density } if (seconds == 0) { @@ -104,28 +121,31 @@ class ClockFragment : Fragment() { } fun updateAlarm() { - context?.getClosestEnabledAlarmString { nextAlarm -> + val safeContext = context ?: return + safeContext.getClosestEnabledAlarmString { nextAlarm -> binding.apply { clockAlarm.beVisibleIf(nextAlarm.isNotEmpty()) clockAlarm.text = nextAlarm - clockAlarm.colorCompoundDrawable(requireContext().getProperTextColor()) + clockAlarm.colorCompoundDrawable(safeContext.getProperTextColor()) } } } private fun updateTimeZones() { - val selectedTimeZones = context?.config?.selectedTimeZones ?: return + val safeContext = activity as? SimpleActivity ?: return + val selectedTimeZones = safeContext.config.selectedTimeZones binding.timeZonesList.beVisibleIf(selectedTimeZones.isNotEmpty()) if (selectedTimeZones.isEmpty()) { return } val selectedTimeZoneIDs = selectedTimeZones.map { it.toInt() } - val timeZones = requireContext().getAllTimeZonesModified().filter { selectedTimeZoneIDs.contains(it.id) } as ArrayList + val timeZones = safeContext.getAllTimeZonesModified() + .filter { selectedTimeZoneIDs.contains(it.id) } as ArrayList val currAdapter = binding.timeZonesList.adapter if (currAdapter == null) { - TimeZonesAdapter(activity as SimpleActivity, timeZones, binding.timeZonesList) { - EditTimeZoneDialog(activity as SimpleActivity, it as MyTimeZone) { + TimeZonesAdapter(safeContext, timeZones, binding.timeZonesList) { + EditTimeZoneDialog(safeContext, it as MyTimeZone) { updateTimeZones() } }.apply { @@ -134,15 +154,16 @@ class ClockFragment : Fragment() { } else { (currAdapter as TimeZonesAdapter).apply { updatePrimaryColor() - updateBackgroundColor(requireContext().getProperBackgroundColor()) - updateTextColor(requireContext().getProperTextColor()) + updateBackgroundColor(safeContext.getProperBackgroundColor()) + updateTextColor(safeContext.getProperTextColor()) updateItems(timeZones) } } } private fun fabClicked() { - AddTimeZonesDialog(activity as SimpleActivity) { + val safeContext = activity as? SimpleActivity ?: return + AddTimeZonesDialog(safeContext) { updateTimeZones() } } diff --git a/app/src/main/kotlin/org/fossify/clock/receivers/AlarmReceiver.kt b/app/src/main/kotlin/org/fossify/clock/receivers/AlarmReceiver.kt index bf5b39af..a0799fb9 100644 --- a/app/src/main/kotlin/org/fossify/clock/receivers/AlarmReceiver.kt +++ b/app/src/main/kotlin/org/fossify/clock/receivers/AlarmReceiver.kt @@ -17,6 +17,7 @@ import org.fossify.clock.R import org.fossify.clock.activities.ReminderActivity import org.fossify.clock.extensions.config import org.fossify.clock.extensions.dbHelper +import org.fossify.clock.extensions.disableExpiredAlarm import org.fossify.clock.extensions.hideNotification import org.fossify.clock.extensions.isScreenOn import org.fossify.clock.extensions.showAlarmNotification @@ -41,6 +42,7 @@ class AlarmReceiver : BroadcastReceiver() { context.showAlarmNotification(alarm) Handler(Looper.getMainLooper()).postDelayed({ context.hideNotification(id) + context.disableExpiredAlarm(alarm) }, context.config.alarmMaxReminderSecs * 1000L) } else { if (isOreoPlus()) { diff --git a/app/src/main/kotlin/org/fossify/clock/receivers/DismissAlarmReceiver.kt b/app/src/main/kotlin/org/fossify/clock/receivers/DismissAlarmReceiver.kt index 8bf5076c..80e9f0a1 100644 --- a/app/src/main/kotlin/org/fossify/clock/receivers/DismissAlarmReceiver.kt +++ b/app/src/main/kotlin/org/fossify/clock/receivers/DismissAlarmReceiver.kt @@ -3,7 +3,11 @@ package org.fossify.clock.receivers import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import org.fossify.clock.extensions.* +import org.fossify.clock.extensions.cancelAlarmClock +import org.fossify.clock.extensions.disableExpiredAlarm +import org.fossify.clock.extensions.dbHelper +import org.fossify.clock.extensions.hideNotification +import org.fossify.clock.extensions.scheduleNextAlarm import org.fossify.clock.helpers.ALARM_ID import org.fossify.clock.helpers.NOTIFICATION_ID import org.fossify.clock.models.Alarm @@ -26,15 +30,7 @@ class DismissAlarmReceiver : BroadcastReceiver() { context.dbHelper.getAlarmWithId(alarmId)?.let { alarm -> context.cancelAlarmClock(alarm) scheduleNextAlarm(alarm, context) - if (alarm.days < 0) { - if (alarm.oneShot) { - alarm.isEnabled = false - context.dbHelper.deleteAlarms(arrayListOf(alarm)) - } else { - context.dbHelper.updateAlarmEnabledState(alarm.id, false) - } - context.updateWidgets() - } + context.disableExpiredAlarm(alarm) } } } diff --git a/app/src/main/kotlin/org/fossify/clock/receivers/HideAlarmReceiver.kt b/app/src/main/kotlin/org/fossify/clock/receivers/HideAlarmReceiver.kt index 257f1ec5..0edd6dac 100644 --- a/app/src/main/kotlin/org/fossify/clock/receivers/HideAlarmReceiver.kt +++ b/app/src/main/kotlin/org/fossify/clock/receivers/HideAlarmReceiver.kt @@ -3,10 +3,10 @@ package org.fossify.clock.receivers import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import org.fossify.clock.extensions.disableExpiredAlarm import org.fossify.clock.extensions.dbHelper import org.fossify.clock.extensions.deleteNotificationChannel import org.fossify.clock.extensions.hideNotification -import org.fossify.clock.extensions.updateWidgets import org.fossify.clock.helpers.ALARM_ID import org.fossify.clock.helpers.ALARM_NOTIFICATION_CHANNEL_ID import org.fossify.commons.helpers.ensureBackgroundThread @@ -20,14 +20,8 @@ class HideAlarmReceiver : BroadcastReceiver() { ensureBackgroundThread { val alarm = context.dbHelper.getAlarmWithId(id) - if (alarm != null && alarm.days < 0) { - if (alarm.oneShot) { - alarm.isEnabled = false - context.dbHelper.deleteAlarms(arrayListOf(alarm)) - } else { - context.dbHelper.updateAlarmEnabledState(alarm.id, false) - } - context.updateWidgets() + if (alarm != null) { + context.disableExpiredAlarm(alarm) } } }