Last active
January 11, 2023 16:07
-
-
Save kyodgorbek/401284de161d215a5c13732cebd098cd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2019 Google Inc. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package com.chargeatfriends.android.network | |
import android.app.NotificationManager | |
import android.app.PendingIntent | |
import android.content.Context | |
import android.content.Intent | |
import android.graphics.BitmapFactory | |
import androidx.core.app.NotificationCompat | |
import com.chargeatfriends.android.MainActivity | |
import com.chargeatfriends.android.R | |
import com.chargeatfriends.android.ui.approve.ApprovalActivity | |
// Notification ID. | |
private val NOTIFICATION_ID = 0 | |
private val REQUEST_CODE = 0 | |
private val FLAGS = 0 | |
private lateinit var contentIntent: Intent | |
// TODO: Step 1.1 extension function to send messages (GIVEN) | |
/** | |
* Builds and delivers the notification. | |
* | |
* @param messageBody, notification text. | |
* @param context, activity context. | |
*/ | |
fun NotificationManager.sendNotification(messageBody: String, title: String, applicationContext: Context, color: Int, targetAction: Int) { | |
// TODO: Step 1.11 create intent | |
when(targetAction){ | |
R.id.mainActivity ->{ | |
contentIntent = Intent(applicationContext, MainActivity::class.java) | |
} | |
R.id.approvalActivity->{ | |
contentIntent = Intent(applicationContext, ApprovalActivity::class.java).apply { | |
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK | |
} | |
} | |
else ->{ | |
contentIntent = Intent(applicationContext, MainActivity::class.java) | |
} | |
} | |
// TODO: Step 1.12 create PendingIntent | |
val contentPendingIntent = PendingIntent.getActivity( | |
applicationContext, | |
NOTIFICATION_ID, | |
contentIntent, | |
PendingIntent.FLAG_UPDATE_CURRENT | |
) | |
// TODO: Step 2.0 add style | |
val eggImage = BitmapFactory.decodeResource( | |
applicationContext.resources, | |
R.drawable.ic_launcher_round | |
) | |
val bigPicStyle = NotificationCompat.BigPictureStyle() | |
.bigPicture(eggImage) | |
.bigLargeIcon(null) | |
// TODO: Step 2.2 add snooze action | |
// val snoozeIntent = Intent(applicationContext, SnoozeReceiver::class.java) | |
// val snoozePendingIntent: PendingIntent = | |
// PendingIntent.getBroadcast(applicationContext, REQUEST_CODE, snoozeIntent, FLAGS) | |
// TODO: Step 1.2 get an instance of NotificationCompat.Builder | |
// Build the notification | |
val builder = NotificationCompat.Builder( | |
applicationContext, | |
// TODO: Step 1.8 use the new 'breakfast' notification channel | |
applicationContext.getString(R.string.notification_channel) | |
) | |
// TODO: Step 1.3 set title, text and icon to builder | |
.setSmallIcon(R.drawable.ic_logo_24dp) | |
.setColor(color) | |
.setContentTitle(title) | |
.setContentText(messageBody) | |
// TODO: Step 1.13 set content intent | |
.setContentIntent(contentPendingIntent) | |
// TODO: Step 2.1 add style to builder | |
.setStyle(bigPicStyle) | |
.setLargeIcon(eggImage) | |
// TODO: Step 2.3 add snooze action | |
// .addAction( | |
// R.drawable.ic_launcher_foreground, | |
// applicationContext.getString(R.string.notification_snooze), | |
// snoozePendingIntent | |
// ) | |
// TODO: Step 2.5 set priority | |
.setPriority(NotificationCompat.PRIORITY_HIGH) | |
.setAutoCancel(true) | |
// TODO Step 1.4 call notify | |
// Deliver the notification | |
notify(NOTIFICATION_ID, builder.build()) | |
} | |
// TODO: Step 1.14 Cancel all notifications | |
/** | |
* Cancels all notifications. | |
* | |
*/ | |
fun NotificationManager.cancelNotifications() { | |
cancelAll() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class PushNotificationService : FirebaseMessagingService() { | |
/** | |
* Called when message is received. | |
* | |
* @param remoteMessage Object representing the message received from Firebase Cloud Messaging. | |
*/ | |
// [START receive_message] | |
override fun onMessageReceived(remoteMessage: RemoteMessage) { | |
// Not getting messages here? See why this may be: https://goo.gl/39bRNJ | |
Log.d(TAG, "From: ${remoteMessage?.from}") | |
// TODO Step 3.5 check messages for data | |
// Check if message contains a data payload. | |
var title = resources.getString(R.string.hi) | |
var color = ContextCompat.getColor(this, R.color.colorCaF) | |
var targetAction = R.id.mainActivity; | |
remoteMessage?.data?.let { | |
if (it.containsKey("action")) { | |
val intent = Intent(it["action"]) | |
intent.putExtra("guid", it["guid"]) | |
intent.putExtra("state", it["state"]) | |
when (it["action"]) { | |
START -> { | |
val broadcaster = LocalBroadcastManager.getInstance(baseContext) | |
intent.putExtra("meterstart", it["meterstart"]) | |
intent.putExtra("updatedate", it["updatedate"]) | |
title = "Ladevorgang gestartet" | |
broadcaster.sendBroadcast(intent) | |
} | |
END -> { | |
val broadcaster = LocalBroadcastManager.getInstance(baseContext) | |
intent.putExtra("meterend", it["meterend"]) | |
intent.putExtra("updatedate", it["updatedate"]) | |
title = "Ladevorgang beendet" | |
broadcaster.sendBroadcast(intent) | |
} | |
END_PAYMENT -> { | |
val broadcaster = LocalBroadcastManager.getInstance(baseContext) | |
intent.putExtra("amount", it["amount"]) | |
intent.putExtra("updatedate", it["updatedate"]) | |
title = "Zahlung erfolgt" | |
broadcaster.sendBroadcast(intent) | |
} | |
VERIFICATION_APPROVED -> { | |
val broadcaster = LocalBroadcastManager.getInstance(baseContext) | |
color = ContextCompat.getColor(this, R.color.colorAccent) | |
targetAction = R.id.navigation_charge; | |
intent.putExtra("amount", it["amount"]) | |
intent.putExtra("updatedate", it["updatedate"]) | |
title = "Zählerstand bestätigt" | |
broadcaster.sendBroadcast(intent) | |
} | |
VERIFICATION_REQUESTED -> { | |
val broadcaster = LocalBroadcastManager.getInstance(baseContext) | |
color = ContextCompat.getColor(this, R.color.colorAccent) | |
targetAction = R.id.approvalActivity; | |
intent.putExtra("start", it["start"]) | |
appCommon.RemoteBookingGuid = UUID.fromString(it["guid"]) | |
appCommon.RemotBookkingIsCpo = true | |
try { | |
val _state: Long = it["state"]!!.toLong() | |
if (_state > 0) { | |
val iState = if ((BookingState.ChargingEnded.bitmask and _state) > 0) 1 else 0 | |
if (iState == 1) { | |
title = "Zählerstande bestätigen" | |
intent.putExtra("end", it["end"]) | |
} else { | |
title = "Start-Zählerstand bestätigen" | |
} | |
} | |
broadcaster.sendBroadcast(intent) | |
} catch (ex: Exception) { | |
} | |
} | |
VERIFICATION_REJECTED -> { | |
val broadcaster = LocalBroadcastManager.getInstance(baseContext) | |
color = ContextCompat.getColor(this, R.color.colorAccent) | |
targetAction = R.id.approvalActivity; | |
intent.putExtra("start", it["start"]) | |
appCommon.RemoteBookingGuid = UUID.fromString(it["guid"]) | |
appCommon.RemotBookkingIsCpo = false | |
try { | |
val _state: Long = it["state"]!!.toLong() | |
if (_state > 0) { | |
val iState = if ((BookingState.ChargingEnded.bitmask and _state) > 0) 1 else 0 | |
title = "Zählerstände überprüfen" | |
} | |
broadcaster.sendBroadcast(intent) | |
} catch (ex: Exception) { | |
} | |
} | |
CORRECTION_REJECTED -> { | |
val broadcaster = LocalBroadcastManager.getInstance(baseContext) | |
color = ContextCompat.getColor(this, R.color.colorAccent) | |
targetAction = R.id.approvalActivity; | |
intent.putExtra("start", it["start"]) | |
appCommon.RemoteBookingGuid = UUID.fromString(it["guid"]) | |
appCommon.RemotBookkingIsCpo = true | |
try { | |
val _state: Long = it["state"]!!.toLong() | |
if (_state > 0) { | |
val iState = if ((BookingState.ChargingEnded.bitmask and _state) > 0) 1 else 0 | |
title = resources.getString(R.string.correction_rejected) | |
} | |
broadcaster.sendBroadcast(intent) | |
} catch (ex: Exception) { | |
} | |
} | |
CHARGING_STARTED -> { | |
color = ContextCompat.getColor(this, R.color.colorActive) | |
title = "Ladevorgang gestartet" | |
} | |
THREE_D_AUTH_REQUIRED ->{ | |
// 3d auth webview | |
} | |
PAYMENT_START_NEXT ->{ | |
// payment is blocked | |
// you can start charging | |
// ocpp charged will start at backend | |
// manual updated ui enter meta value | |
} | |
} | |
} | |
Log.d(TAG, "Message data payload: " + remoteMessage.data) | |
} | |
// TODO Step 3.6 check messages for notification and call sendNotification | |
// Check if message contains a notification payload. | |
remoteMessage?.notification?.let { | |
Log.d(TAG, "Message Notification Body: ${it.body}") | |
sendNotification(it.body!!, title, color, targetAction) | |
} | |
} | |
// [END receive_message] | |
//TODO Step 3.2 log registration token | |
// [START on_new_token] | |
/** | |
* Called if InstanceID token is updated. This may occur if the security of | |
* the previous token had been compromised. Note that this is called when the InstanceID token | |
* is initially generated so this is where you would retrieve the token. | |
*/ | |
override fun onNewToken(token: String) { | |
Log.d(TAG, "Refreshed token: $token") | |
// If you want to send messages to this application instance or | |
// manage this apps subscriptions on the server side, send the | |
// Instance ID token to your app server. | |
appCommon.sendRegistrationToServer(token) | |
} | |
// [END on_new_token] | |
/** | |
* Persist token to third-party servers. | |
* | |
* @param token The new token. | |
*/ | |
/** | |
* Create and show a simple notification containing the received FCM message. | |
* | |
* @param messageBody FCM message body received. | |
*/ | |
private fun sendNotification(messageBody: String, title: String, color: Int, target: Int) { | |
val notificationManager = ContextCompat.getSystemService( | |
applicationContext, | |
NotificationManager::class.java | |
) as NotificationManager | |
notificationManager.sendNotification(messageBody, title, applicationContext, color, target) | |
} | |
companion object { | |
private const val TAG = "MyFirebaseMsgService" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.chargeatfriends.android.ui.reserve | |
import android.app.DatePickerDialog | |
import android.app.TimePickerDialog | |
import android.os.Bundle | |
import android.text.format.DateFormat | |
import android.text.method.LinkMovementMethod | |
import android.util.DisplayMetrics | |
import android.util.Log | |
import android.view.LayoutInflater | |
import android.view.View | |
import android.view.ViewGroup | |
import android.widget.* | |
import androidx.constraintlayout.widget.ConstraintLayout | |
import androidx.core.content.ContextCompat | |
import androidx.core.view.isVisible | |
import androidx.fragment.app.Fragment | |
import androidx.navigation.fragment.findNavController | |
import com.chargeatfriends.android.R | |
import com.chargeatfriends.android.model.AccessProfile | |
import com.chargeatfriends.android.model.Booking | |
import com.chargeatfriends.android.model.ChargePointType | |
import com.chargeatfriends.android.model.appCommon | |
import com.chargeatfriends.android.model.stripe.BackendApiFactory | |
import com.chargeatfriends.android.network.EndToNextStartMinutes | |
import com.chargeatfriends.android.network.StartToNextStartMinutes | |
import com.chargeatfriends.android.network.baseUrl | |
import com.google.gson.GsonBuilder | |
import com.google.gson.JsonDeserializationContext | |
import com.google.gson.JsonDeserializer | |
import com.google.gson.JsonElement | |
import com.google.gson.reflect.TypeToken | |
import com.squareup.picasso.Picasso | |
import kotlinx.coroutines.CoroutineScope | |
import kotlinx.coroutines.Dispatchers | |
import kotlinx.coroutines.launch | |
import kotlinx.coroutines.withContext | |
import java.lang.reflect.Type | |
import java.time.* | |
import java.time.format.DateTimeFormatter | |
import java.util.* | |
import kotlin.math.pow | |
import kotlin.math.roundToInt | |
class ReserveFragment : Fragment(R.layout.reserve_fragment), DatePickerDialog.OnDateSetListener, | |
TimePickerDialog.OnTimeSetListener, SeekBar.OnSeekBarChangeListener { | |
// private lateinit var viewModel: ReserveViewModel | |
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 + appCommon.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? { | |
val root = inflater.inflate(R.layout.reserve_fragment, container, false) | |
/* val textView: TextView = root.findViewById(R.id.text_notifications) | |
settingsViewModel.text.observe(viewLifecycleOwner, Observer { | |
textView.text = it | |
})*/ | |
if (appCommon.getLoginToken().isEmpty()) { | |
findNavController().navigate(R.id.navigation_settings_login) | |
} | |
if (appCommon.isChargePointInitialized()) { | |
/// Head Section Name, Power | |
StationNameView = root.findViewById(R.id.StationNameTextView) as TextView | |
StationNameView.text = appCommon.CurrentChargePoint.CpName | |
PowerTextView = root.findViewById(R.id.PowerTextView) as TextView | |
PowerTextView.text = String.format(Locale.GERMAN, "%.1f", appCommon.CurrentChargePoint.Power) + " kW" | |
// ConnectorNameView = root.findViewById(R.id.ConnectorNameTextView) as TextView | |
// ConnectorNameView.text = appCommon.CurrentChargePoint.Name | |
/// Open Hours - Tarifs | |
OpenHoursTextView = root.findViewById(R.id.OpenHoursTextView) as TextView | |
TarifTextView = root.findViewById(R.id.TarifTextView) as TextView | |
TarifTextView.text = String.format(Locale.GERMAN, "%.0f", appCommon.CurrentChargePoint.Tarif) + " Ct/kWh" | |
PvTarifLayout = root.findViewById(R.id.PvTarifLayout) as ConstraintLayout | |
PvTarifLayout.isVisible = false | |
PvTarifTextView = root.findViewById(R.id.PvTarifTextView) as TextView | |
PvTarifTextView.text = String.format(Locale.GERMAN, "%.0f", appCommon.CurrentChargePoint.SunShineRate) + " Ct/kWh" | |
PvHoursTextView = root.findViewById(R.id.PvHoursTextView) as TextView | |
PvTarifTextView.text = "" | |
/// Timing | |
LinearLayoutTiming = root.findViewById(R.id.LinearLayoutTiming) as LinearLayout | |
LinearLayoutTiming.isVisible = !appCommon.CurrentChargePoint.hasType(ChargePointType.AdHoc) | |
// Dropdown Days | |
DayDropdown = root.findViewById(R.id.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 (appCommon.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 = root.findViewById(R.id.start_button) as Button | |
startButton.setOnClickListener(clickListener) | |
// editEndTime = root.findViewById(R.id.editEndTime) as TextView | |
EndTime = if (appCommon.CurrentChargePoint.hasType(ChargePointType.AdHoc)) { // pure Ad Hoc station | |
LocalTime.now().plusSeconds(3600 * 8) | |
} else if (appCommon.isAdhocBooking) { // Ad hoc @ reservable station | |
LocalTime.now().plusSeconds(3600 * 4) | |
} else { // reserved booking | |
LocalTime.now().plusSeconds(3600 + 3600 * 4) | |
} | |
durationSlider = root.findViewById(R.id.durationSlider) as SeekBar | |
/// 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 = root.findViewById(R.id.BookNowButton) as Button | |
if (appCommon.isAdhocBooking) { | |
linearLayoutStartTime = root.findViewById(R.id.linearLayoutStartTime) as LinearLayout | |
linearLayoutStartTime.isVisible = false | |
BookNowButton.text = resources.getString(R.string.charge_now) | |
StartTime = LocalTime.now() | |
} else if (appCommon.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 = root.findViewById(R.id.switchAgb) as androidx.appcompat.widget.SwitchCompat | |
agbSwitch.isChecked = false | |
BookNowButton.isEnabled = false | |
scheduleConflictText = root.findViewById(R.id.scheduleConflictText) as TextView | |
agbConstraint = root.findViewById(R.id.agbConstraint) as ConstraintLayout | |
BookNowButton.setOnClickListener(clickListener) | |
agbSwitch.setOnClickListener(clickListener) | |
agbText = root.findViewById(R.id.AgbText) as TextView | |
agbText.movementMethod = LinkMovementMethod.getInstance() | |
/// Background Image | |
ThumbnailImageView = root.findViewById(R.id.imageView) as 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(appCommon.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() | |
} | |
return root | |
} | |
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 = appCommon.CurrentChargePoint | |
// booking.Token = appCommon.getLoginToken() | |
booking.FriendGuid = appCommon.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 (appCommon.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 (appCommon.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 (appCommon.isAdhocBooking) { | |
setBookNowButtonState(resources.getString(R.string.charge_now), agbSwitch.isChecked, R.color.colorOkDark, R.color.white) | |
} else if (appCommon.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( | |
appCommon.getLoginToken(), | |
appCommon.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( | |
appCommon.getLoginToken(), | |
appCommon.CurrentChargePoint.Guid.toString() | |
) | |
.string() | |
} | |
withContext(Dispatchers.Main) { | |
response.fold( | |
onSuccess = { | |
Log.d("checkAvailability-Success", it) | |
Log.d("checkAvailability-Success", "getAccessProfile: ${it.length}") | |
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", appCommon.CurrentChargePoint.MoonShineRate) + " Ct/kWh" | |
PvTarifTextView.text = String.format(Locale.GERMAN, "%.0f", appCommon.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( | |
appCommon.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 (appCommon.isAdhocBooking) { | |
appCommon.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