Skip to content

Instantly share code, notes, and snippets.

@necatisozer
Created August 5, 2020 04:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save necatisozer/8fe20171d96d881f71c5b93dfd7b0ec8 to your computer and use it in GitHub Desktop.
Save necatisozer/8fe20171d96d881f71c5b93dfd7b0ec8 to your computer and use it in GitHub Desktop.
package tr.com.bisu.app.core.presentation.base.extension
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import android.widget.DatePicker
import android.widget.TimePicker
import androidx.annotation.CheckResult
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.WhichButton.POSITIVE
import com.afollestad.materialdialogs.actions.setActionButtonEnabled
import com.afollestad.materialdialogs.callbacks.onDismiss
import com.afollestad.materialdialogs.customview.customView
import com.afollestad.materialdialogs.utils.MDUtil.isLandscape
import tr.com.bisu.app.core.presentation.R
import java.time.LocalDateTime
import java.time.ZoneId
import java.util.*
typealias DateTimeCallback = ((dialog: MaterialDialog, datetime: LocalDateTime) -> Unit)?
fun MaterialDialog.dateTimePickerSpinner(
minDateTime: LocalDateTime? = null,
maxDateTime: LocalDateTime? = null,
currentDateTime: LocalDateTime = LocalDateTime.now(),
requireFutureDateTime: Boolean = false,
show24HoursView: Boolean = false,
dateTimeCallback: DateTimeCallback = null
): MaterialDialog {
customView(
R.layout.view_date_time_picker,
dialogWrapContent = windowContext.isLandscape()
)
getDatePicker().apply {
minDateTime?.let { minDate = it.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() }
maxDateTime?.let { maxDate = it.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() }
init(
currentDateTime.year,
currentDateTime.monthValue - 1,
currentDateTime.dayOfMonth
) { view, year, monthOfYear, dayOfMonth ->
val futureTime = isFutureTime(getDatePicker(), getTimePicker())
setActionButtonEnabled(POSITIVE, !requireFutureDateTime || futureTime)
}
}
getTimePicker().apply {
setIs24HourView(show24HoursView)
hour(currentDateTime.hour)
minute(currentDateTime.minute)
setOnTimeChangedListener { _, _, _ ->
val isFutureTime = isFutureTime(getDatePicker(), this)
setActionButtonEnabled(POSITIVE, !requireFutureDateTime || isFutureTime)
}
}
positiveButton(android.R.string.ok) {
val selectedTime = toDateTime(getDatePicker(), getTimePicker())
dateTimeCallback?.invoke(it, selectedTime)
}
negativeButton(android.R.string.cancel)
if (requireFutureDateTime) {
val changeListener = TimeChangeListener(windowContext, getTimePicker()) {
val isFutureTime = isFutureTime(getDatePicker(), it)
setActionButtonEnabled(POSITIVE, !requireFutureDateTime || isFutureTime)
}
onDismiss { changeListener.dispose() }
}
return this
}
@CheckResult
fun MaterialDialog.selectedDateTime(): LocalDateTime {
return toDateTime(getDatePicker(), getTimePicker())
}
/**************************************************************************************************/
internal fun TimePicker.hour(): Int = if (isNougat()) hour else currentHour
internal fun TimePicker.minute(): Int = if (isNougat()) minute else currentMinute
internal fun TimePicker.hour(value: Int) {
if (isNougat()) hour = value else currentHour = value
}
internal fun TimePicker.minute(value: Int) {
if (isNougat()) minute = value else currentMinute = value
}
internal fun MaterialDialog.getDatePicker() = findViewById<DatePicker>(R.id.datePicker)
internal fun MaterialDialog.getTimePicker() = findViewById<TimePicker>(R.id.timePicker)
private fun isNougat() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
/**************************************************************************************************/
internal fun isFutureTime(datePicker: DatePicker, timePicker: TimePicker): Boolean {
val now = LocalDateTime.now()
val dateTime = toDateTime(datePicker, timePicker)
return dateTime >= now
}
internal fun TimePicker.isFutureTime(): Boolean {
val now = LocalDateTime.now()
return toDateTime() >= now
}
internal fun DatePicker.isFutureDate(): Boolean {
val now = LocalDateTime.now()
return toDateTime() >= now
}
internal fun DatePicker.toDateTime(): LocalDateTime {
return LocalDateTime.now()
.withYear(year)
.withMonth(month + 1)
.withDayOfMonth(dayOfMonth)
}
internal fun TimePicker.toDateTime(): LocalDateTime {
return LocalDateTime.now()
.withHour(hour())
.withMinute(minute())
}
internal fun toDateTime(datePicker: DatePicker, timePicker: TimePicker): LocalDateTime {
return LocalDateTime.of(
datePicker.year,
datePicker.month + 1,
datePicker.dayOfMonth,
timePicker.hour(),
timePicker.minute()
)
}
/**************************************************************************************************/
internal class TimeChangeListener<T : Any>(
private var context: Context?,
private val argument: T?,
private var onChange: ((arg: T) -> Unit)? = null
) {
private var lastHour: Int = -1
private var lastMinute: Int = -1
private val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val now = Calendar.getInstance()
val newHour = now.get(Calendar.HOUR_OF_DAY)
val newMinute = now.get(Calendar.MINUTE)
if (argument != null && (lastHour != newHour || lastMinute != newMinute)) {
onChange?.invoke(argument)
lastHour = newHour
lastMinute = newMinute
}
}
}
init {
requireNotNull(context)
requireNotNull(argument)
requireNotNull(onChange)
val filter = IntentFilter().apply {
addAction(Intent.ACTION_TIME_TICK)
addAction(Intent.ACTION_TIMEZONE_CHANGED)
addAction(Intent.ACTION_TIME_CHANGED)
}
context!!.registerReceiver(receiver, filter)
}
fun dispose() {
context?.unregisterReceiver(receiver)
context = null
onChange = null
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment