Fix various issues regarding Notification Profile scheduling.

- Timezone conversion when detecting scheduled profile
- Not automatically enabling a scheduled profile on creation regardless
  of when other profiles were enabled/disabled
fork-5.53.8
Cody Henthorne 2021-12-08 17:28:36 -05:00
rodzic 372b0d9f2b
commit a8a104242a
11 zmienionych plików z 126 dodań i 104 usunięć

Wyświetl plik

@ -1,10 +1,10 @@
package org.thoughtcrime.securesms.components.settings.app.notifications.manual package org.thoughtcrime.securesms.components.settings.app.notifications.manual
import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile
import java.util.Calendar import java.time.LocalDateTime
data class NotificationProfileSelectionState( data class NotificationProfileSelectionState(
val notificationProfiles: List<NotificationProfile> = listOf(), val notificationProfiles: List<NotificationProfile> = listOf(),
val expandedId: Long = -1L, val expandedId: Long = -1L,
val timeSlotB: Calendar val timeSlotB: LocalDateTime
) )

Wyświetl plik

@ -9,8 +9,10 @@ import io.reactivex.rxjava3.kotlin.plusAssign
import io.reactivex.rxjava3.kotlin.subscribeBy import io.reactivex.rxjava3.kotlin.subscribeBy
import org.thoughtcrime.securesms.components.settings.app.notifications.profiles.NotificationProfilesRepository import org.thoughtcrime.securesms.components.settings.app.notifications.profiles.NotificationProfilesRepository
import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile
import org.thoughtcrime.securesms.util.isBetween
import org.thoughtcrime.securesms.util.livedata.Store import org.thoughtcrime.securesms.util.livedata.Store
import java.util.Calendar import org.thoughtcrime.securesms.util.toMillis
import java.time.LocalDateTime
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class NotificationProfileSelectionViewModel(private val repository: NotificationProfilesRepository) : ViewModel() { class NotificationProfileSelectionViewModel(private val repository: NotificationProfilesRepository) : ViewModel() {
@ -53,29 +55,24 @@ class NotificationProfileSelectionViewModel(private val repository: Notification
.subscribe() .subscribe()
} }
fun enableUntil(profile: NotificationProfile, calendar: Calendar) { fun enableUntil(profile: NotificationProfile, enableUntil: LocalDateTime) {
disposables += repository.manuallyEnableProfileForDuration(profile.id, calendar.timeInMillis) disposables += repository.manuallyEnableProfileForDuration(profile.id, enableUntil.toMillis())
.subscribe() .subscribe()
} }
companion object { companion object {
private fun getTimeSlotB(): Calendar { @Suppress("CascadeIf")
val now = Calendar.getInstance() private fun getTimeSlotB(): LocalDateTime {
val sixPm = Calendar.getInstance() val now = LocalDateTime.now()
val eightAm = Calendar.getInstance() val sixPm = now.withHour(18).withMinute(0).withSecond(0)
val eightAm = now.withHour(8).withMinute(0).withSecond(0)
sixPm.set(Calendar.HOUR_OF_DAY, 18) return if (now.isBetween(eightAm, sixPm)) {
sixPm.set(Calendar.MINUTE, 0)
sixPm.set(Calendar.SECOND, 0)
eightAm.set(Calendar.HOUR_OF_DAY, 8)
eightAm.set(Calendar.MINUTE, 0)
eightAm.set(Calendar.SECOND, 0)
return if (now.before(sixPm) && (now.after(eightAm) || now == eightAm)) {
sixPm sixPm
} else { } else if (now.isBefore(eightAm)) {
eightAm eightAm
} else {
eightAm.plusDays(1)
} }
} }
} }

Wyświetl plik

@ -9,12 +9,12 @@ import org.thoughtcrime.securesms.components.emoji.EmojiImageView
import org.thoughtcrime.securesms.components.settings.DSLSettingsText import org.thoughtcrime.securesms.components.settings.DSLSettingsText
import org.thoughtcrime.securesms.components.settings.PreferenceModel import org.thoughtcrime.securesms.components.settings.PreferenceModel
import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile
import org.thoughtcrime.securesms.util.DateUtils
import org.thoughtcrime.securesms.util.MappingAdapter import org.thoughtcrime.securesms.util.MappingAdapter
import org.thoughtcrime.securesms.util.MappingViewHolder import org.thoughtcrime.securesms.util.MappingViewHolder
import org.thoughtcrime.securesms.util.formatHours
import org.thoughtcrime.securesms.util.visible import org.thoughtcrime.securesms.util.visible
import java.util.Calendar import java.time.LocalDateTime
import java.util.Locale import java.time.LocalTime
/** /**
* Notification Profile selection preference. * Notification Profile selection preference.
@ -34,10 +34,10 @@ object NotificationProfileSelection {
override val summary: DSLSettingsText, override val summary: DSLSettingsText,
val notificationProfile: NotificationProfile, val notificationProfile: NotificationProfile,
val isExpanded: Boolean, val isExpanded: Boolean,
val timeSlotB: Calendar, val timeSlotB: LocalDateTime,
val onRowClick: (NotificationProfile) -> Unit, val onRowClick: (NotificationProfile) -> Unit,
val onTimeSlotAClick: (NotificationProfile) -> Unit, val onTimeSlotAClick: (NotificationProfile) -> Unit,
val onTimeSlotBClick: (NotificationProfile, Calendar) -> Unit, val onTimeSlotBClick: (NotificationProfile, LocalDateTime) -> Unit,
val onViewSettingsClick: (NotificationProfile) -> Unit, val onViewSettingsClick: (NotificationProfile) -> Unit,
val onToggleClick: (NotificationProfile) -> Unit val onToggleClick: (NotificationProfile) -> Unit
) : PreferenceModel<Entry>() { ) : PreferenceModel<Entry>() {
@ -87,7 +87,7 @@ object NotificationProfileSelection {
expansion.visible = model.isExpanded expansion.visible = model.isExpanded
timeSlotB.text = context.getString( timeSlotB.text = context.getString(
R.string.NotificationProfileSelection__until_s, R.string.NotificationProfileSelection__until_s,
DateUtils.getTimeString(context, Locale.getDefault(), model.timeSlotB.timeInMillis) LocalTime.from(model.timeSlotB).formatHours()
) )
if (TOGGLE_EXPANSION in payload || UPDATE_TIMESLOT in payload) { if (TOGGLE_EXPANSION in payload || UPDATE_TIMESLOT in payload) {
@ -107,7 +107,7 @@ object NotificationProfileSelection {
timeSlotB.text = context.getString( timeSlotB.text = context.getString(
R.string.NotificationProfileSelection__until_s, R.string.NotificationProfileSelection__until_s,
DateUtils.getTimeString(context, Locale.getDefault(), model.timeSlotB.timeInMillis) LocalTime.from(model.timeSlotB).formatHours()
) )
itemView.isSelected = model.isOn itemView.isSelected = model.isOn

Wyświetl plik

@ -35,7 +35,7 @@ import java.time.format.DateTimeFormatter
*/ */
class EditNotificationProfileScheduleFragment : LoggingFragment(R.layout.fragment_edit_notification_profile_schedule) { class EditNotificationProfileScheduleFragment : LoggingFragment(R.layout.fragment_edit_notification_profile_schedule) {
private val viewModel: EditNotificationProfileScheduleViewModel by viewModels(factoryProducer = { EditNotificationProfileScheduleViewModel.Factory(profileId) }) private val viewModel: EditNotificationProfileScheduleViewModel by viewModels(factoryProducer = { EditNotificationProfileScheduleViewModel.Factory(profileId, createMode) })
private val lifecycleDisposable = LifecycleDisposable() private val lifecycleDisposable = LifecycleDisposable()
private val profileId: Long by lazy { EditNotificationProfileScheduleFragmentArgs.fromBundle(requireArguments()).profileId } private val profileId: Long by lazy { EditNotificationProfileScheduleFragmentArgs.fromBundle(requireArguments()).profileId }

Wyświetl plik

@ -17,7 +17,8 @@ import java.time.DayOfWeek
* from the database and into the [scheduleSubject] allowing the safe use of !! with [schedule]. * from the database and into the [scheduleSubject] allowing the safe use of !! with [schedule].
*/ */
class EditNotificationProfileScheduleViewModel( class EditNotificationProfileScheduleViewModel(
profileId: Long, private val profileId: Long,
private val createMode: Boolean,
private val repository: NotificationProfilesRepository private val repository: NotificationProfilesRepository
) : ViewModel() { ) : ViewModel() {
@ -72,14 +73,23 @@ class EditNotificationProfileScheduleViewModel(
} else if (createMode && !schedule.enabled) { } else if (createMode && !schedule.enabled) {
Single.just(SaveScheduleResult.Success) Single.just(SaveScheduleResult.Success)
} else { } else {
repository.updateSchedule(schedule).toSingle { SaveScheduleResult.Success } repository.updateSchedule(schedule)
.toSingleDefault(SaveScheduleResult.Success)
.flatMap { r ->
if (createMode && schedule.enabled && schedule.coversTime(System.currentTimeMillis())) {
repository.manuallyToggleProfile(profileId, schedule)
.toSingleDefault(r)
} else {
Single.just(r)
}
}
} }
return result.observeOn(AndroidSchedulers.mainThread()) return result.observeOn(AndroidSchedulers.mainThread())
} }
class Factory(private val profileId: Long) : ViewModelProvider.Factory { class Factory(private val profileId: Long, private val createMode: Boolean) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T { override fun <T : ViewModel> create(modelClass: Class<T>): T {
return modelClass.cast(EditNotificationProfileScheduleViewModel(profileId, NotificationProfilesRepository()))!! return modelClass.cast(EditNotificationProfileScheduleViewModel(profileId, createMode, NotificationProfilesRepository()))!!
} }
} }

Wyświetl plik

@ -96,19 +96,23 @@ class NotificationProfilesRepository {
} }
fun manuallyToggleProfile(profile: NotificationProfile, now: Long = System.currentTimeMillis()): Completable { fun manuallyToggleProfile(profile: NotificationProfile, now: Long = System.currentTimeMillis()): Completable {
return manuallyToggleProfile(profile.id, profile.schedule, now)
}
fun manuallyToggleProfile(profileId: Long, schedule: NotificationProfileSchedule, now: Long = System.currentTimeMillis()): Completable {
return Completable.fromAction { return Completable.fromAction {
val profiles = database.getProfiles() val profiles = database.getProfiles()
val activeProfile = NotificationProfiles.getActiveProfile(profiles, now) val activeProfile = NotificationProfiles.getActiveProfile(profiles, now)
if (profile.id == activeProfile?.id) { if (profileId == activeProfile?.id) {
SignalStore.notificationProfileValues().manuallyEnabledProfile = 0 SignalStore.notificationProfileValues().manuallyEnabledProfile = 0
SignalStore.notificationProfileValues().manuallyEnabledUntil = 0 SignalStore.notificationProfileValues().manuallyEnabledUntil = 0
SignalStore.notificationProfileValues().manuallyDisabledAt = now SignalStore.notificationProfileValues().manuallyDisabledAt = now
SignalStore.notificationProfileValues().lastProfilePopup = 0 SignalStore.notificationProfileValues().lastProfilePopup = 0
SignalStore.notificationProfileValues().lastProfilePopupTime = 0 SignalStore.notificationProfileValues().lastProfilePopupTime = 0
} else { } else {
val inScheduledWindow = profile.schedule.isCurrentlyActive(now) val inScheduledWindow = schedule.isCurrentlyActive(now)
SignalStore.notificationProfileValues().manuallyEnabledProfile = if (inScheduledWindow) 0 else profile.id SignalStore.notificationProfileValues().manuallyEnabledProfile = if (inScheduledWindow) 0 else profileId
SignalStore.notificationProfileValues().manuallyEnabledUntil = if (inScheduledWindow) 0 else Long.MAX_VALUE SignalStore.notificationProfileValues().manuallyEnabledUntil = if (inScheduledWindow) 0 else Long.MAX_VALUE
SignalStore.notificationProfileValues().manuallyDisabledAt = if (inScheduledWindow) 0 else now SignalStore.notificationProfileValues().manuallyDisabledAt = if (inScheduledWindow) 0 else now
} }

Wyświetl plik

@ -8,6 +8,7 @@ import org.thoughtcrime.securesms.util.formatHours
import org.thoughtcrime.securesms.util.toLocalDateTime import org.thoughtcrime.securesms.util.toLocalDateTime
import org.thoughtcrime.securesms.util.toLocalTime import org.thoughtcrime.securesms.util.toLocalTime
import org.thoughtcrime.securesms.util.toMillis import org.thoughtcrime.securesms.util.toMillis
import org.thoughtcrime.securesms.util.toOffset
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.ZoneId import java.time.ZoneId
@ -26,7 +27,7 @@ object NotificationProfiles {
val manualProfile: NotificationProfile? = profiles.firstOrNull { it.id == storeValues.manuallyEnabledProfile } val manualProfile: NotificationProfile? = profiles.firstOrNull { it.id == storeValues.manuallyEnabledProfile }
val scheduledProfile: NotificationProfile? = profiles.sortedDescending().filter { it.schedule.isCurrentlyActive(now, zoneId) }.firstOrNull { profile -> val scheduledProfile: NotificationProfile? = profiles.sortedDescending().filter { it.schedule.isCurrentlyActive(now, zoneId) }.firstOrNull { profile ->
profile.schedule.startDateTime(localNow).toMillis() > storeValues.manuallyDisabledAt profile.schedule.startDateTime(localNow).toMillis(zoneId.toOffset()) > storeValues.manuallyDisabledAt
} }
if (manualProfile == null || scheduledProfile == null) { if (manualProfile == null || scheduledProfile == null) {

Wyświetl plik

@ -3,17 +3,25 @@ package org.thoughtcrime.securesms.util
import java.time.Instant import java.time.Instant
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.LocalTime import java.time.LocalTime
import java.time.OffsetDateTime
import java.time.ZoneId import java.time.ZoneId
import java.time.ZoneOffset import java.time.ZoneOffset
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle import java.time.format.FormatStyle
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
/**
* Given a [ZoneId] return the time offset as a [ZoneOffset].
*/
fun ZoneId.toOffset(): ZoneOffset {
return OffsetDateTime.now(this).offset
}
/** /**
* Convert [LocalDateTime] to be same as [System.currentTimeMillis] * Convert [LocalDateTime] to be same as [System.currentTimeMillis]
*/ */
fun LocalDateTime.toMillis(): Long { fun LocalDateTime.toMillis(zoneOffset: ZoneOffset = ZoneId.systemDefault().toOffset()): Long {
return TimeUnit.SECONDS.toMillis(toEpochSecond(ZoneOffset.UTC)) return TimeUnit.SECONDS.toMillis(toEpochSecond(zoneOffset))
} }
/** /**

Wyświetl plik

@ -411,7 +411,7 @@
</plurals> </plurals>
<!-- Show in conversation list overflow menu to open selection bottom sheet --> <!-- Show in conversation list overflow menu to open selection bottom sheet -->
<string name="ConversationListFragment__notification_profile">Notification Profile</string> <string name="ConversationListFragment__notification_profile">Notification profile</string>
<!-- Tooltip shown after you have created your first notification profile --> <!-- Tooltip shown after you have created your first notification profile -->
<string name="ConversationListFragment__turn_your_notification_profile_on_or_off_here">Turn your notification profile on or off here.</string> <string name="ConversationListFragment__turn_your_notification_profile_on_or_off_here">Turn your notification profile on or off here.</string>
<!-- Message shown in top toast to indicate the named profile is on --> <!-- Message shown in top toast to indicate the named profile is on -->

Wyświetl plik

@ -7,6 +7,7 @@ import org.junit.Test
import org.thoughtcrime.securesms.util.toMillis import org.thoughtcrime.securesms.util.toMillis
import java.time.DayOfWeek import java.time.DayOfWeek
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.ZoneOffset
import java.util.TimeZone import java.util.TimeZone
class NotificationProfileScheduleTest { class NotificationProfileScheduleTest {
@ -37,105 +38,105 @@ class NotificationProfileScheduleTest {
fun `when time is within enabled schedule 9am to 5pm then return true`() { fun `when time is within enabled schedule 9am to 5pm then return true`() {
val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 900, end = 1700, daysEnabled = setOf(DayOfWeek.SUNDAY)) val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 900, end = 1700, daysEnabled = setOf(DayOfWeek.SUNDAY))
assertTrue(schedule.isCurrentlyActive(sunday9am.toMillis())) assertTrue(schedule.isCurrentlyActive(sunday9am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(sunday9am.plusHours(1).toMillis())) assertTrue(schedule.isCurrentlyActive(sunday9am.plusHours(1).toMillis(ZoneOffset.UTC)))
} }
@Test @Test
fun `when time is outside enabled schedule 9am to 5pm then return false`() { fun `when time is outside enabled schedule 9am to 5pm then return false`() {
val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 900, end = 1700, daysEnabled = setOf(DayOfWeek.SUNDAY)) val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 900, end = 1700, daysEnabled = setOf(DayOfWeek.SUNDAY))
assertFalse(schedule.isCurrentlyActive(sunday1am.toMillis())) assertFalse(schedule.isCurrentlyActive(sunday1am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(sunday10pm.toMillis())) assertFalse(schedule.isCurrentlyActive(sunday10pm.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday1am.toMillis())) assertFalse(schedule.isCurrentlyActive(monday1am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday9am.toMillis())) assertFalse(schedule.isCurrentlyActive(monday9am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday10pm.toMillis())) assertFalse(schedule.isCurrentlyActive(monday10pm.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(tuesday9am.toMillis())) assertFalse(schedule.isCurrentlyActive(tuesday9am.toMillis(ZoneOffset.UTC)))
} }
@Test @Test
fun `when time is inside enabled with day wrapping schedule 10pm to 2am then return true`() { fun `when time is inside enabled with day wrapping schedule 10pm to 2am then return true`() {
val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 2100, end = 200, daysEnabled = setOf(DayOfWeek.MONDAY)) val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 2100, end = 200, daysEnabled = setOf(DayOfWeek.MONDAY))
assertTrue(schedule.isCurrentlyActive(monday10pm.toMillis())) assertTrue(schedule.isCurrentlyActive(monday10pm.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(tuesday1am.toMillis())) assertTrue(schedule.isCurrentlyActive(tuesday1am.toMillis(ZoneOffset.UTC)))
} }
@Test @Test
fun `when time is outside enabled with day wrapping schedule 10pm to 2am then return false`() { fun `when time is outside enabled with day wrapping schedule 10pm to 2am then return false`() {
val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 2100, end = 200, daysEnabled = setOf(DayOfWeek.MONDAY)) val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 2100, end = 200, daysEnabled = setOf(DayOfWeek.MONDAY))
assertFalse(schedule.isCurrentlyActive(sunday1am.toMillis())) assertFalse(schedule.isCurrentlyActive(sunday1am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(sunday10pm.toMillis())) assertFalse(schedule.isCurrentlyActive(sunday10pm.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday1am.toMillis())) assertFalse(schedule.isCurrentlyActive(monday1am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday9am.toMillis())) assertFalse(schedule.isCurrentlyActive(monday9am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(tuesday9am.toMillis())) assertFalse(schedule.isCurrentlyActive(tuesday9am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(tuesday10pm.toMillis())) assertFalse(schedule.isCurrentlyActive(tuesday10pm.toMillis(ZoneOffset.UTC)))
} }
@Test @Test
fun `when time is inside enabled schedule 12am to 10am then return false`() { fun `when time is inside enabled schedule 12am to 10am then return false`() {
val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 0, end = 1000, daysEnabled = setOf(DayOfWeek.SUNDAY)) val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 0, end = 1000, daysEnabled = setOf(DayOfWeek.SUNDAY))
assertTrue(schedule.isCurrentlyActive(sunday0am.toMillis())) assertTrue(schedule.isCurrentlyActive(sunday0am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(sunday1am.toMillis())) assertTrue(schedule.isCurrentlyActive(sunday1am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(sunday9am.toMillis())) assertTrue(schedule.isCurrentlyActive(sunday9am.toMillis(ZoneOffset.UTC)))
} }
@Test @Test
fun `when time is inside enabled schedule 12am to 12am then return false`() { fun `when time is inside enabled schedule 12am to 12am then return false`() {
val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 0, end = 2400, daysEnabled = setOf(DayOfWeek.SUNDAY)) val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 0, end = 2400, daysEnabled = setOf(DayOfWeek.SUNDAY))
assertTrue(schedule.isCurrentlyActive(sunday0am.toMillis())) assertTrue(schedule.isCurrentlyActive(sunday0am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(sunday1am.toMillis())) assertTrue(schedule.isCurrentlyActive(sunday1am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(sunday9am.toMillis())) assertTrue(schedule.isCurrentlyActive(sunday9am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(sunday10pm.toMillis())) assertTrue(schedule.isCurrentlyActive(sunday10pm.toMillis(ZoneOffset.UTC)))
} }
@Test @Test
fun `when time is outside enabled schedule 12am to 12am then return false`() { fun `when time is outside enabled schedule 12am to 12am then return false`() {
val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 0, end = 2400, daysEnabled = setOf(DayOfWeek.SUNDAY)) val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 0, end = 2400, daysEnabled = setOf(DayOfWeek.SUNDAY))
assertFalse(schedule.isCurrentlyActive(monday0am.toMillis())) assertFalse(schedule.isCurrentlyActive(monday0am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday1am.toMillis())) assertFalse(schedule.isCurrentlyActive(monday1am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday9am.toMillis())) assertFalse(schedule.isCurrentlyActive(monday9am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday10pm.toMillis())) assertFalse(schedule.isCurrentlyActive(monday10pm.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(tuesday1am.toMillis())) assertFalse(schedule.isCurrentlyActive(tuesday1am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(tuesday9am.toMillis())) assertFalse(schedule.isCurrentlyActive(tuesday9am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(tuesday10pm.toMillis())) assertFalse(schedule.isCurrentlyActive(tuesday10pm.toMillis(ZoneOffset.UTC)))
} }
@Test @Test
fun `when enabled schedule 12am to 12am for all days then return true`() { fun `when enabled schedule 12am to 12am for all days then return true`() {
val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 0, end = 2400, daysEnabled = DayOfWeek.values().toSet()) val schedule = NotificationProfileSchedule(id = 1L, enabled = true, start = 0, end = 2400, daysEnabled = DayOfWeek.values().toSet())
assertTrue(schedule.isCurrentlyActive(sunday0am.toMillis())) assertTrue(schedule.isCurrentlyActive(sunday0am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(sunday1am.toMillis())) assertTrue(schedule.isCurrentlyActive(sunday1am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(sunday9am.toMillis())) assertTrue(schedule.isCurrentlyActive(sunday9am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(sunday10pm.toMillis())) assertTrue(schedule.isCurrentlyActive(sunday10pm.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(monday0am.toMillis())) assertTrue(schedule.isCurrentlyActive(monday0am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(monday1am.toMillis())) assertTrue(schedule.isCurrentlyActive(monday1am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(monday9am.toMillis())) assertTrue(schedule.isCurrentlyActive(monday9am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(monday10pm.toMillis())) assertTrue(schedule.isCurrentlyActive(monday10pm.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(tuesday1am.toMillis())) assertTrue(schedule.isCurrentlyActive(tuesday1am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(tuesday9am.toMillis())) assertTrue(schedule.isCurrentlyActive(tuesday9am.toMillis(ZoneOffset.UTC)))
assertTrue(schedule.isCurrentlyActive(tuesday10pm.toMillis())) assertTrue(schedule.isCurrentlyActive(tuesday10pm.toMillis(ZoneOffset.UTC)))
} }
@Test @Test
fun `when disabled schedule 12am to 12am for all days then return false`() { fun `when disabled schedule 12am to 12am for all days then return false`() {
val schedule = NotificationProfileSchedule(id = 1L, enabled = false, start = 0, end = 2400, daysEnabled = DayOfWeek.values().toSet()) val schedule = NotificationProfileSchedule(id = 1L, enabled = false, start = 0, end = 2400, daysEnabled = DayOfWeek.values().toSet())
assertFalse(schedule.isCurrentlyActive(sunday0am.toMillis())) assertFalse(schedule.isCurrentlyActive(sunday0am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(sunday1am.toMillis())) assertFalse(schedule.isCurrentlyActive(sunday1am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(sunday9am.toMillis())) assertFalse(schedule.isCurrentlyActive(sunday9am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(sunday10pm.toMillis())) assertFalse(schedule.isCurrentlyActive(sunday10pm.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday0am.toMillis())) assertFalse(schedule.isCurrentlyActive(monday0am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday1am.toMillis())) assertFalse(schedule.isCurrentlyActive(monday1am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday9am.toMillis())) assertFalse(schedule.isCurrentlyActive(monday9am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(monday10pm.toMillis())) assertFalse(schedule.isCurrentlyActive(monday10pm.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(tuesday1am.toMillis())) assertFalse(schedule.isCurrentlyActive(tuesday1am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(tuesday9am.toMillis())) assertFalse(schedule.isCurrentlyActive(tuesday9am.toMillis(ZoneOffset.UTC)))
assertFalse(schedule.isCurrentlyActive(tuesday10pm.toMillis())) assertFalse(schedule.isCurrentlyActive(tuesday10pm.toMillis(ZoneOffset.UTC)))
} }
} }

Wyświetl plik

@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.util.toMillis
import java.time.DayOfWeek import java.time.DayOfWeek
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.ZoneId import java.time.ZoneId
import java.time.ZoneOffset
@RunWith(RobolectricTestRunner::class) @RunWith(RobolectricTestRunner::class)
@Config(manifest = Config.NONE, application = Application::class) @Config(manifest = Config.NONE, application = Application::class)
@ -69,30 +70,30 @@ class NotificationProfilesTest {
fun `when first is scheduled and second is not manually enabled and now is within schedule return first`() { fun `when first is scheduled and second is not manually enabled and now is within schedule return first`() {
val schedule = NotificationProfileSchedule(id = 3L, true, start = 700, daysEnabled = setOf(DayOfWeek.SUNDAY)) val schedule = NotificationProfileSchedule(id = 3L, true, start = 700, daysEnabled = setOf(DayOfWeek.SUNDAY))
val profiles = listOf(first.copy(schedule = schedule), second) val profiles = listOf(first.copy(schedule = schedule), second)
assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(), utc), `is`(profiles[0])) assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(ZoneOffset.UTC), utc), `is`(profiles[0]))
} }
@Test @Test
fun `when first is scheduled and second is manually enabled forever within first's schedule then return second`() { fun `when first is scheduled and second is manually enabled forever within first's schedule then return second`() {
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_PROFILE, second.id) signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_PROFILE, second.id)
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_UNTIL, Long.MAX_VALUE) signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_UNTIL, Long.MAX_VALUE)
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis()) signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis(ZoneOffset.UTC))
val schedule = NotificationProfileSchedule(id = 3L, true, start = 700, daysEnabled = setOf(DayOfWeek.SUNDAY)) val schedule = NotificationProfileSchedule(id = 3L, true, start = 700, daysEnabled = setOf(DayOfWeek.SUNDAY))
val profiles = listOf(first.copy(schedule = schedule), second) val profiles = listOf(first.copy(schedule = schedule), second)
assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(), utc), `is`(profiles[1])) assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(ZoneOffset.UTC), utc), `is`(profiles[1]))
} }
@Test @Test
fun `when first is scheduled and second is manually enabled forever before first's schedule start then return first`() { fun `when first is scheduled and second is manually enabled forever before first's schedule start then return first`() {
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_PROFILE, second.id) signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_PROFILE, second.id)
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_UNTIL, Long.MAX_VALUE) signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_UNTIL, Long.MAX_VALUE)
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis()) signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis(ZoneOffset.UTC))
val schedule = NotificationProfileSchedule(id = 3L, true, start = 900, daysEnabled = setOf(DayOfWeek.SUNDAY)) val schedule = NotificationProfileSchedule(id = 3L, true, start = 900, daysEnabled = setOf(DayOfWeek.SUNDAY))
val profiles = listOf(first.copy(schedule = schedule), second) val profiles = listOf(first.copy(schedule = schedule), second)
assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday930am.toMillis(), utc), `is`(profiles[0])) assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday930am.toMillis(ZoneOffset.UTC), utc), `is`(profiles[0]))
} }
@Test @Test
@ -101,41 +102,41 @@ class NotificationProfilesTest {
val secondSchedule = NotificationProfileSchedule(id = 4L, true, start = 800, daysEnabled = setOf(DayOfWeek.SUNDAY)) val secondSchedule = NotificationProfileSchedule(id = 4L, true, start = 800, daysEnabled = setOf(DayOfWeek.SUNDAY))
val profiles = listOf(first.copy(schedule = firstSchedule), second.copy(schedule = secondSchedule)) val profiles = listOf(first.copy(schedule = firstSchedule), second.copy(schedule = secondSchedule))
assertThat("active profile is second", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(), utc), `is`(profiles[1])) assertThat("active profile is second", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(ZoneOffset.UTC), utc), `is`(profiles[1]))
} }
@Test @Test
fun `when first and second have overlapping schedules and first is created before second and first is manually enabled within overlapping schedule then return first`() { fun `when first and second have overlapping schedules and first is created before second and first is manually enabled within overlapping schedule then return first`() {
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_PROFILE, first.id) signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_PROFILE, first.id)
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_UNTIL, Long.MAX_VALUE) signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_UNTIL, Long.MAX_VALUE)
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis()) signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis(ZoneOffset.UTC))
val firstSchedule = NotificationProfileSchedule(id = 3L, true, start = 700, daysEnabled = setOf(DayOfWeek.SUNDAY)) val firstSchedule = NotificationProfileSchedule(id = 3L, true, start = 700, daysEnabled = setOf(DayOfWeek.SUNDAY))
val secondSchedule = NotificationProfileSchedule(id = 4L, true, start = 700, daysEnabled = setOf(DayOfWeek.SUNDAY)) val secondSchedule = NotificationProfileSchedule(id = 4L, true, start = 700, daysEnabled = setOf(DayOfWeek.SUNDAY))
val profiles = listOf(first.copy(schedule = firstSchedule), second.copy(schedule = secondSchedule)) val profiles = listOf(first.copy(schedule = firstSchedule), second.copy(schedule = secondSchedule))
assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(), utc), `is`(profiles[0])) assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(ZoneOffset.UTC), utc), `is`(profiles[0]))
} }
@Test @Test
fun `when profile is manually enabled for set time after schedule end and now is after schedule end but before manual then return profile`() { fun `when profile is manually enabled for set time after schedule end and now is after schedule end but before manual then return profile`() {
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_PROFILE, first.id) signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_PROFILE, first.id)
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_UNTIL, sunday930am.toMillis()) signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_UNTIL, sunday930am.toMillis(ZoneOffset.UTC))
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis()) signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis(ZoneOffset.UTC))
val schedule = NotificationProfileSchedule(id = 3L, true, start = 700, end = 845, daysEnabled = setOf(DayOfWeek.SUNDAY)) val schedule = NotificationProfileSchedule(id = 3L, true, start = 700, end = 845, daysEnabled = setOf(DayOfWeek.SUNDAY))
val profiles = listOf(first.copy(schedule = schedule)) val profiles = listOf(first.copy(schedule = schedule))
assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(), utc), `is`(profiles[0])) assertThat("active profile is first", NotificationProfiles.getActiveProfile(profiles, sunday9am.toMillis(ZoneOffset.UTC), utc), `is`(profiles[0]))
} }
@Test @Test
fun `when profile is manually enabled for set time before schedule end and now is after manual but before schedule end then return null`() { fun `when profile is manually enabled for set time before schedule end and now is after manual but before schedule end then return null`() {
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_PROFILE, first.id) signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_PROFILE, first.id)
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_UNTIL, sunday9am.toMillis()) signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_ENABLED_UNTIL, sunday9am.toMillis(ZoneOffset.UTC))
signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis()) signalStore.dataSet.putLong(NotificationProfileValues.KEY_MANUALLY_DISABLED_AT, sunday830am.toMillis(ZoneOffset.UTC))
val schedule = NotificationProfileSchedule(id = 3L, true, start = 700, end = 1000, daysEnabled = setOf(DayOfWeek.SUNDAY)) val schedule = NotificationProfileSchedule(id = 3L, true, start = 700, end = 1000, daysEnabled = setOf(DayOfWeek.SUNDAY))
val profiles = listOf(first.copy(schedule = schedule)) val profiles = listOf(first.copy(schedule = schedule))
assertThat("active profile is null", NotificationProfiles.getActiveProfile(profiles, sunday930am.toMillis(), utc), nullValue()) assertThat("active profile is null", NotificationProfiles.getActiveProfile(profiles, sunday930am.toMillis(ZoneOffset.UTC), utc), nullValue())
} }
} }