mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-27 06:29:54 +00:00
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
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
package org.thoughtcrime.securesms.components.settings.app.notifications.manual
|
||||
|
||||
import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile
|
||||
import java.util.Calendar
|
||||
import java.time.LocalDateTime
|
||||
|
||||
data class NotificationProfileSelectionState(
|
||||
val notificationProfiles: List<NotificationProfile> = listOf(),
|
||||
val expandedId: Long = -1L,
|
||||
val timeSlotB: Calendar
|
||||
val timeSlotB: LocalDateTime
|
||||
)
|
||||
|
||||
@@ -9,8 +9,10 @@ import io.reactivex.rxjava3.kotlin.plusAssign
|
||||
import io.reactivex.rxjava3.kotlin.subscribeBy
|
||||
import org.thoughtcrime.securesms.components.settings.app.notifications.profiles.NotificationProfilesRepository
|
||||
import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile
|
||||
import org.thoughtcrime.securesms.util.isBetween
|
||||
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
|
||||
|
||||
class NotificationProfileSelectionViewModel(private val repository: NotificationProfilesRepository) : ViewModel() {
|
||||
@@ -53,29 +55,24 @@ class NotificationProfileSelectionViewModel(private val repository: Notification
|
||||
.subscribe()
|
||||
}
|
||||
|
||||
fun enableUntil(profile: NotificationProfile, calendar: Calendar) {
|
||||
disposables += repository.manuallyEnableProfileForDuration(profile.id, calendar.timeInMillis)
|
||||
fun enableUntil(profile: NotificationProfile, enableUntil: LocalDateTime) {
|
||||
disposables += repository.manuallyEnableProfileForDuration(profile.id, enableUntil.toMillis())
|
||||
.subscribe()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun getTimeSlotB(): Calendar {
|
||||
val now = Calendar.getInstance()
|
||||
val sixPm = Calendar.getInstance()
|
||||
val eightAm = Calendar.getInstance()
|
||||
@Suppress("CascadeIf")
|
||||
private fun getTimeSlotB(): LocalDateTime {
|
||||
val now = LocalDateTime.now()
|
||||
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)
|
||||
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)) {
|
||||
return if (now.isBetween(eightAm, sixPm)) {
|
||||
sixPm
|
||||
} else {
|
||||
} else if (now.isBefore(eightAm)) {
|
||||
eightAm
|
||||
} else {
|
||||
eightAm.plusDays(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,12 +9,12 @@ import org.thoughtcrime.securesms.components.emoji.EmojiImageView
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
|
||||
import org.thoughtcrime.securesms.components.settings.PreferenceModel
|
||||
import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile
|
||||
import org.thoughtcrime.securesms.util.DateUtils
|
||||
import org.thoughtcrime.securesms.util.MappingAdapter
|
||||
import org.thoughtcrime.securesms.util.MappingViewHolder
|
||||
import org.thoughtcrime.securesms.util.formatHours
|
||||
import org.thoughtcrime.securesms.util.visible
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
import java.time.LocalDateTime
|
||||
import java.time.LocalTime
|
||||
|
||||
/**
|
||||
* Notification Profile selection preference.
|
||||
@@ -34,10 +34,10 @@ object NotificationProfileSelection {
|
||||
override val summary: DSLSettingsText,
|
||||
val notificationProfile: NotificationProfile,
|
||||
val isExpanded: Boolean,
|
||||
val timeSlotB: Calendar,
|
||||
val timeSlotB: LocalDateTime,
|
||||
val onRowClick: (NotificationProfile) -> Unit,
|
||||
val onTimeSlotAClick: (NotificationProfile) -> Unit,
|
||||
val onTimeSlotBClick: (NotificationProfile, Calendar) -> Unit,
|
||||
val onTimeSlotBClick: (NotificationProfile, LocalDateTime) -> Unit,
|
||||
val onViewSettingsClick: (NotificationProfile) -> Unit,
|
||||
val onToggleClick: (NotificationProfile) -> Unit
|
||||
) : PreferenceModel<Entry>() {
|
||||
@@ -87,7 +87,7 @@ object NotificationProfileSelection {
|
||||
expansion.visible = model.isExpanded
|
||||
timeSlotB.text = context.getString(
|
||||
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) {
|
||||
@@ -107,7 +107,7 @@ object NotificationProfileSelection {
|
||||
|
||||
timeSlotB.text = context.getString(
|
||||
R.string.NotificationProfileSelection__until_s,
|
||||
DateUtils.getTimeString(context, Locale.getDefault(), model.timeSlotB.timeInMillis)
|
||||
LocalTime.from(model.timeSlotB).formatHours()
|
||||
)
|
||||
|
||||
itemView.isSelected = model.isOn
|
||||
|
||||
@@ -35,7 +35,7 @@ import java.time.format.DateTimeFormatter
|
||||
*/
|
||||
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 profileId: Long by lazy { EditNotificationProfileScheduleFragmentArgs.fromBundle(requireArguments()).profileId }
|
||||
|
||||
@@ -17,7 +17,8 @@ import java.time.DayOfWeek
|
||||
* from the database and into the [scheduleSubject] allowing the safe use of !! with [schedule].
|
||||
*/
|
||||
class EditNotificationProfileScheduleViewModel(
|
||||
profileId: Long,
|
||||
private val profileId: Long,
|
||||
private val createMode: Boolean,
|
||||
private val repository: NotificationProfilesRepository
|
||||
) : ViewModel() {
|
||||
|
||||
@@ -72,14 +73,23 @@ class EditNotificationProfileScheduleViewModel(
|
||||
} else if (createMode && !schedule.enabled) {
|
||||
Single.just(SaveScheduleResult.Success)
|
||||
} 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())
|
||||
}
|
||||
|
||||
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 {
|
||||
return modelClass.cast(EditNotificationProfileScheduleViewModel(profileId, NotificationProfilesRepository()))!!
|
||||
return modelClass.cast(EditNotificationProfileScheduleViewModel(profileId, createMode, NotificationProfilesRepository()))!!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,19 +96,23 @@ class NotificationProfilesRepository {
|
||||
}
|
||||
|
||||
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 {
|
||||
val profiles = database.getProfiles()
|
||||
val activeProfile = NotificationProfiles.getActiveProfile(profiles, now)
|
||||
|
||||
if (profile.id == activeProfile?.id) {
|
||||
if (profileId == activeProfile?.id) {
|
||||
SignalStore.notificationProfileValues().manuallyEnabledProfile = 0
|
||||
SignalStore.notificationProfileValues().manuallyEnabledUntil = 0
|
||||
SignalStore.notificationProfileValues().manuallyDisabledAt = now
|
||||
SignalStore.notificationProfileValues().lastProfilePopup = 0
|
||||
SignalStore.notificationProfileValues().lastProfilePopupTime = 0
|
||||
} else {
|
||||
val inScheduledWindow = profile.schedule.isCurrentlyActive(now)
|
||||
SignalStore.notificationProfileValues().manuallyEnabledProfile = if (inScheduledWindow) 0 else profile.id
|
||||
val inScheduledWindow = schedule.isCurrentlyActive(now)
|
||||
SignalStore.notificationProfileValues().manuallyEnabledProfile = if (inScheduledWindow) 0 else profileId
|
||||
SignalStore.notificationProfileValues().manuallyEnabledUntil = if (inScheduledWindow) 0 else Long.MAX_VALUE
|
||||
SignalStore.notificationProfileValues().manuallyDisabledAt = if (inScheduledWindow) 0 else now
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.thoughtcrime.securesms.util.formatHours
|
||||
import org.thoughtcrime.securesms.util.toLocalDateTime
|
||||
import org.thoughtcrime.securesms.util.toLocalTime
|
||||
import org.thoughtcrime.securesms.util.toMillis
|
||||
import org.thoughtcrime.securesms.util.toOffset
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
|
||||
@@ -26,7 +27,7 @@ object NotificationProfiles {
|
||||
val manualProfile: NotificationProfile? = profiles.firstOrNull { it.id == storeValues.manuallyEnabledProfile }
|
||||
|
||||
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) {
|
||||
|
||||
@@ -3,17 +3,25 @@ package org.thoughtcrime.securesms.util
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
import java.time.LocalTime
|
||||
import java.time.OffsetDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
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]
|
||||
*/
|
||||
fun LocalDateTime.toMillis(): Long {
|
||||
return TimeUnit.SECONDS.toMillis(toEpochSecond(ZoneOffset.UTC))
|
||||
fun LocalDateTime.toMillis(zoneOffset: ZoneOffset = ZoneId.systemDefault().toOffset()): Long {
|
||||
return TimeUnit.SECONDS.toMillis(toEpochSecond(zoneOffset))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user