diff --git a/app/src/main/java/io/timelimit/android/extensions/LocalDateTime.kt b/app/src/main/java/io/timelimit/android/extensions/LocalDateTime.kt new file mode 100644 index 0000000..8f8641c --- /dev/null +++ b/app/src/main/java/io/timelimit/android/extensions/LocalDateTime.kt @@ -0,0 +1,21 @@ +/* + * TimeLimit Copyright 2019 - 2023 Jonas Lochmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package io.timelimit.android.extensions + +import org.threeten.bp.LocalDateTime +import org.threeten.bp.ZoneId + +fun LocalDateTime.toInstant(zone: ZoneId) = toInstant(zone.rules.getOffset(this)) \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/ui/manage/child/category/specialmode/SetCategorySpecialModeFragment.kt b/app/src/main/java/io/timelimit/android/ui/manage/child/category/specialmode/SetCategorySpecialModeFragment.kt index 92de700..5f04729 100644 --- a/app/src/main/java/io/timelimit/android/ui/manage/child/category/specialmode/SetCategorySpecialModeFragment.kt +++ b/app/src/main/java/io/timelimit/android/ui/manage/child/category/specialmode/SetCategorySpecialModeFragment.kt @@ -1,5 +1,5 @@ /* - * TimeLimit Copyright 2019 - 2022 Jonas Lochmann + * TimeLimit Copyright 2019 - 2023 Jonas Lochmann * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,12 +30,14 @@ import com.google.android.material.bottomsheet.BottomSheetDialog import io.timelimit.android.R import io.timelimit.android.databinding.SpecialModeDialogBinding import io.timelimit.android.extensions.showSafe +import io.timelimit.android.extensions.toInstant import io.timelimit.android.ui.main.ActivityViewModel import io.timelimit.android.ui.main.getActivityViewModel import io.timelimit.android.ui.payment.RequiresPurchaseDialogFragment import org.threeten.bp.Instant import org.threeten.bp.LocalDate import org.threeten.bp.LocalDateTime +import org.threeten.bp.LocalTime import org.threeten.bp.ZoneId class SetCategorySpecialModeFragment: DialogFragment() { @@ -48,8 +50,8 @@ class SetCategorySpecialModeFragment: DialogFragment() { private const val PAGE_TYPE = 0 private const val PAGE_SUGGESTION = 1 - private const val PAGE_CLOCK = 2 - private const val PAGE_CALENDAR = 3 + private const val PAGE_CALENDAR = 2 + private const val PAGE_CLOCK = 3 fun newInstance(childId: String, categoryId: String, mode: SpecialModeDialogMode) = SetCategorySpecialModeFragment().apply { arguments = Bundle().apply { @@ -166,24 +168,22 @@ class SetCategorySpecialModeFragment: DialogFragment() { } run { - fun readClockTime(timeZone: String, now: Long) = binding.timePicker.let { - LocalDateTime.ofInstant( - Instant.ofEpochMilli(now), - ZoneId.of(timeZone) - ) - .toLocalDate() - .atStartOfDay(ZoneId.of(timeZone)) - .plusHours(it.currentHour.toLong()) - .plusMinutes(it.currentMinute.toLong()) - .toEpochSecond() * 1000 + fun readClockTime(timeZone: String, localDate: LocalDate?, now: Long) = binding.timePicker.let { + val zoneId = ZoneId.of(timeZone) + + LocalDateTime.of( + localDate ?: LocalDateTime.ofInstant(Instant.ofEpochMilli(now), zoneId).toLocalDate(), + LocalTime.of(it.currentHour, it.currentMinute) + ).toInstant(zoneId).toEpochMilli() } fun update() { val content = model.content.value + val screen = content?.screen val minTime = model.minTimestamp.value - if (content?.screen is SetCategorySpecialModeModel.Screen.WithType.ClockScreen && minTime != null) { - val currentSelectedTime = readClockTime(content.childTimezone, model.now()) + if (screen is SetCategorySpecialModeModel.Screen.WithType.ClockScreen && minTime != null) { + val currentSelectedTime = readClockTime(content.childTimezone, screen.date, model.now()) val isEnabled = currentSelectedTime > minTime binding.confirmTimePickerButton.isEnabled = isEnabled @@ -203,10 +203,8 @@ class SetCategorySpecialModeFragment: DialogFragment() { } run { - fun readCalendarTime(timeZone: String) = binding.datePicker.let { + fun readCalendarDate() = binding.datePicker.let { LocalDate.of(it.year, it.month + 1, it.dayOfMonth) - .atStartOfDay(ZoneId.of(timeZone)) - .toEpochSecond() * 1000 } fun update() { @@ -214,19 +212,23 @@ class SetCategorySpecialModeFragment: DialogFragment() { val minTime = model.minTimestamp.value if (content?.screen is SetCategorySpecialModeModel.Screen.WithType.CalendarScreen && minTime != null) { - val currentSelectedTime = readCalendarTime(content.childTimezone) - val isEnabled = currentSelectedTime > minTime + val zoneId = ZoneId.of(content.childTimezone) + val currentSelectedDate = readCalendarDate() + val currentStartOfDayTime = LocalDateTime.of(currentSelectedDate, LocalTime.MIN).toInstant(zoneId).toEpochMilli() + val currentEndOfDayTime = LocalDateTime.of(currentSelectedDate, LocalTime.of(23, 59)).toInstant(zoneId).toEpochMilli() - binding.confirmDatePickerButton.isEnabled = isEnabled + val isConfirmEnabled = currentStartOfDayTime > minTime + val isClockEnabled = currentEndOfDayTime > minTime - if (isEnabled) { - binding.confirmDatePickerButton.setOnClickListener { - val currentSelectedTimeNew = readCalendarTime(content.childTimezone) + binding.confirmDatePickerButton.isEnabled = isConfirmEnabled + binding.timeOfDayDatePickerButton.isEnabled = isClockEnabled - if (currentSelectedTimeNew > minTime) { - model.applySelection(timeInMillis = currentSelectedTimeNew, auth = auth) - } else update() - } + if (isConfirmEnabled) binding.confirmDatePickerButton.setOnClickListener { + model.applySelection(timeInMillis = currentStartOfDayTime, auth = auth) + } + + if (isClockEnabled) binding.timeOfDayDatePickerButton.setOnClickListener { + model.openClockScreen(currentSelectedDate) } } else binding.confirmDatePickerButton.isEnabled = false } diff --git a/app/src/main/java/io/timelimit/android/ui/manage/child/category/specialmode/SetCategorySpecialModeModel.kt b/app/src/main/java/io/timelimit/android/ui/manage/child/category/specialmode/SetCategorySpecialModeModel.kt index 6023917..140b64b 100644 --- a/app/src/main/java/io/timelimit/android/ui/manage/child/category/specialmode/SetCategorySpecialModeModel.kt +++ b/app/src/main/java/io/timelimit/android/ui/manage/child/category/specialmode/SetCategorySpecialModeModel.kt @@ -26,6 +26,7 @@ import io.timelimit.android.logic.DefaultAppLogic import io.timelimit.android.sync.actions.UpdateCategoryDisableLimitsAction import io.timelimit.android.sync.actions.UpdateCategoryTemporarilyBlockedAction import io.timelimit.android.ui.main.ActivityViewModel +import org.threeten.bp.LocalDate class SetCategorySpecialModeModel(application: Application): AndroidViewModel(application) { private val logic = DefaultAppLogic.with(application) @@ -113,7 +114,7 @@ class SetCategorySpecialModeModel(application: Application): AndroidViewModel(ap options = options ) } - DurationSelection.Clock -> Screen.WithType.ClockScreen(type = type) + is DurationSelection.Clock -> Screen.WithType.ClockScreen(type = type, date = durationSelection.date) DurationSelection.Calendar -> Screen.WithType.CalendarScreen(type = type) } @@ -127,11 +128,15 @@ class SetCategorySpecialModeModel(application: Application): AndroidViewModel(ap } fun selectType(type: Type) { typeLive.value = type } - fun openClockScreen() { durationSelectionLive.value = DurationSelection.Clock } + fun openClockScreen(date: LocalDate? = null) { durationSelectionLive.value = DurationSelection.Clock(date) } fun openCalendarScreen() { durationSelectionLive.value = DurationSelection.Calendar } fun goBack(): Boolean = if (durationSelectionLive.value != DurationSelection.SuggestionList) { - durationSelectionLive.value = DurationSelection.SuggestionList + val v = durationSelectionLive.value + + durationSelectionLive.value = + if (v is DurationSelection.Clock && v.date != null) DurationSelection.Calendar + else DurationSelection.SuggestionList true } else if ( @@ -240,10 +245,10 @@ class SetCategorySpecialModeModel(application: Application): AndroidViewModel(ap DisableLimits } - internal enum class DurationSelection { - SuggestionList, - Clock, - Calendar + internal sealed class DurationSelection { + object SuggestionList: DurationSelection() + data class Clock(val date: LocalDate?): DurationSelection() + object Calendar: DurationSelection() } data class Content( @@ -259,8 +264,8 @@ class SetCategorySpecialModeModel(application: Application): AndroidViewModel(ap sealed class WithType: Screen() { abstract val type: Type - data class ClockScreen(override val type: Type, ): WithType() - data class CalendarScreen(override val type: Type, ): WithType() + data class ClockScreen(override val type: Type, val date: LocalDate?): WithType() + data class CalendarScreen(override val type: Type): WithType() data class SuggestionList( override val type: Type, diff --git a/app/src/main/res/layout/special_mode_dialog.xml b/app/src/main/res/layout/special_mode_dialog.xml index 7a88a3d..50833d3 100644 --- a/app/src/main/res/layout/special_mode_dialog.xml +++ b/app/src/main/res/layout/special_mode_dialog.xml @@ -1,6 +1,6 @@