Add Android app implementation with core features

Co-authored-by: Kenearos <86194771+Kenearos@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-11-14 17:24:17 +00:00
parent 3069fcd8ba
commit 976d494d9f
28 changed files with 1784 additions and 3 deletions

View file

@ -0,0 +1,177 @@
package com.dienstplan.nrw
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.dienstplan.nrw.data.DutyDataStore
import com.dienstplan.nrw.databinding.ActivityDutyEntryBinding
import com.dienstplan.nrw.databinding.ItemDutyEntryBinding
import com.dienstplan.nrw.model.DutyEntry
import java.text.SimpleDateFormat
import java.util.*
class DutyEntryActivity : AppCompatActivity() {
private lateinit var binding: ActivityDutyEntryBinding
private lateinit var adapter: DutyEntryAdapter
private var year: Int = 0
private var month: Int = 0
private val dateFormat = SimpleDateFormat("dd.MM.yyyy", Locale.GERMANY)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityDutyEntryBinding.inflate(layoutInflater)
setContentView(binding.root)
year = intent.getIntExtra("year", 0)
month = intent.getIntExtra("month", 0)
if (year == 0 || month == 0) {
finish()
return
}
setupUI()
loadDuties()
}
private fun setupUI() {
val monthName = getMonthName(month)
binding.tvSelectedMonth.text = String.format("%s %d", monthName, year)
adapter = DutyEntryAdapter { duty ->
deleteDuty(duty)
}
binding.rvDuties.layoutManager = LinearLayoutManager(this)
binding.rvDuties.adapter = adapter
binding.btnAddDuty.setOnClickListener {
showAddDutyDialog()
}
binding.btnSave.setOnClickListener {
Toast.makeText(this, "Dienste gespeichert", Toast.LENGTH_SHORT).show()
finish()
}
}
private fun loadDuties() {
val duties = DutyDataStore.getDutiesForMonth(year, month)
adapter.submitList(duties)
}
private fun showAddDutyDialog() {
val dates = DutyDataStore.getDatesInMonth(year, month)
val dateStrings = dates.map { dateFormat.format(it) }.toTypedArray()
AlertDialog.Builder(this)
.setTitle("Datum auswählen")
.setItems(dateStrings) { _, which ->
val selectedDate = dates[which]
showEmployeeDialog(selectedDate)
}
.show()
}
private fun showEmployeeDialog(date: Date) {
val builder = AlertDialog.Builder(this)
val inflater = layoutInflater
val dialogView = inflater.inflate(R.layout.dialog_add_duty, null)
val etName = dialogView.findViewById<android.widget.EditText>(R.id.etEmployeeName)
val etShare = dialogView.findViewById<android.widget.EditText>(R.id.etShare)
builder.setView(dialogView)
.setTitle("Dienst hinzufügen - ${dateFormat.format(date)}")
.setPositiveButton("Hinzufügen") { _, _ ->
val name = etName.text.toString().trim()
val shareStr = etShare.text.toString().trim()
if (name.isEmpty()) {
Toast.makeText(this, R.string.error_empty_name, Toast.LENGTH_SHORT).show()
return@setPositiveButton
}
val share = shareStr.toDoubleOrNull() ?: 0.0
if (share <= 0.0 || share > 1.0) {
Toast.makeText(this, R.string.error_invalid_share, Toast.LENGTH_SHORT).show()
return@setPositiveButton
}
val monthKey = DutyDataStore.getMonthKey(year, month)
val duty = DutyEntry(
date = date,
employeeName = name,
share = share,
monthKey = monthKey
)
DutyDataStore.addDuty(duty)
loadDuties()
}
.setNegativeButton("Abbrechen", null)
.show()
}
private fun deleteDuty(duty: DutyEntry) {
DutyDataStore.deleteDuty(duty.id)
loadDuties()
}
private fun getMonthName(month: Int): String {
val months = resources.getStringArray(R.array.months)
return if (month in 1..12) months[month - 1] else ""
}
}
class DutyEntryAdapter(
private val onDelete: (DutyEntry) -> Unit
) : RecyclerView.Adapter<DutyEntryAdapter.ViewHolder>() {
private var duties = listOf<DutyEntry>()
private val dateFormat = SimpleDateFormat("dd.MM.yyyy", Locale.GERMANY)
fun submitList(newDuties: List<DutyEntry>) {
duties = newDuties.sortedBy { it.date }
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = ItemDutyEntryBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(duties[position])
}
override fun getItemCount() = duties.size
inner class ViewHolder(
private val binding: ItemDutyEntryBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(duty: DutyEntry) {
binding.tvDate.text = dateFormat.format(duty.date)
binding.etEmployeeName.setText(duty.employeeName)
binding.etShare.setText(duty.share.toString())
binding.btnDelete.setOnClickListener {
onDelete(duty)
}
// Disable editing in the list view
binding.etEmployeeName.isEnabled = false
binding.etShare.isEnabled = false
}
}
}