Skip to content

Instantly share code, notes, and snippets.

@kyodgorbek
Last active September 14, 2022 09:30
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 kyodgorbek/b78a0fa14f949baf9209b8ff5914aa02 to your computer and use it in GitHub Desktop.
Save kyodgorbek/b78a0fa14f949baf9209b8ff5914aa02 to your computer and use it in GitHub Desktop.
@AndroidEntryPoint
class ReserveFragment : Fragment(), DatePickerDialog.OnDateSetListener,
TimePickerDialog.OnTimeSetListener, SeekBar.OnSeekBarChangeListener {
// private lateinit var viewModel: ReserveViewModel
private lateinit var binding: FragmentReserveBinding
@Inject
lateinit var sharedPref:SharedPref
lateinit var StationNameView: TextView
lateinit var OpenHoursTextView: TextView
lateinit var TarifTextView: TextView
lateinit var PvTarifTextView: TextView
lateinit var PowerTextView: TextView
lateinit var PvHoursTextView: TextView
lateinit var startButton: Button
lateinit var BookNowButton: Button
var isAvailable: Boolean = false
var accesProfileLoaded: Boolean = false
lateinit var scheduleConflictText: TextView
lateinit var agbConstraint: ConstraintLayout
lateinit var PvTarifLayout: ConstraintLayout
lateinit var agbText: TextView
lateinit var durationSlider: SeekBar
lateinit var linearLayoutStartTime: LinearLayout
lateinit var LinearLayoutTiming: LinearLayout
lateinit var timeSlots: IntArray
lateinit var DayDropdown: Spinner
var duration: Int = 4
var booking = Booking()
lateinit var agbSwitch: androidx.appcompat.widget.SwitchCompat
private lateinit var ThumbnailImageView: ImageView
var StartTime: LocalTime = LocalTime.now().plusSeconds(3600)
var EndTime: LocalTime = LocalTime.now().plusSeconds(3600 + sharedPref.defaultChargeTime)
var StartDate: LocalDate = LocalDate.now()
val dateFormat: DateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy")
val timeFormat: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm")
// val dateParseFormat: DateTimeFormatter = DateTimeFormatter.ofPattern("d.M.yyyy")
// val timeParseFormat: DateTimeFormatter = DateTimeFormatter.ofPattern("H:m")
/* override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProvider(this).get(ReserveViewModel::class.java)
// TODO: Use the ViewModel
if (appCommon.isAdhocBooking) {
StartTime = LocalTime.now()
EndTime = LocalTime.now().plusSeconds(3600 + appCommon.defaultChargeTime)
}
}*/
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentReserveBinding.inflate(inflater, container, false)
sharedPref = SharedPref(requireContext())
return binding.root
/* val textView: TextView = root.findViewById(R.id.text_notifications)
settingsViewModel.text.observe(viewLifecycleOwner, Observer {
textView.text = it
})*/
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
sharedPref = SharedPref(requireContext())
if (sharedPref.token.isEmpty()) {
findNavController().navigate(R.id.navigation_settings_login)
}
if (sharedPref.isChargePointInitialized()) {
/// Head Section Name, Power
StationNameView = binding.StationNameTextView
StationNameView.text = sharedPref.CurrentChargePoint.CpName
PowerTextView = binding.PowerTextView
PowerTextView.text = String.format(Locale.GERMAN, "%.1f", sharedPref.CurrentChargePoint.Power) + " kW"
// ConnectorNameView = root.findViewById(R.id.ConnectorNameTextView) as TextView
// ConnectorNameView.text = appCommon.CurrentChargePoint.Name
/// Open Hours - Tarifs
OpenHoursTextView = binding.OpenHoursTextView
TarifTextView = binding.TarifTextView
TarifTextView.text = String.format(Locale.GERMAN, "%.0f", sharedPref.CurrentChargePoint.Tarif) + " Ct/kWh"
PvTarifLayout = binding.PvTarifLayout
PvTarifLayout.isVisible = false
PvTarifTextView = binding.PvTarifTextView
PvTarifTextView.text = String.format(Locale.GERMAN, "%.0f", sharedPref.CurrentChargePoint.SunShineRate) + " Ct/kWh"
PvHoursTextView = binding.PvHoursTextView
PvTarifTextView.text = ""
/// Timing
LinearLayoutTiming = binding.LinearLayoutTiming
LinearLayoutTiming.isVisible = !sharedPref.CurrentChargePoint.hasType(ChargePointType.AdHoc)
// Dropdown Days
DayDropdown = binding.DayDropdown
val tomorrow = LocalDateTime.now().plusDays(1)
val atomorrow = LocalDateTime.now().plusDays(2)
/* Create an ArrayAdapter using the string array and a default spinner layout
ArrayAdapter.createFromResource(
requireContext(),
R.array.DefaultDateValues,
android.R.layout.simple_spinner_item
).also { adapter ->
// Specify the layout to use when the list of choices appears
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
// Apply the adapter to the spinner
spinner.adapter = adapter
}*/
timeSlots = intArrayOf(1, 2, 3, 4, 5, 6, 8, 10, 12)
val spinnerData = arrayOf("Heute", tomorrow.format(dateFormat), "" + atomorrow.format(dateFormat))
DayDropdown.adapter = ArrayAdapter<String>(requireContext(), android.R.layout.simple_spinner_dropdown_item, spinnerData)
val startTime = if (sharedPref.isAdhocBooking) {
OffsetDateTime.now()
} else {
OffsetDateTime.now().plusSeconds(3600)
}
booking.STime = OffsetDateTime.from(startTime)
/* editStartTime.text = StartTime.hour.toString()+":"+ StartTime.minute.toString()
editStartTime.text = StartTime.format(timeFormat)
editStartTime.setOnClickListener(clickListener)
*/
startButton = binding.startButton
startButton.setOnClickListener(clickListener)
// editEndTime = root.findViewById(R.id.editEndTime) as TextView
EndTime = if (sharedPref.CurrentChargePoint.hasType(ChargePointType.AdHoc)) { // pure Ad Hoc station
LocalTime.now().plusSeconds(3600 * 8)
} else if (sharedPref.isAdhocBooking) { // Ad hoc @ reservable station
LocalTime.now().plusSeconds(3600 * 4)
} else { // reserved booking
LocalTime.now().plusSeconds(3600 + 3600 * 4)
}
durationSlider = binding.durationSlider
/// Booknow Button and AGB Section
/*
editEndTime.text = EndTime.format(timeFormat)
editEndTime.setOnClickListener(clickListener)
endButton = root.findViewById(R.id.end_button) as Button
endButton.setOnClickListener(clickListener)
editTextDate = root.findViewById(R.id.editTextDate) as TextView
editTextDate.text = StartDate.format(dateFormat)
*/
BookNowButton = binding.BookNowButton
if (sharedPref.isAdhocBooking) {
linearLayoutStartTime = binding.linearLayoutStartTime
linearLayoutStartTime.isVisible = false
BookNowButton.text = resources.getString(R.string.charge_now)
StartTime = LocalTime.now()
} else if (sharedPref.CurrentChargePoint.hasType(ChargePointType.AdHoc)) {
BookNowButton.text = "nur Direktladen möglich"
} else {
BookNowButton.text = resources.getString(R.string.book_now)
StartTime = LocalTime.now().plusSeconds(3600 * 1)
startButton.text = StartTime.format(timeFormat)
}
agbSwitch = binding.switchAgb
agbSwitch.isChecked = false
BookNowButton.isEnabled = false
scheduleConflictText = binding.scheduleConflictText
agbConstraint = binding.agbConstraint
BookNowButton.setOnClickListener(clickListener)
agbSwitch.setOnClickListener(clickListener)
agbText = binding.AgbText
agbText.movementMethod = LinkMovementMethod.getInstance()
/// Background Image
ThumbnailImageView = binding.imageView
val displayMetrics = DisplayMetrics()
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
val display = requireActivity().display
display?.getRealMetrics(displayMetrics)
} else {
@Suppress("DEPRECATION")
val display = requireActivity().windowManager.defaultDisplay
@Suppress("DEPRECATION")
display.getMetrics(displayMetrics)
}
val height = displayMetrics.heightPixels - 32 - 60
val width = displayMetrics.widthPixels
Picasso.get()
.load(sharedPref.CurrentChargePoint.ThumbnailUrl)
// .placeholder(R.drawable.ic_camera_grey_100dp)
// .error(R.drawable.ic_notifications_black_24dp)
.resize(width, height)
.centerCrop()
.into(ThumbnailImageView)
this.durationSlider!!.setOnSeekBarChangeListener(this)
checkAvailability()
getAccessProfile()
}
}
private fun enableBookNowButton(enabled: Boolean) {
BookNowButton.isEnabled = agbSwitch.isChecked and enabled
updateUiByExistingBookings()
}
val clickListener = View.OnClickListener { view ->
// throw RuntimeException("Test Crash") // Force a crash
when (view.id) {
R.id.switchAgb -> {
enableBookNowButton(isAvailable)
}
R.id.BookNowButton -> {
val chargePoint = sharedPref.CurrentChargePoint
// booking.Token = appCommon.getLoginToken()
booking.FriendGuid = sharedPref.FriendGuid
booking.CpGuid = chargePoint.Guid
// val StartDate = LocalDate.parse(editTextDate.text, dateParseFormat)
var StartDateTime = LocalDateTime.now()
var EndDateTime = LocalDateTime.now().plusHours(3)
StartDate = LocalDate.now().plusDays(DayDropdown.selectedItemPosition.toLong())
EndTime = StartTime.plusHours(duration.toLong())
try {
// StartTime = LocalTime.parse(editStartTime.text, timeParseFormat)
// EndTime = LocalTime.parse(editEndTime.text, timeParseFormat)
StartDateTime = LocalDateTime.of(StartDate, StartTime)
EndDateTime = LocalDateTime.of(StartDate, EndTime)
} catch (err: Exception) {
}
val odt = OffsetDateTime.now()
val zoneOffset = odt.offset
booking.STime = OffsetDateTime.of(StartDateTime, zoneOffset)
booking.ETime = OffsetDateTime.of(EndDateTime, zoneOffset)
booking.InsertDate = OffsetDateTime.now()
booking.UpdateDate = OffsetDateTime.now()
booking.Name = chargePoint.Name
booking.ServiceFee = chargePoint.ServieFee
booking.kWhPrice = chargePoint.Tarif
if (sharedPref.getLoginToken().length > 0) {
CreateBooking(booking)
// findNavController().navigateUp()
} else {
findNavController().navigate(R.id.navigation_settings_login)
}
}
R.id.start_button -> {
currentTimeSet = 0
val timePickerDialog = TimePickerDialog(
requireContext(), this, StartTime.hour, StartTime.minute,
DateFormat.is24HourFormat(requireContext())
)
timePickerDialog.show()
}
}
}
var currentTimeSet: Int = 0 // 0: Start, 1: End
override fun onDateSet(view: DatePicker?, year: Int, month: Int, dayOfMonth: Int) {
StartDate = LocalDate.of(year, month, dayOfMonth)
// editTextDate.text = StartDate.format(dateFormat)
}
override fun onTimeSet(view: TimePicker?, hourOfDay: Int, minute: Int) {
if (currentTimeSet == 0) {
StartTime = LocalTime.of(hourOfDay, minute)
startButton.text = StartTime.format(timeFormat)
} else {
}
checkTimes()
checkAvailability()
}
private fun checkTimes() {
if (StartTime < LocalTime.now()) {
StartTime = LocalTime.now()
startButton.text = StartTime.format(timeFormat)
}
if (sharedPref.isAdhocBooking) {
//adhoc booking for at least 15 minutes
if (StartTime.plusMinutes(15) > EndTime) {
EndTime = StartTime.plusMinutes(15)
startButton.text = EndTime.format(timeFormat)
}
} else {
//reserve for at least one hour
if (StartTime.plusHours(1) > EndTime) {
EndTime = StartTime.plusHours(1)
startButton.text = EndTime.format(timeFormat)
}
}
}
val dayFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm")
private fun updateUiByExistingBookings() {
if (!isAvailable) {
val odt = OffsetDateTime.now()
val zoneOffset = odt.offset
// val firstBooking = OffsetDateTime.of( bookingsJson[0].STime?.minusMinutes(60)?.toLocalDateTime(), zoneOffset)
// val lastBooking = OffsetDateTime.of(bookingsJson[bookingsJson.size - 1].ETime?.plusMinutes(15)?.toLocalDateTime(), zoneOffset)
if (this::bookingsJson.isInitialized) {
scheduleConflictText.text =
"verfügbar vor " + bookingsJson[0].STime?.minusMinutes(60)?.localFormat() + " oder nach " + bookingsJson[bookingsJson.size - 1].ETime?.plusMinutes(15)?.localFormat() + " Uhr"
}
scheduleConflictText.isVisible = true
agbConstraint.alpha = 0.0f
BookNowButton.isVisible = false
setBookNowButtonState("Ladepunkt belegt", false, R.color.colorFalse, R.color.white)
} else {
scheduleConflictText.isVisible = false
BookNowButton.isVisible = true
agbConstraint.alpha = 1.0f
if (sharedPref.isAdhocBooking) {
setBookNowButtonState(resources.getString(R.string.charge_now), agbSwitch.isChecked, R.color.colorOkDark, R.color.white)
} else if (sharedPref.CurrentChargePoint.hasType(ChargePointType.AdHoc)) {
setBookNowButtonState("nur Direktladen möglich", false, R.color.TextMedium, R.color.white)
agbConstraint.alpha = 0.0f
} else {
setBookNowButtonState(resources.getString(R.string.book_now), agbSwitch.isChecked, R.color.colorOkDark, R.color.white)
}
}
}
private fun setBookNowButtonState(text: String, enabled: Boolean, bgColor: Int, tColor: Int) {
BookNowButton.text = text
BookNowButton.isEnabled = enabled
if (enabled) {
BookNowButton.setBackgroundColor(ContextCompat.getColor(requireContext(), bgColor))
BookNowButton.setTextColor(ContextCompat.getColor(requireContext(), tColor))
} else {
BookNowButton.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.SwitchOff))
BookNowButton.setTextColor(ContextCompat.getColor(requireContext(), R.color.Disabled))
}
}
private fun OffsetDateTime.localFormat(): String? {
val dayFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm")
return this.atZoneSameInstant(OffsetDateTime.now().toZonedDateTime().zone)?.format(dayFormatter)
}
private lateinit var bookingsJson: List<Booking>
private lateinit var accessProfileList: List<AccessProfile>
private val listOfBookings = object : TypeToken<ArrayList<Booking?>?>() {}.type
private val listOfAccessProfiles = object : TypeToken<ArrayList<AccessProfile?>?>() {}.type
private fun checkAvailability() {
val odt = OffsetDateTime.now()
val zoneOffset = odt.offset
val sTime: OffsetDateTime = OffsetDateTime.of(StartDate, StartTime, zoneOffset)
val eTime: OffsetDateTime = OffsetDateTime.of(StartDate, EndTime, zoneOffset)
CoroutineScope(Dispatchers.IO).launch {
val response =
kotlin.runCatching {
backendApi
.getBookingAvailability(
sharedPref.getLoginToken(),
sharedPref.CurrentChargePoint.Guid.toString(),
sTime.toString(),
sTime.toString()
)
.string()
}
withContext(Dispatchers.Main) {
response.fold(
onSuccess = {
Log.d("checkAvailability-Success", it)
var gSon = GsonBuilder()
// .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.setPrettyPrinting()
.registerTypeAdapter(
OffsetDateTime::class.java,
JsonDeserializer { json: JsonElement, type: Type?, context: JsonDeserializationContext? -> OffsetDateTime.parse(json.asString) } as JsonDeserializer<OffsetDateTime>)
.create()
bookingsJson = gSon.fromJson<List<Booking>>(it, listOfBookings).sortedBy { booking -> booking.STime?.toEpochSecond() }
if (bookingsJson != null && bookingsJson.count() > 0) {
isAvailable = !(bookingsJson.any { b ->
(b.StartTime == null && (
(b.STime?.minusMinutes(StartToNextStartMinutes)?.toEpochSecond()!! < sTime.toEpochSecond()) and (b.ETime?.plusMinutes(EndToNextStartMinutes)
?.toEpochSecond()!! > sTime.toEpochSecond())
or ((b.STime?.minusMinutes(StartToNextStartMinutes)?.toEpochSecond()!! < eTime.toEpochSecond()) and (b.ETime?.plusMinutes(EndToNextStartMinutes)
?.toEpochSecond()!! > eTime.toEpochSecond()))
or ((b.STime?.toEpochSecond()!! > sTime.toEpochSecond()) and (b.ETime?.plusMinutes(EndToNextStartMinutes)?.toEpochSecond()!! < eTime.toEpochSecond()))
)
) or (b.StartTime != null && b.EndTime == null && (
(b.StartTime?.toEpochSecond()!! < sTime.toEpochSecond()) and (b.ETime?.plusMinutes(EndToNextStartMinutes)?.toEpochSecond()!! > sTime.toEpochSecond())
or ((b.StartTime?.toEpochSecond()!! < eTime.toEpochSecond()) and (b.ETime?.plusMinutes(EndToNextStartMinutes)
?.toEpochSecond()!! > eTime.toEpochSecond()))
or ((b.StartTime?.toEpochSecond()!! > sTime.toEpochSecond()) and (b.ETime?.plusMinutes(EndToNextStartMinutes)
?.toEpochSecond()!! < eTime.toEpochSecond())))
) or (b.EndTime != null && (
(b.StartTime?.toEpochSecond()!! < sTime.toEpochSecond()) and (b.EndTime?.toEpochSecond()!! > sTime.toEpochSecond())
or ((b.StartTime?.toEpochSecond()!! < eTime.toEpochSecond()) and (b.EndTime?.toEpochSecond()!! > eTime.toEpochSecond()))
or ((b.StartTime?.toEpochSecond()!! > sTime.toEpochSecond()) and (b.EndTime?.toEpochSecond()!! < eTime.toEpochSecond())))
)
})
} else {
isAvailable = true
}
updateUiByExistingBookings()
//view?.hideKeyboard()
},
onFailure = {
Log.d("CreateBooking-Fail", it.message.orEmpty())
}
)
}
}
}
private fun getAccessProfile() {
val odt = OffsetDateTime.now()
val zoneOffset = odt.offset
val sTime: OffsetDateTime = OffsetDateTime.of(StartDate, StartTime, zoneOffset)
val eTime: OffsetDateTime = OffsetDateTime.of(StartDate, EndTime, zoneOffset)
CoroutineScope(Dispatchers.IO).launch {
val response =
kotlin.runCatching {
backendApi
.getAccessProfile(
sharedPref.getLoginToken(),
sharedPref.CurrentChargePoint.Guid.toString()
)
.string()
}
withContext(Dispatchers.Main) {
response.fold(
onSuccess = {
Log.d("checkAvailability-Success", it)
var gSon = GsonBuilder()
// .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.setPrettyPrinting()
.registerTypeAdapter(
OffsetDateTime::class.java,
JsonDeserializer { json: JsonElement, type: Type?, context: JsonDeserializationContext? -> OffsetDateTime.parse(json.asString) } as JsonDeserializer<OffsetDateTime>)
.create()
accessProfileList = gSon.fromJson<List<AccessProfile>>(it, listOfAccessProfiles)
accesProfileLoaded = true
if (accessProfileList != null && accessProfileList.count() > 0) {
updateAccessProfileUI()
} else {
}
updateUiByExistingBookings()
//view?.hideKeyboard()
},
onFailure = {
Log.d("CreateBooking-Fail", it.message.orEmpty())
accesProfileLoaded = false
}
)
}
}
}
private fun updateAccessProfileUI() {
if (accesProfileLoaded) {
val dayOfWeek = (2f.pow(StartDate.dayOfWeek.value - 1)).roundToInt()
val todaysProfile = accessProfileList.find { ap -> (ap.Weekday and dayOfWeek) > 0 }
if (todaysProfile != null) {
todaysProfile.calcTimeSlot()
Log.d("Found AccessProfile", todaysProfile.Guid.toString())
var hasRates: Boolean = todaysProfile.hasRates(AccessProfile.Rates.Pv)
PvTarifLayout.isVisible = hasRates
if (hasRates) {
PvHoursTextView.text = "(" + getTimeFromDuration(todaysProfile.PvStarts) + "-" + getTimeFromDuration(todaysProfile.PvEnds) + ")"
TarifTextView.text = String.format(Locale.GERMAN, "%.0f", sharedPref.CurrentChargePoint.MoonShineRate) + " Ct/kWh"
PvTarifTextView.text = String.format(Locale.GERMAN, "%.0f", sharedPref.CurrentChargePoint.SunShineRate) + " Ct/kWh"
}
}
}
}
private fun getTimeFromDuration(duration: Duration): String {
val hours: Long = duration.toHours() % 24
val minutes: Long = duration.toMinutes() % 60
return "%02d".format(hours) + "%02d".format(minutes)
}
private val backendApi = BackendApiFactory(baseUrl).create()
private val elementOfBooking = object : TypeToken<Booking?>() {}.type
private fun CreateBooking(booking: Booking) {
CoroutineScope(Dispatchers.IO).launch {
val response =
kotlin.runCatching {
backendApi
.createBooking(
sharedPref.getLoginToken(),
booking.CpGuid.toString(),
booking.STime.toString(),
booking.ETime.toString()
)
.string()
}
withContext(Dispatchers.Main) {
response.fold(
onSuccess = {
Log.d("CreateBooking-Success", it)
var gSon = GsonBuilder()
// .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.setPrettyPrinting()
.registerTypeAdapter(
OffsetDateTime::class.java,
JsonDeserializer { json: JsonElement, type: Type?, context: JsonDeserializationContext? -> OffsetDateTime.parse(json.asString) } as JsonDeserializer<OffsetDateTime>)
.create()
var _booking = gSon.fromJson<Booking>(it, elementOfBooking)
if (_booking != null) {
if (sharedPref.isAdhocBooking) {
sharedPref.setBooking(_booking)
findNavController().navigate(R.id.navigation_charge)
} else {
findNavController().navigate(R.id.navigation_bookings)
}
} else {
}
//view?.hideKeyboard()
},
onFailure = {
Log.d("CreateBooking-Fail", it.message.orEmpty())
}
)
}
}
}
override fun onProgressChanged(p0: SeekBar?, progress: Int, p2: Boolean) {
duration = timeSlots[progress]
}
override fun onStartTrackingTouch(p0: SeekBar?) {
}
override fun onStopTrackingTouch(p0: SeekBar?) {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment