kopia lustrzana https://github.com/ryukoposting/Signal-Android
217 wiersze
7.9 KiB
Kotlin
217 wiersze
7.9 KiB
Kotlin
package org.thoughtcrime.securesms.conversation
|
|
|
|
import android.os.Bundle
|
|
import android.text.format.DateFormat
|
|
import android.view.LayoutInflater
|
|
import android.view.View
|
|
import android.view.ViewGroup
|
|
import androidx.fragment.app.FragmentManager
|
|
import com.google.android.material.datepicker.CalendarConstraints
|
|
import com.google.android.material.datepicker.DateValidatorPointForward
|
|
import com.google.android.material.datepicker.MaterialDatePicker
|
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
import com.google.android.material.timepicker.MaterialTimePicker
|
|
import com.google.android.material.timepicker.TimeFormat
|
|
import org.thoughtcrime.securesms.R
|
|
import org.thoughtcrime.securesms.components.FixedRoundedCornerBottomSheetDialogFragment
|
|
import org.thoughtcrime.securesms.components.ViewBinderDelegate
|
|
import org.thoughtcrime.securesms.databinding.ScheduleMessageTimePickerBottomSheetBinding
|
|
import org.thoughtcrime.securesms.util.BottomSheetUtil
|
|
import org.thoughtcrime.securesms.util.DateUtils
|
|
import org.thoughtcrime.securesms.util.atMidnight
|
|
import org.thoughtcrime.securesms.util.atUTC
|
|
import org.thoughtcrime.securesms.util.formatHours
|
|
import org.thoughtcrime.securesms.util.fragments.findListener
|
|
import org.thoughtcrime.securesms.util.toLocalDateTime
|
|
import org.thoughtcrime.securesms.util.toMillis
|
|
import java.time.LocalDateTime
|
|
import java.time.LocalTime
|
|
import java.time.ZoneId
|
|
import java.time.ZoneOffset
|
|
import java.time.ZonedDateTime
|
|
import java.time.format.DateTimeFormatter
|
|
import java.util.Locale
|
|
|
|
/**
|
|
* Bottom sheet dialog that allows selecting a timestamp after the current time for
|
|
* scheduling a message send.
|
|
*
|
|
* Will call [ScheduleCallback.onScheduleSend] with the selected time, if called with [showSchedule]
|
|
* Will call [RescheduleCallback.onReschedule] with the selected time, if called with [showReschedule]
|
|
*/
|
|
class ScheduleMessageTimePickerBottomSheet : FixedRoundedCornerBottomSheetDialogFragment() {
|
|
override val peekHeightPercentage: Float = 0.66f
|
|
override val themeResId: Int = R.style.Widget_Signal_FixedRoundedCorners_Messages
|
|
|
|
private var scheduledDate: Long = 0
|
|
private var scheduledHour: Int = 0
|
|
private var scheduledMinute: Int = 0
|
|
|
|
private val binding by ViewBinderDelegate(ScheduleMessageTimePickerBottomSheetBinding::bind)
|
|
|
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
|
return inflater.inflate(R.layout.schedule_message_time_picker_bottom_sheet, container, false)
|
|
}
|
|
|
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
val initialTime = arguments?.getLong(KEY_INITIAL_TIME)
|
|
scheduledDate = initialTime ?: System.currentTimeMillis()
|
|
var scheduledLocalDateTime = scheduledDate.toLocalDateTime()
|
|
if (initialTime == null) {
|
|
scheduledLocalDateTime = scheduledLocalDateTime.plusMinutes(5L - (scheduledLocalDateTime.minute % 5))
|
|
}
|
|
|
|
scheduledHour = scheduledLocalDateTime.hour
|
|
scheduledMinute = scheduledLocalDateTime.minute
|
|
|
|
binding.scheduleSend.setOnClickListener {
|
|
if (getSelectedTimestamp() < System.currentTimeMillis()) {
|
|
MaterialAlertDialogBuilder(requireContext())
|
|
.setMessage(R.string.ScheduleMessageTimePickerBottomSheet__select_time_in_past_dialog_warning)
|
|
.setPositiveButton(R.string.ScheduleMessageTimePickerBottomSheet__select_time_in_past_dialog_positive_button) { _, _ -> scheduleMessageSend() }
|
|
.setNegativeButton(android.R.string.cancel, null)
|
|
.show()
|
|
} else {
|
|
scheduleMessageSend()
|
|
}
|
|
}
|
|
|
|
val zoneOffsetFormatter = DateTimeFormatter.ofPattern("OOOO")
|
|
val zoneNameFormatter = DateTimeFormatter.ofPattern("zzzz")
|
|
val zonedDateTime = ZonedDateTime.now()
|
|
binding.timezoneDisclaimer.apply {
|
|
text = getString(
|
|
R.string.ScheduleMessageTimePickerBottomSheet__timezone_disclaimer,
|
|
zoneOffsetFormatter.format(zonedDateTime),
|
|
zoneNameFormatter.format(zonedDateTime)
|
|
)
|
|
}
|
|
|
|
updateSelectedDate()
|
|
updateSelectedTime()
|
|
|
|
setupDateSelector()
|
|
setupTimeSelector()
|
|
}
|
|
|
|
private fun setupDateSelector() {
|
|
binding.daySelector.setOnClickListener {
|
|
val local = LocalDateTime.now()
|
|
.atMidnight()
|
|
.atUTC()
|
|
.toMillis()
|
|
val datePicker =
|
|
MaterialDatePicker.Builder.datePicker()
|
|
.setTitleText(getString(R.string.ScheduleMessageTimePickerBottomSheet__select_date_title))
|
|
.setSelection(scheduledDate)
|
|
.setCalendarConstraints(CalendarConstraints.Builder().setStart(local).setValidator(DateValidatorPointForward.now()).build())
|
|
.build()
|
|
|
|
datePicker.addOnDismissListener {
|
|
datePicker.clearOnDismissListeners()
|
|
datePicker.clearOnPositiveButtonClickListeners()
|
|
}
|
|
|
|
datePicker.addOnPositiveButtonClickListener {
|
|
it.let {
|
|
scheduledDate = it.toLocalDateTime(ZoneOffset.UTC).atZone(ZoneId.systemDefault()).toMillis()
|
|
updateSelectedDate()
|
|
}
|
|
}
|
|
datePicker.show(childFragmentManager, "DATE_PICKER")
|
|
}
|
|
}
|
|
|
|
private fun setupTimeSelector() {
|
|
binding.timeSelector.setOnClickListener {
|
|
val timeFormat = if (DateFormat.is24HourFormat(requireContext())) TimeFormat.CLOCK_24H else TimeFormat.CLOCK_12H
|
|
val timePickerFragment = MaterialTimePicker.Builder()
|
|
.setTimeFormat(timeFormat)
|
|
.setHour(scheduledHour)
|
|
.setMinute(scheduledMinute)
|
|
.setTitleText(getString(R.string.ScheduleMessageTimePickerBottomSheet__select_time_title))
|
|
.build()
|
|
|
|
timePickerFragment.addOnDismissListener {
|
|
timePickerFragment.clearOnDismissListeners()
|
|
timePickerFragment.clearOnPositiveButtonClickListeners()
|
|
}
|
|
|
|
timePickerFragment.addOnPositiveButtonClickListener {
|
|
scheduledHour = timePickerFragment.hour
|
|
scheduledMinute = timePickerFragment.minute
|
|
|
|
updateSelectedTime()
|
|
}
|
|
|
|
timePickerFragment.show(childFragmentManager, "TIME_PICKER")
|
|
}
|
|
}
|
|
|
|
private fun getSelectedTimestamp(): Long {
|
|
return scheduledDate.toLocalDateTime()
|
|
.withMinute(scheduledMinute)
|
|
.withHour(scheduledHour)
|
|
.withSecond(0)
|
|
.withNano(0)
|
|
.toMillis()
|
|
}
|
|
|
|
private fun updateSelectedDate() {
|
|
binding.dateText.text = DateUtils.getDayPrecisionTimeString(requireContext(), Locale.getDefault(), scheduledDate)
|
|
}
|
|
|
|
private fun updateSelectedTime() {
|
|
val scheduledTime = LocalTime.of(scheduledHour, scheduledMinute)
|
|
binding.timeText.text = scheduledTime.formatHours(requireContext())
|
|
}
|
|
|
|
private fun scheduleMessageSend() {
|
|
dismissAllowingStateLoss()
|
|
val messageId = arguments?.getLong(KEY_MESSAGE_ID)
|
|
if (messageId == null) {
|
|
findListener<ScheduleCallback>()?.onScheduleSend(getSelectedTimestamp())
|
|
} else {
|
|
val selectedTime = getSelectedTimestamp()
|
|
if (selectedTime != arguments?.getLong(KEY_INITIAL_TIME)) {
|
|
findListener<RescheduleCallback>()?.onReschedule(selectedTime, messageId)
|
|
}
|
|
}
|
|
}
|
|
|
|
interface ScheduleCallback {
|
|
fun onScheduleSend(scheduledTime: Long)
|
|
}
|
|
|
|
interface RescheduleCallback {
|
|
fun onReschedule(scheduledTime: Long, messageId: Long)
|
|
}
|
|
|
|
companion object {
|
|
|
|
private const val KEY_MESSAGE_ID = "message_id"
|
|
private const val KEY_INITIAL_TIME = "initial_time"
|
|
|
|
@JvmStatic
|
|
fun showSchedule(fragmentManager: FragmentManager) {
|
|
val fragment = ScheduleMessageTimePickerBottomSheet()
|
|
|
|
fragment.show(fragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
|
|
}
|
|
|
|
@JvmStatic
|
|
fun showReschedule(fragmentManager: FragmentManager, messageId: Long, initialTime: Long) {
|
|
val args = Bundle().apply {
|
|
putLong(KEY_MESSAGE_ID, messageId)
|
|
putLong(KEY_INITIAL_TIME, initialTime)
|
|
}
|
|
|
|
val fragment = ScheduleMessageTimePickerBottomSheet().apply {
|
|
arguments = args
|
|
}
|
|
|
|
fragment.show(fragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
|
|
}
|
|
}
|
|
}
|