Aktualisiere gesamtes Design (+ Technikeranzeige in Projektübersicht)

master
Niko Diamadis 4 years ago
parent a2790b1938
commit 14c37b1be5
Signed by: niko
GPG Key ID: BE53B0B17B1B142E

@ -69,13 +69,12 @@ dependencies {
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation 'com.afollestad.material-dialogs:bottomsheets:3.3.0'
implementation 'com.afollestad.material-dialogs:input:3.3.0'
implementation 'com.airbnb.android:lottie:3.7.0'
implementation 'com.amitshekhar.android:android-networking:1.0.2'
implementation 'com.github.cyb3rko:about-icons:1.4.0'
implementation 'com.github.GrenderG:Toasty:1.5.0'
implementation "com.github.parse-community.Parse-SDK-Android:parse:1.26.0"
implementation 'com.google.android.material:material:1.3.0-alpha03'
implementation 'com.google.android.material:material:1.6.1'
implementation 'com.google.firebase:firebase-crashlytics-ktx:18.2.1'
implementation 'com.itextpdf:itextpdf:5.5.13.2'
implementation "com.mikepenz:aboutlibraries:$about_libraries_version"

@ -13,7 +13,7 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:requestLegacyExternalStorage="true"
android:theme="@style/DarkActionbar"
android:theme="@style/Theme.TechnikLogger"
tools:ignore="AllowBackup"
android:name=".App">
<activity android:name="com.cyb3rko.techniklogger.MainActivity"
@ -26,7 +26,7 @@
</intent-filter>
</activity>
<activity android:name="com.mikepenz.aboutlibraries.ui.LibsActivity"
android:theme="@style/NoActionBar"
android:theme="@style/Theme.TechnikLogger.NoActionBar"
android:screenOrientation="portrait" />
<meta-data

@ -33,6 +33,7 @@ class MainActivity : AppCompatActivity() {
_binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.toolbar)
navController = findNavController(R.id.nav_host_fragment_activity_main)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
@ -46,7 +47,7 @@ class MainActivity : AppCompatActivity() {
sharedPrefEditor.apply()
if (sharedPref.getString("name", "") == "") {
showNameDialog(this, sharedPref, sharedPrefEditor)
showNameDialog(this, sharedPref, sharedPrefEditor, false)
} else {
updateCheck()
}

@ -12,6 +12,7 @@ import com.androidnetworking.AndroidNetworking
import com.androidnetworking.error.ANError
import com.androidnetworking.interfaces.StringRequestListener
import com.cyb3rko.installer.DownloadApk
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import es.dmoral.toasty.Toasty
import java.nio.charset.StandardCharsets
@ -53,14 +54,13 @@ internal fun updateCheck(activity: MainActivity) {
}
internal fun showDownloadDialog(context: Context, newestVersion: String) {
MaterialDialog(context).show {
title(0, "Neues Update verfügbar")
message(0, "Das Update '$newestVersion' steht zum Download bereit!\n\nAktuell installierte Version: '${BuildConfig.VERSION_NAME}'")
positiveButton(0, "Herunterladen") {
it.cancel()
MaterialAlertDialogBuilder(context)
.setTitle("Neues Update verfügbar")
.setMessage("Das Update '$newestVersion' steht zum Download bereit!\n\nAktuell installierte Version: '${BuildConfig.VERSION_NAME}'")
.setPositiveButton("Herunterladen") { _, _ ->
downloadApk(context, newestVersion)
}
}
.show()
}
private fun downloadApk(context: Context, newestVersion: String) {

@ -1,12 +1,16 @@
package com.cyb3rko.techniklogger
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.content.SharedPreferences
import androidx.core.content.ContextCompat.startActivity
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.input.getInputField
import com.afollestad.materialdialogs.input.input
import android.view.View
import android.view.inputmethod.InputMethodManager
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
internal const val CLASS_EINSATZ = "Einsatz"
internal const val CLASS_JAHR = "Jahr"
@ -36,27 +40,59 @@ internal const val SHARED_PREFERENCE = "Safe"
internal fun showNameDialog(
activity: MainActivity,
sharedPref: SharedPreferences,
sharedPrefEditor: SharedPreferences.Editor
sharedPrefEditor: SharedPreferences.Editor,
cancelable: Boolean
) {
val currentName = sharedPref.getString(COLUMN_TECHNIKER_NAME, "")
MaterialDialog(activity)
.cancelable(false)
.title(0, "Bitte gib deinen Namen ein")
.show {
input(hint = "Dein Name", prefill = currentName, waitForPositiveButton = false) { dialog, inputName ->
try {
if (!inputName[0].isUpperCase()) {
dialog.getInputField().error = "Der Anfangsbuchstabe sollte groß sein"
}
} catch (ignored: IndexOutOfBoundsException) {
}
@SuppressLint("InflateParams")
val inputField = activity.layoutInflater
.inflate(R.layout.dialog_view_name, null)
.findViewById<TextInputLayout>(R.id.md_input)
positiveButton(0, "Speichern") {
sharedPrefEditor.putString("name", inputName.toString().trim()).commit()
activity.finish()
startActivity(activity, Intent(activity, MainActivity::class.java), null)
@SuppressLint("InflateParams")
val inputTextField = inputField
.findViewById<TextInputEditText>(R.id.md_input_text).apply {
setText(currentName)
}
MaterialAlertDialogBuilder(activity)
.setView(inputField)
.setCancelable(cancelable)
.setTitle("Bitte gib deinen Namen ein")
.setPositiveButton(android.R.string.ok, null)
.create().apply {
setOnShowListener {
getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
closeKeyboard(activity)
val inputName = inputTextField.text.toString()
try {
if (!inputName[0].isUpperCase()) {
inputField.error = "Der Anfangsbuchstabe sollte groß sein"
}
} catch (ignored: IndexOutOfBoundsException) {
}
if (inputName != currentName) {
sharedPrefEditor.putString("name", inputName.trim()).commit()
activity.finish()
activity.startActivity(Intent(activity, MainActivity::class.java))
} else {
dismiss()
}
}
}
show()
}
}
private fun closeKeyboard(activity: Activity) {
val imm = activity.getSystemService(
AppCompatActivity.INPUT_METHOD_SERVICE
) as InputMethodManager
var view = activity.currentFocus
if (view == null) {
view = View(activity)
}
imm.hideSoftInputFromWindow(view.windowToken, 0)
}

@ -1,5 +1,6 @@
package com.cyb3rko.techniklogger.fragments
import android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
import android.text.Html
@ -11,7 +12,6 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.afollestad.materialdialogs.MaterialDialog
import com.cyb3rko.techniklogger.*
import com.cyb3rko.techniklogger.CLASS_EINSATZ
import com.cyb3rko.techniklogger.COLUMN_EINSATZ_JAHR
@ -19,6 +19,7 @@ import com.cyb3rko.techniklogger.CURRENT_YEAR
import com.cyb3rko.techniklogger.SHARED_PREFERENCE
import com.cyb3rko.techniklogger.databinding.FragmentEinsatzPusherBinding
import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat
import com.parse.ParseObject
@ -43,6 +44,7 @@ class EinsatzPusherFragment : Fragment() {
private var duration = ""
private lateinit var location: String
private lateinit var name: String
@SuppressLint("SimpleDateFormat")
private val simpleDateFormat = SimpleDateFormat("dd.MM.yyyy")
// This property is only valid between onCreateView and onDestroyView.
@ -123,32 +125,32 @@ class EinsatzPusherFragment : Fragment() {
if (childKey != "") {
binding.deleteButton.visibility = View.VISIBLE
binding.deleteButton.setOnClickListener {
MaterialDialog(myContext)
.show {
message(0, "Möchtest du diesen Einsatz entfernen?")
positiveButton(0, "Ja") {
val query = ParseQuery<ParseObject>(CLASS_TEILNAHME)
query.whereEqualTo("an", ParseObject.createWithoutData(CLASS_EINSATZ, childKey))
query.findInBackground { objects, e ->
if (e == null) {
ParseObject.deleteAllInBackground(objects) {
entry.deleteInBackground {
if (it == null) {
findNavController().navigate(R.id.navigation_listing)
} else {
Toasty.error(myContext, "Fehler bei Einsatz-Löschung").show()
Log.e("TechnikLogger.Einsätze", it.message.toString())
}
MaterialAlertDialogBuilder(myContext)
.setTitle("Einsatz entfernen")
.setMessage("Möchtest du diesen Einsatz entfernen?")
.setPositiveButton("Ja") { _, _ ->
val query = ParseQuery<ParseObject>(CLASS_TEILNAHME)
query.whereEqualTo("an", ParseObject.createWithoutData(CLASS_EINSATZ, childKey))
query.findInBackground { objects, e ->
if (e == null) {
ParseObject.deleteAllInBackground(objects) {
entry.deleteInBackground {
if (it == null) {
findNavController().navigate(R.id.navigation_listing)
} else {
Toasty.error(myContext, "Fehler bei Einsatz-Löschung").show()
Log.e("TechnikLogger.Einsätze", it.message.toString())
}
}
} else {
Toasty.error(myContext, "Fehler bei Teilnehmer-Löschung").show()
Log.e("TechnikLogger.Einsätze", e.message.toString())
}
} else {
Toasty.error(myContext, "Fehler bei Teilnehmer-Löschung").show()
Log.e("TechnikLogger.Einsätze", e.message.toString())
}
}
negativeButton(0, "Abbrechen")
}
.setNegativeButton("Abbrechen", null)
.show()
}
}

@ -45,8 +45,6 @@ class ListingFragment : Fragment() {
if (sharedPref.getString(CURRENT_YEAR, "") == "") {
findNavController().navigate(R.id.navigation_years)
}
val actionBarTitle = "Einsätze ${sharedPref.getString(CURRENT_YEAR_NAME, "")}"
(requireActivity() as MainActivity).setActionBarTitle(actionBarTitle)
binding.loadingAnimation.playAnimation()
@ -115,9 +113,26 @@ class ListingFragment : Fragment() {
return root
}
override fun onPause() {
super.onPause()
(requireActivity() as MainActivity).setActionBarSubtitle(null)
override fun onStart() {
super.onStart()
updateActionBarNumber()
val actionBarTitle = "Einsätze ${sharedPref.getString(CURRENT_YEAR_NAME, "")}"
(requireActivity() as MainActivity).setActionBarTitle(actionBarTitle)
}
override fun onStop() {
super.onStop()
updateActionBarNumber(true)
}
private fun updateActionBarNumber(empty: Boolean = false) {
(requireActivity() as MainActivity).setActionBarSubtitle(
if (empty) {
null
} else {
projectsAdapter.currentList.size.toString()
}
)
}
private fun loadEntries() {
@ -152,7 +167,7 @@ class ListingFragment : Fragment() {
showAnimation(false)
binding.swipeRefreshLayout.isRefreshing = false
projectsAdapter.submitList(data)
(requireActivity() as MainActivity).setActionBarSubtitle(objects.size.toString())
updateActionBarNumber()
projectsAdapter.registerAdapterDataObserver(object: RecyclerView.AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
@ -264,7 +279,7 @@ class ListingFragment : Fragment() {
// as you specify a parent activity in AndroidManifest.xml.
when (item.itemId) {
R.id.action_year -> findNavController().navigate(R.id.navigation_years)
R.id.action_rename -> showNameDialog(requireActivity() as MainActivity, sharedPref, sharedPrefEditor)
R.id.action_rename -> showNameDialog(requireActivity() as MainActivity, sharedPref, sharedPrefEditor, true)
R.id.action_about -> findNavController().navigate(R.id.navigation_about)
}

@ -6,22 +6,20 @@ import android.content.SharedPreferences
import android.os.Bundle
import android.text.Html
import android.util.Log
import android.view.*
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CheckedTextView
import androidx.appcompat.app.AlertDialog
import androidx.core.view.get
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.isItemChecked
import com.afollestad.materialdialogs.list.listItemsMultiChoice
import com.afollestad.materialdialogs.list.uncheckItems
import com.cyb3rko.techniklogger.*
import com.cyb3rko.techniklogger.CLASS_EINSATZ
import com.cyb3rko.techniklogger.COLUMN_TECHNIKER_ADMIN
import com.cyb3rko.techniklogger.NAME
import com.cyb3rko.techniklogger.SHARED_PREFERENCE
import com.cyb3rko.techniklogger.databinding.FragmentProjectBinding
import com.cyb3rko.techniklogger.recycler.ProjectTechniker
import com.cyb3rko.techniklogger.recycler.ProjectTechnikerAdapter
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat
import com.parse.ParseObject
@ -42,7 +40,6 @@ class ProjectFragment : Fragment() {
private var dauer = ""
private lateinit var einsatz: ParseObject
private lateinit var sharedPref: SharedPreferences
private val techniker: MutableList<ProjectTechniker> = mutableListOf()
private var time = ""
private val binding get() = _binding!!
@ -64,113 +61,83 @@ class ProjectFragment : Fragment() {
technikerAdapter = ProjectTechnikerAdapter {
if (adminMode || it.name == sharedPref.getString(NAME, "")) {
MaterialDialog(myContext)
.show {
title(text = it.name)
val uhrzeit = if (it.uhrzeit == "0") time else it.uhrzeit
message(text = Html.fromHtml("<strong><u>Arbeitszeit:</u></strong><br/>$uhrzeit Uhr<br/><br/>Wie möchtest du " +
"diesen Eintrag bearbeiten?"))
positiveButton(text = "Arbeitszeit ändern") { _ ->
var tempTime = ""
var time1 = Time(0)
var time2 = Time(0)
val builder = MaterialTimePicker.Builder().setTimeFormat(
TimeFormat.CLOCK_24H).setTitleText("Von")
val builder2 = MaterialTimePicker.Builder().setTimeFormat(TimeFormat.CLOCK_24H).setTitleText("Bis")
val picker = builder.build()
val picker2 = builder2.build()
var hour: String
var minute: String
var hoursInMillis: Long
var minutesInMillis: Long
if (it.uhrzeit != "0") {
val times = it.uhrzeit.split(" - ")
builder.setHour(times[0].split(":")[0].toInt())
.setMinute(times[0].split(":")[1].toInt())
builder2.setHour(times[1].split(":")[0].toInt())
.setMinute(times[1].split(":")[1].toInt())
}
picker.addOnPositiveButtonClickListener {
hour = picker.hour.toString()
hour = if (hour.length != 1) hour else "0$hour"
minute = picker.minute.toString()
minute = if (minute.length != 1) minute else "0$minute"
hoursInMillis = picker.hour.toLong() * 3600000
minutesInMillis = picker.minute.toLong() * 60000
time1 = Time(hoursInMillis + minutesInMillis)
val testTime = time.split(" ")[0]
val testHour = testTime.split(":")[0]
val testMinutes = testTime.split(":")[1]
if (testHour > hour) {
Toasty.warning(myContext, "Zeitpunkt nicht im Zeitraum des Einsatzes", Toasty.LENGTH_LONG).show()
} else if (testHour == hour && testMinutes > minute) {
Toasty.warning(myContext, "Zeitpunkt nicht im Zeitraum des Einsatzes", Toasty.LENGTH_LONG).show()
} else {
tempTime += "${hour}:${minute} - "
activity?.let { it1 -> picker2.show(it1.supportFragmentManager, picker2.tag) }
}
}
picker2.addOnPositiveButtonClickListener { _ ->
hour = picker2.hour.toString()
hour = if (hour.length != 1) hour else "0$hour"
minute = picker2.minute.toString()
minute = if (minute.length != 1) minute else "0$minute"
hoursInMillis = hour.toLong() * 3600000
minutesInMillis = minute.toLong() * 60000
time2 = Time(hoursInMillis + minutesInMillis)
val testTime = time.split(" ")[2]
val testHour = testTime.split(":")[0]
val testMinutes = testTime.split(":")[1]
if (testHour < hour) {
Toasty.warning(myContext, "Zeitpunkt nicht im Zeitraum des Einsatzes", Toasty.LENGTH_LONG).show()
return@addOnPositiveButtonClickListener
} else if (testHour == hour && testMinutes < minute) {
Toasty.warning(myContext, "Zeitpunkt nicht im Zeitraum des Einsatzes", Toasty.LENGTH_LONG).show()
return@addOnPositiveButtonClickListener
}
tempTime += "${hour}:${minute}"
it.uhrzeit = tempTime
val tempDauer = DecimalFormat("#.#").format((time2.time - time1.time) / 3600 / 1000.toFloat())
it.dauer = tempDauer.replace(",", ".")
val uhrzeit = if (it.uhrzeit == "0") time else it.uhrzeit
val message = "<strong><u>Arbeitszeit:</u></strong><br/>$uhrzeit Uhr<br/><br/>" +
"Wie möchtest du diesen Eintrag bearbeiten?"
if (it.teilnahmeKey != "0") {
val dauerFloat = it.dauer.toFloat()
ParseObject.createWithoutData(CLASS_TEILNAHME, it.teilnahmeKey).apply {
put(COLUMN_EINSATZ_DAUER, dauerFloat)
put(COLUMN_EINSATZ_UHRZEIT, it.uhrzeit)
saveInBackground()
}
} else {
val query = ParseQuery.getQuery<ParseObject>(CLASS_TEILNAHME)
query.whereEqualTo(COLUMN_TEILNAHME_VON, ParseObject.createWithoutData(CLASS_TECHNIKER, it.objectId))
query.whereEqualTo(COLUMN_TEILNAHME_AN, ParseObject.createWithoutData(CLASS_EINSATZ, objectId))
query.getFirstInBackground { obj, e ->
if (e == null) {
it.teilnahmeKey = obj.objectId
val dauerFloat = it.dauer.toFloat()
ParseObject.createWithoutData(CLASS_TEILNAHME, it.teilnahmeKey).apply {
put(COLUMN_TEILNAHME_DAUER, dauerFloat)
put(COLUMN_TEILNAHME_UHRZEIT, it.uhrzeit)
saveInBackground()
}
technikerAdapter.submitList(techniker)
} else {
Toasty.error(myContext, "Fehler bei Techniker-Abfrage").show()
Log.e("TechnikLogger.TechSuche", e.message.toString())
}
}
}
technikerAdapter.submitList(techniker)
}
activity?.let { it1 -> picker.show(it1.supportFragmentManager, picker.tag) }
@Suppress("DEPRECATION")
MaterialAlertDialogBuilder(myContext)
.setTitle(it.name)
.setMessage(Html.fromHtml(message))
.setPositiveButton("Arbeitszeit ändern") { _, _ ->
var tempTime = ""
var time1 = Time(0)
var time2 = Time(0)
val builder = MaterialTimePicker.Builder().setTimeFormat(
TimeFormat.CLOCK_24H).setTitleText("Von")
val builder2 = MaterialTimePicker.Builder().setTimeFormat(TimeFormat.CLOCK_24H).setTitleText("Bis")
val picker = builder.build()
val picker2 = builder2.build()
var hour: String
var minute: String
var hoursInMillis: Long
var minutesInMillis: Long
if (it.uhrzeit != "0") {
val times = it.uhrzeit.split(" - ")
builder.setHour(times[0].split(":")[0].toInt())
.setMinute(times[0].split(":")[1].toInt())
builder2.setHour(times[1].split(":")[0].toInt())
.setMinute(times[1].split(":")[1].toInt())
}
negativeButton(text = "Entfernen") { _ ->
techniker.remove(it)
picker.addOnPositiveButtonClickListener {
hour = picker.hour.toString()
hour = if (hour.length != 1) hour else "0$hour"
minute = picker.minute.toString()
minute = if (minute.length != 1) minute else "0$minute"
hoursInMillis = picker.hour.toLong() * 3600000
minutesInMillis = picker.minute.toLong() * 60000
time1 = Time(hoursInMillis + minutesInMillis)
val testTime = time.split(" ")[0]
val testHour = testTime.split(":")[0]
val testMinutes = testTime.split(":")[1]
if (testHour > hour) {
Toasty.warning(myContext, "Zeitpunkt nicht im Zeitraum des Einsatzes", Toasty.LENGTH_LONG).show()
} else if (testHour == hour && testMinutes > minute) {
Toasty.warning(myContext, "Zeitpunkt nicht im Zeitraum des Einsatzes", Toasty.LENGTH_LONG).show()
} else {
tempTime += "${hour}:${minute} - "
activity?.let { it1 -> picker2.show(it1.supportFragmentManager, picker2.tag) }
}
}
picker2.addOnPositiveButtonClickListener { _ ->
hour = picker2.hour.toString()
hour = if (hour.length != 1) hour else "0$hour"
minute = picker2.minute.toString()
minute = if (minute.length != 1) minute else "0$minute"
hoursInMillis = hour.toLong() * 3600000
minutesInMillis = minute.toLong() * 60000
time2 = Time(hoursInMillis + minutesInMillis)
val testTime = time.split(" ")[2]
val testHour = testTime.split(":")[0]
val testMinutes = testTime.split(":")[1]
if (testHour < hour) {
Toasty.warning(myContext, "Zeitpunkt nicht im Zeitraum des Einsatzes", Toasty.LENGTH_LONG).show()
return@addOnPositiveButtonClickListener
} else if (testHour == hour && testMinutes < minute) {
Toasty.warning(myContext, "Zeitpunkt nicht im Zeitraum des Einsatzes", Toasty.LENGTH_LONG).show()
return@addOnPositiveButtonClickListener
}
tempTime += "${hour}:${minute}"
it.uhrzeit = tempTime
val tempDauer = DecimalFormat("#.#").format((time2.time - time1.time) / 3600 / 1000.toFloat())
it.dauer = tempDauer.replace(",", ".")
if (it.teilnahmeKey != "0") {
ParseObject.createWithoutData (CLASS_TEILNAHME, it.teilnahmeKey).deleteInBackground() {
technikerAdapter.submitList(techniker)
binding.technikerView.text = "Techniker: ${techniker.size}"
val dauerFloat = it.dauer.toFloat()
ParseObject.createWithoutData(CLASS_TEILNAHME, it.teilnahmeKey).apply {
put(COLUMN_EINSATZ_DAUER, dauerFloat)
put(COLUMN_EINSATZ_UHRZEIT, it.uhrzeit)
saveInBackground()
}
} else {
val query = ParseQuery.getQuery<ParseObject>(CLASS_TEILNAHME)
@ -178,13 +145,13 @@ class ProjectFragment : Fragment() {
query.whereEqualTo(COLUMN_TEILNAHME_AN, ParseObject.createWithoutData(CLASS_EINSATZ, objectId))
query.getFirstInBackground { obj, e ->
if (e == null) {
obj.deleteInBackground {
if (it == null) {
technikerAdapter.submitList(techniker)
binding.technikerView.text = "Techniker: ${techniker.size}"
} else {
Toasty.error(myContext, "Fehler bei Techniker-Löschung").show()
Log.e("TechnikLogger.TechLösch", it.message.toString())
it.teilnahmeKey = obj.objectId
val dauerFloat = it.dauer.toFloat()
ParseObject.createWithoutData(CLASS_TEILNAHME, it.teilnahmeKey).apply {
put(COLUMN_TEILNAHME_DAUER, dauerFloat)
put(COLUMN_TEILNAHME_UHRZEIT, it.uhrzeit)
saveInBackground {
loadData()
}
}
} else {
@ -193,15 +160,45 @@ class ProjectFragment : Fragment() {
}
}
}
if (techniker.size == 0) showDivider(false)
}
activity?.let { it1 -> picker.show(it1.supportFragmentManager, picker.tag) }
}
.setNegativeButton("Entfernen") { _, _ ->
val list = technikerAdapter.currentList.toMutableList()
list.remove(it)
technikerAdapter.submitList(list)
updateTechnikerCount(list.size)
if (list.isEmpty()) {
showDivider(false)
}
if (it.teilnahmeKey != "0") {
ParseObject.createWithoutData(CLASS_TEILNAHME, it.teilnahmeKey).deleteInBackground()
} else {
val query = ParseQuery.getQuery<ParseObject>(CLASS_TEILNAHME)
query.whereEqualTo(COLUMN_TEILNAHME_VON, ParseObject.createWithoutData(CLASS_TECHNIKER, it.objectId))
query.whereEqualTo(COLUMN_TEILNAHME_AN, ParseObject.createWithoutData(CLASS_EINSATZ, objectId))
query.getFirstInBackground { obj, e ->
if (e == null) {
obj.deleteInBackground {
if (it != null) {
Toasty.error(myContext, "Fehler bei Techniker-Löschung").show()
Log.e("TechnikLogger.TechLösch", it.message.toString())
}
}
} else {
Toasty.error(myContext, "Fehler bei Techniker-Abfrage").show()
Log.e("TechnikLogger.TechSuche", e.message.toString())
}
}
}
if (technikerAdapter.currentList.size == 0) showDivider(false)
}
.show()
}
}
loadData()
technikerAdapter.submitList(techniker as List<ProjectTechniker>?)
binding.recyclerView.layoutManager = LinearLayoutManager(myContext)
binding.recyclerView.adapter = technikerAdapter
@ -219,26 +216,26 @@ class ProjectFragment : Fragment() {
if (adminMode) {
binding.resetButton.visibility = View.VISIBLE
binding.resetButton.setOnClickListener {
MaterialDialog(myContext).show {
message(text = "Möchtest du alle eingetragenen Techniker entfernen?")
positiveButton(text = "Ja") {
MaterialAlertDialogBuilder(myContext)
.setTitle("Techniker entfernen")
.setMessage("Möchtest du alle eingetragenen Techniker entfernen?")
.setPositiveButton("Ja") { _, _ ->
val query = ParseQuery<ParseObject>(CLASS_TEILNAHME)
query.whereEqualTo(COLUMN_TEILNAHME_AN, ParseObject.createWithoutData(CLASS_EINSATZ, objectId))
query.findInBackground { objects, e ->
if (e == null) {
ParseObject.deleteAllInBackground(objects)
techniker.clear()
technikerAdapter.submitList(techniker)
technikerAdapter.submitList(listOf())
showDivider(false)
binding.technikerView.text = "Techniker: 0"
updateTechnikerCount(0)
} else {
Toasty.error(myContext, "Fehler bei Techniker-Abfrage", Toasty.LENGTH_SHORT).show()
Log.e("TechnikLogger.TechSuche", e.message.toString())
}
}
}
negativeButton(text = "Abbrechen")
}
.setNegativeButton("Abbrechen", null)
.show()
}
}
@ -249,10 +246,8 @@ class ProjectFragment : Fragment() {
binding.titleView.text = ""
binding.dateView.text = ""
binding.locationView.text = ""
techniker.clear()
technikerAdapter.submitList(techniker)
showDivider(false)
binding.technikerView.text = "Techniker: 0"
updateTechnikerCount(0)
loadData()
}
}
@ -280,6 +275,7 @@ class ProjectFragment : Fragment() {
query2.findInBackground { objects2, e2 ->
if (e2 == null) {
if (objects2.isNotEmpty()) {
val techniker = mutableListOf<ProjectTechniker>()
objects2.forEach {
val teilnehmenderTechniker = it.getParseObject(COLUMN_TEILNAHME_VON)!!
val dauerEntry = it.getNumber(COLUMN_EINSATZ_DAUER)!!
@ -293,9 +289,8 @@ class ProjectFragment : Fragment() {
time
))
}
techniker.sortBy { it.name }
technikerAdapter.submitList(techniker)
binding.technikerView.text = "Techniker: ${techniker.size}"
updateTechnikerCount(techniker.size)
showDivider()
}
} else {
@ -313,7 +308,7 @@ class ProjectFragment : Fragment() {
}
private fun technikerExists(name: String): Boolean {
for (techniker in techniker) {
for (techniker in technikerAdapter.currentList) {
if (name == techniker.name) {
return true
}
@ -322,41 +317,39 @@ class ProjectFragment : Fragment() {
}
private fun bothAdd(name: String) {
MaterialDialog(myContext)
.show {
message(text = "Möchtest du nur dich oder auch andere Techniker eintragen?")
positiveButton(text = "Mich") {
selfAdd(name, true)
}
negativeButton(text = "Andere") {
otherAdd()
}
MaterialAlertDialogBuilder(myContext)
.setMessage("Möchtest du nur dich oder auch andere Techniker eintragen?")
.setPositiveButton("Mich") { _, _ ->
selfAdd(name, true)
}
.setNegativeButton("Andere") { _, _ ->
otherAdd()
}
.show()
}
private fun selfAdd(name: String, skip: Boolean = false) {
if (!technikerExists(name)) {
if (!skip) {
MaterialDialog(myContext).show {
message(text = "Möchtest du dich als involvierter Techniker eintragen?")
positiveButton(text = "Ja") {
MaterialAlertDialogBuilder(myContext)
.setMessage("Möchtest du dich als involvierter Techniker eintragen?")
.setPositiveButton("Ja") { _, _ ->
val technikerObject = ParseObject.createWithoutData(CLASS_TECHNIKER, sharedPref.getString(TECHNIKER_ID, "")!!)
technikerObject.put(COLUMN_TECHNIKER_NAME, sharedPref.getString(NAME, "")!!)
addTechniker(listOf(technikerObject))
}
negativeButton(text = "Abbrechen")
}
.setNegativeButton("Abbrechen", null)
.show()
} else {
val technikerObject = ParseObject.createWithoutData(CLASS_TECHNIKER, sharedPref.getString(TECHNIKER_ID, "")!!)
technikerObject.put(COLUMN_TECHNIKER_NAME, sharedPref.getString(NAME, "")!!)
addTechniker(listOf(technikerObject))
}
} else {
MaterialDialog(myContext)
.show {
message(text = "Du bist bereits eingetragen.")
positiveButton(text = "Ok")
}
MaterialAlertDialogBuilder(myContext)
.setMessage("Du bist bereits eingetragen.")
.setPositiveButton(android.R.string.ok, null)
.show()
}
}
@ -368,30 +361,52 @@ class ProjectFragment : Fragment() {
if (e == null) {
objects.sortBy { it.getString(COLUMN_TECHNIKER_NAME) }
val currentSelection = mutableListOf<Int>()
val currentSelection = BooleanArray(objects.size)
val names = mutableListOf<String>()
objects.forEachIndexed { index, obj ->
val name = obj.getString(COLUMN_TECHNIKER_NAME)!!
names.add(name)
techniker.forEach {
if (name == it.name) currentSelection.add(index)
technikerAdapter.currentList.forEach {
if (name == it.name) {
currentSelection[index] = true
}
}
}
val intArray = currentSelection.toIntArray()
MaterialDialog(myContext).show {
listItemsMultiChoice(items = names, initialSelection = intArray, disabledIndices = intArray)
positiveButton(text = "Hinzufügen") {
it.uncheckItems(intArray)
val checkedNames = mutableListOf<ParseObject>()
objects.forEachIndexed { index, obj ->
if (it.isItemChecked(index)) {
checkedNames.add(obj)
val checkedNames = mutableListOf<String>()
MaterialAlertDialogBuilder(myContext)
.setMultiChoiceItems(names.toTypedArray(), currentSelection) { dialogInterface, index, isChecked ->
val dialogList = (dialogInterface as AlertDialog).listView
val item = dialogList[index] as CheckedTextView
if (!item.isEnabled) {
item.isChecked = true
} else {
if (isChecked) {
checkedNames.add(names[index])
} else {
checkedNames.remove(names[index])
}
}
addTechniker(checkedNames)
}
negativeButton(text = "Abbrechen")
}
.setPositiveButton("Hinzufügen") { _, _ ->
val newNameObjects = mutableListOf<ParseObject>()
objects.forEach {
if (checkedNames.contains(it.getString(COLUMN_TECHNIKER_NAME)!!)) {
newNameObjects.add(it)
}
}
addTechniker(newNameObjects)
}
.create().apply {
setOnShowListener {
val dialogList = (it as AlertDialog).listView
currentSelection.forEachIndexed { index, b ->
if (b) dialogList[index].isEnabled = false
}
}
}.show()
} else {
Toasty.error(myContext, "Fehler bei Techniker-Suche").show()
Log.e("TechnikLogger.TechSuche", e.message.toString())
@ -407,20 +422,10 @@ class ProjectFragment : Fragment() {
entryObject.put(COLUMN_TEILNAHME_VON, it)
entryObject.put(COLUMN_TEILNAHME_AN, project)
entryList.add(entryObject)
techniker.add(ProjectTechniker(
dauer,
it.getString(COLUMN_TECHNIKER_NAME)!!,
it.objectId,
"0",
time
))
}
ParseObject.saveAllInBackground(entryList) {
if (it == null) {
techniker.sortBy { it.name }
technikerAdapter.submitList(techniker)
showDivider(true)
binding.technikerView.text = "Techniker: ${techniker.size}"
loadData()
} else {
Toasty.error(myContext, "Fehler bei Techniker-Suche").show()
Log.e("TechnikLogger.TechSuche", it.message.toString())
@ -428,12 +433,16 @@ class ProjectFragment : Fragment() {
}
}
private fun updateTechnikerCount(counter: Int) {
binding.technikerView.text = "Techniker: $counter"
}
private fun showDivider(show: Boolean = true) {
binding.divider.visibility = if (show) View.VISIBLE else View.GONE
}
private fun emptyCheck() {
if (techniker.isEmpty()) {
if (technikerAdapter.currentList.isEmpty()) {
binding.divider.visibility = View.GONE
}
}

@ -1,17 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/toolbar_container"
android:layout_height="wrap_content"
android:layout_width="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
style="@style/ActionBar"
android:layout_height="?attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>
<fragment
android:id="@+id/nav_host_fragment_activity_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar_container"
app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.textfield.TextInputLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/md_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/md_dialog_frame_margin_horizontal"
android:paddingEnd="@dimen/md_dialog_frame_margin_horizontal"
android:paddingTop="@dimen/md_dialog_frame_margin_vertical">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/md_input_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:lines="1"
android:singleLine="true"
android:hint="Dein Name" />
</com.google.android.material.textfield.TextInputLayout>

@ -6,7 +6,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.cardview.widget.CardView
<com.google.android.material.card.MaterialCardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
@ -24,7 +24,7 @@
android:text="Einsatz hinzufügen"
android:gravity="center_horizontal" />
</androidx.cardview.widget.CardView>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
@ -32,8 +32,6 @@
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
android:layout_marginTop="15dp"
app:startIconTintMode="src_in"
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
android:hint="Name">
<com.google.android.material.textfield.TextInputEditText
@ -49,8 +47,6 @@
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
android:layout_marginTop="15dp"
app:startIconTintMode="src_in"
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
android:hint="Ort">
<com.google.android.material.textfield.TextInputEditText
@ -60,7 +56,7 @@
</com.google.android.material.textfield.TextInputLayout>
<androidx.cardview.widget.CardView
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="25dp"
@ -93,15 +89,14 @@
android:layout_gravity="center_vertical|end"
android:padding="10dp"
android:layout_marginStart="50dp"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:textSize="12dp"
style="@style/Widget.Material3.Button.OutlinedButton"
android:text="Datum auswählen" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</com.google.android.material.card.MaterialCardView>
<androidx.cardview.widget.CardView
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="25dp"
@ -131,7 +126,7 @@
</LinearLayout>
</androidx.cardview.widget.CardView>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.button.MaterialButton
android:id="@+id/finished_button"
@ -141,8 +136,6 @@
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
android:layout_marginTop="35dp"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:textSize="12sp"
android:text="Fertig" />
<com.google.android.material.button.MaterialButton
@ -154,8 +147,7 @@
android:layout_marginEnd="25dp"
android:layout_marginTop="20dp"
android:visibility="gone"
style="@style/Widget.MaterialComponents.Button"
android:textSize="12sp"
style="@style/Widget.Material3.Button.OutlinedButton"
android:text="Einsatz entfernen"
tools:visibility="visible" />

@ -13,7 +13,7 @@
android:orientation="vertical"
tools:ignore="HardcodedText">
<androidx.cardview.widget.CardView
<com.google.android.material.card.MaterialCardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
@ -34,9 +34,9 @@
android:textSize="20sp"
tools:text="Beispiel-Einsatz" />
</androidx.cardview.widget.CardView>
</com.google.android.material.card.MaterialCardView>
<androidx.cardview.widget.CardView
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="25dp"
@ -70,9 +70,9 @@
</LinearLayout>
</androidx.cardview.widget.CardView>
</com.google.android.material.card.MaterialCardView>
<androidx.cardview.widget.CardView
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="25dp"
@ -106,9 +106,9 @@
</LinearLayout>
</androidx.cardview.widget.CardView>
</com.google.android.material.card.MaterialCardView>
<androidx.cardview.widget.CardView
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="25dp"
@ -199,7 +199,7 @@
</LinearLayout>
</androidx.cardview.widget.CardView>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>

@ -1,21 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:background="@color/cardBackground"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="5dp"
android:orientation="horizontal"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/cardBackground"
android:orientation="vertical"
android:padding="10dp">
@ -60,6 +57,6 @@
</LinearLayout>
</androidx.cardview.widget.CardView>
</com.google.android.material.card.MaterialCardView>

@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:background="@color/cardBackground"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
@ -18,4 +17,4 @@
android:gravity="center_horizontal"
tools:text="1900/1901" />
</androidx.cardview.widget.CardView>
</com.google.android.material.card.MaterialCardView>

@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#2A81C6</color>
<color name="colorPrimaryDark">#055A9C</color>
<color name="colorAccent">#1596F8</color>
<color name="cardBackground">@color/cardview_dark_background</color>
<color name="blurBackground">#92000000</color>
<color name="adaptiveDrawableTint">#FFFFFF</color>
<color name="statusBarColor">@android:color/transparent</color>
<color name="refreshLayoutBackground">@color/cardview_dark_background</color>
<color name="refreshLayoutArrow">#FFFFFF</color>

@ -0,0 +1,24 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.TechnikLogger" parent="Base.Theme.TechnikLogger">
<item name="actionBarStyle">@style/ActionBar</item>
<!-- Transparent system bars for edge-to-edge. -->
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:statusBarColor">@color/statusBarColor</item>
<item name="android:windowLightStatusBar">false</item>
<item name="android:textColorPrimary">#FFFFFF</item>
<item name="android:textColorSecondary">#FFFFFF</item>
<item name="android:textColorTertiaryInverse">#FFFFFF</item>
</style>
<style name="ActionBar" parent="Widget.Material3.ActionBar.Solid">
<item name="android:background">@color/statusBarColor</item>
</style>
<style name="Theme.TechnikLogger.NoActionBar" parent="Base.Theme.TechnikLogger">
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:statusBarColor">@color/statusBarColor</item>
<item name="android:windowLightStatusBar">false</item>
</style>
</resources>

@ -3,10 +3,10 @@
<color name="colorPrimary">#1596F8</color>
<color name="colorPrimaryDark">#055A9C</color>
<color name="colorAccent">@color/colorPrimary</color>
<color name="cardBackground">#FFFFFF</color>
<color name="blurBackground">#92000000</color>
<color name="drawableTint">#FFFFFF</color>
<color name="adaptiveDrawableTint">#000000</color>
<color name="statusBarColor">@color/colorPrimary</color>
<color name="refreshLayoutBackground">#FFFFFF</color>
<color name="refreshLayoutArrow">#393939</color>

@ -1,20 +0,0 @@
<resources>
<!-- Base application theme. -->
<style name="DarkActionbar" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="materialCalendarTheme">@style/ThemeOverlay.MaterialComponents.MaterialCalendar</item>
</style>
<style name="NoActionBar" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
</resources>

@ -0,0 +1,11 @@
<resources>
<!-- Base application theme. -->
<style name="Base.Theme.TechnikLogger" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryContainer">@color/colorAccent</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
Loading…
Cancel
Save