package com.reactcommunity.rndatetimepicker import android.content.DialogInterface import android.os.Bundle import androidx.fragment.app.FragmentManager import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.WritableNativeMap import com.google.android.material.datepicker.CalendarConstraints import com.google.android.material.datepicker.CalendarConstraints.DateValidator import com.google.android.material.datepicker.CompositeDateValidator import com.google.android.material.datepicker.DateValidatorPointBackward import com.google.android.material.datepicker.DateValidatorPointForward import com.google.android.material.datepicker.MaterialDatePicker import com.google.android.material.datepicker.MaterialPickerOnPositiveButtonClickListener import java.util.Calendar class RNMaterialDatePicker( private val args: Bundle, private val promise: Promise, private val fragmentManager: FragmentManager, private val reactContext: ReactApplicationContext ) { private var promiseResolved = false private var datePicker: MaterialDatePicker? = null private var builder = MaterialDatePicker.Builder.datePicker() fun open() { createDatePicker() addListeners() show() } private fun createDatePicker() { setInitialDate() setTitle() setInputMode() setButtons() setConstraints() setFullscreen() datePicker = builder.build() } private fun setInitialDate() { val initialDate = RNDate(args) builder.setSelection(initialDate.timestamp()) } private fun setTitle() { val title = args.getString(RNConstants.ARG_TITLE) if (!title.isNullOrEmpty()) { builder.setTitleText(args.getString(RNConstants.ARG_TITLE)) } } private fun setInputMode() { if (args.getString(RNConstants.ARG_INITIAL_INPUT_MODE).isNullOrEmpty()) { builder.setInputMode(MaterialDatePicker.INPUT_MODE_CALENDAR) return } val inputMode = RNMaterialInputMode.valueOf( args.getString(RNConstants.ARG_INITIAL_INPUT_MODE)!!.uppercase() ) if (inputMode == RNMaterialInputMode.KEYBOARD) { builder.setInputMode(MaterialDatePicker.INPUT_MODE_TEXT) } else { builder.setInputMode(MaterialDatePicker.INPUT_MODE_CALENDAR) } } private fun setConstraints() { val constraintsBuilder = CalendarConstraints.Builder() if (args.containsKey(RNConstants.FIRST_DAY_OF_WEEK)) { constraintsBuilder.setFirstDayOfWeek(args.getInt(RNConstants.FIRST_DAY_OF_WEEK)) } val validators = mutableListOf() if (args.containsKey(RNConstants.ARG_MINDATE)) { val minDate = Common.minDateWithTimeZone(args) validators.add(DateValidatorPointForward.from(minDate)) } if (args.containsKey(RNConstants.ARG_MAXDATE)) { val maxDate = Common.maxDateWithTimeZone(args) validators.add(DateValidatorPointBackward.before(maxDate)) } constraintsBuilder.setValidator(CompositeDateValidator.allOf(validators)) builder.setCalendarConstraints(constraintsBuilder.build()) } private fun setFullscreen() { val isFullscreen = args.getBoolean(RNConstants.ARG_FULLSCREEN) if (isFullscreen) { builder.setTheme(com.google.android.material.R.style.ThemeOverlay_Material3_MaterialCalendar_Fullscreen) } else { builder.setTheme(com.google.android.material.R.style.ThemeOverlay_Material3_MaterialCalendar) } } private fun addListeners() { val listeners = Listeners() datePicker!!.addOnPositiveButtonClickListener(listeners) datePicker!!.addOnDismissListener(listeners) } private fun show() { datePicker!!.show(fragmentManager, MaterialDatePickerModule.NAME) } private fun setButtons() { val buttons = args.getBundle(RNConstants.ARG_DIALOG_BUTTONS) ?: return val negativeButton = buttons.getBundle(Common.NEGATIVE) val positiveButton = buttons.getBundle(Common.POSITIVE) if (negativeButton != null) { builder.setNegativeButtonText(negativeButton.getString(Common.LABEL)) } if (positiveButton != null) { builder.setPositiveButtonText(positiveButton.getString(Common.LABEL)) } } private inner class Listeners : MaterialPickerOnPositiveButtonClickListener, DialogInterface.OnDismissListener { override fun onDismiss(dialog: DialogInterface) { if (promiseResolved || !reactContext.hasActiveReactInstance()) return val result = WritableNativeMap() result.putString("action", RNConstants.ACTION_DISMISSED) promise.resolve(result) promiseResolved = true } override fun onPositiveButtonClick(selection: Long) { if (promiseResolved || !reactContext.hasActiveReactInstance()) return val newCalendar = createNewCalendar(selection) val result = WritableNativeMap() result.putString("action", RNConstants.ACTION_DATE_SET) result.putDouble("timestamp", newCalendar.timeInMillis.toDouble()) result.putDouble( "utcOffset", newCalendar.timeZone.getOffset(newCalendar.timeInMillis).toDouble() / 1000 / 60 ) promise.resolve(result) promiseResolved = true } private fun createNewCalendar(selection: Long): Calendar { val initialDate = RNDate(args) val newCalendar = Calendar.getInstance( Common.getTimeZone( args ) ) newCalendar.timeInMillis = selection newCalendar[Calendar.HOUR_OF_DAY] = initialDate.hour() newCalendar[Calendar.MINUTE] = initialDate.minute() newCalendar[Calendar.SECOND] = 0 newCalendar[Calendar.MILLISECOND] = 0 return newCalendar } } }