Compare commits

...

8 Commits

@ -7,6 +7,7 @@ plugins {
}
android {
namespace 'com.cyb3rko.techniklogger'
signingConfigs {
signingconf {
Properties properties = new Properties()
@ -18,14 +19,14 @@ android {
keyPassword properties.getProperty("signing.key.password")
}
}
compileSdk 33
compileSdk 34
defaultConfig {
applicationId "com.cyb3rko.techniklogger"
minSdk 19
targetSdk 33
versionCode 17
versionName "2.2.3"
targetSdk 34
versionCode 18
versionName "2.2.4"
}
buildTypes {
@ -43,11 +44,11 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '11'
jvmTarget = '17'
}
buildFeatures {
viewBinding true
@ -58,17 +59,16 @@ dependencies {
implementation platform('com.google.firebase:firebase-bom:31.4.0')
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
implementation 'androidx.recyclerview:recyclerview:1.3.0'
implementation 'androidx.core:core-ktx:1.12.0'
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"
implementation 'androidx.recyclerview:recyclerview:1.3.1'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'com.afollestad.material-dialogs:bottomsheets:3.3.0'
implementation 'com.airbnb.android:lottie:5.2.0'
implementation 'com.amitshekhar.android:android-networking:1.0.2'
implementation 'com.github.GrenderG:Toasty:1.5.2'
implementation 'com.github.parse-community.Parse-SDK-Android:parse:1.26.0'
implementation 'com.google.android.material:material:1.8.0'
implementation 'com.google.android.material:material:1.10.0'
implementation 'com.google.firebase:firebase-analytics-ktx' // BOM versioning
implementation 'com.google.firebase:firebase-crashlytics-ktx' // BOM versioning
implementation 'com.itextpdf:itextpdf:5.5.13.3'

@ -20,5 +20,5 @@
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keep class com.cyb3rko.techniklogger.* { *; }
-keep class com.cyb3rko.techniklogger.** { *; }
-keep class com.cyb3rko.techniklogger.** { *; }
-keep class com.parse.** { <init>(...); }

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.cyb3rko.techniklogger">
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"

@ -136,6 +136,7 @@ internal fun showNameDialog(
}
if (inputName != currentName) {
dismiss()
Safe.storeKey(activity, NAME, inputName.trim())
activity.finish()
activity.startActivity(Intent(activity, MainActivity::class.java))

@ -12,6 +12,9 @@ internal class Member : ParseObject() {
val name
get() = getString(COLUMN_NAME)!!
val retired
get() = getBoolean(COLUMN_RETIRED)
fun setAdmin(admin: Boolean) {
put(COLUMN_ADMIN, admin)
}
@ -20,10 +23,28 @@ internal class Member : ParseObject() {
put(COLUMN_NAME, name)
}
private fun setRetired(retired: Boolean) {
put(COLUMN_RETIRED, retired)
}
fun retire() {
put(COLUMN_RETIRED, true)
}
override fun equals(other: Any?): Boolean {
if (other !is Member) return false
return this.name == other.name && this.admin == other.admin && this.retired == other.retired
}
fun copy(): Member {
val member = Member()
member.objectId = objectId
member.setAdmin(admin)
member.setName(name)
member.setRetired(retired)
return member
}
companion object {
const val CLASS_NAME = "Techniker"

@ -275,7 +275,8 @@ class ListingFragment : Fragment() {
30 -> "11"
31, 32 -> "12"
33 -> "13"
else -> "> 13"
34 -> "14"
else -> "> 14"
}
info += "\nSoftware: $androidName ($androidVersion)"
@ -303,7 +304,7 @@ class ListingFragment : Fragment() {
.setTitle("Niko Diamadis")
.setMessage(
Html.fromHtml("Für Fragen, Fehlermeldung und Anmerkungen:<br/><br/>" +
"<b>Signal/Whatsapp</b>: +49 1511 7779103<br/>" +
"<b>Signal/WhatsApp</b>: +49 1511 7779103<br/>" +
"<b>E-Mail</b>: niko@cyb3rko.de"
))
.show()

@ -5,22 +5,16 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AbsListView
import android.widget.ArrayAdapter
import androidx.fragment.app.Fragment
import com.afollestad.materialdialogs.LayoutMode
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
import com.afollestad.materialdialogs.callbacks.onDismiss
import com.afollestad.materialdialogs.customview.customView
import com.cyb3rko.techniklogger.R
import com.cyb3rko.techniklogger.data.objects.Member
import com.cyb3rko.techniklogger.data.ParseController
import com.cyb3rko.techniklogger.databinding.FragmentManageMembersBinding
import com.cyb3rko.techniklogger.logE
import com.cyb3rko.techniklogger.modals.MemberBottomSheet
import com.cyb3rko.techniklogger.showErrorToast
import com.cyb3rko.techniklogger.showSuccessToast
import com.google.android.material.switchmaterial.SwitchMaterial
import com.google.android.material.textfield.TextInputEditText
class ManageMembersFragment : Fragment() {
private var _binding: FragmentManageMembersBinding? = null
@ -42,31 +36,19 @@ class ManageMembersFragment : Fragment() {
loadMembers()
binding.fab.setOnClickListener {
MaterialDialog(myContext, BottomSheet(LayoutMode.WRAP_CONTENT)).show {
title(text = "Techniker hinzufügen")
customView(R.layout.member_dialog, horizontalPadding = true)
positiveButton(text = "Hinzufügen") {
val view = it.view
val newName = view.findViewById<TextInputEditText>(R.id.name).text.toString()
val newAdmin = view.findViewById<SwitchMaterial>(R.id.admin).isChecked
Member().apply {
setAdmin(newAdmin)
setName(newName)
saveInBackground {
if (it == null) {
showSuccessToast("Hinzugefügt")
showAnimation(true)
loadMembers()
} else {
showErrorToast("Fehler beim Hinzufügen")
logE("Fehler beim Hinzufügen des Technikers")
it.printStackTrace()
}
}
MemberBottomSheet { member ->
member?.saveInBackground { exception ->
if (exception == null) {
showSuccessToast("Hinzugefügt")
showAnimation(true)
loadMembers()
} else {
showErrorToast("Fehler beim Hinzufügen")
logE("Fehler beim Hinzufügen des Technikers")
exception.printStackTrace()
}
}
}
}.show(parentFragmentManager, MemberBottomSheet.TAG)
}
binding.swipeRefreshLayout.apply {
@ -79,6 +61,24 @@ class ManageMembersFragment : Fragment() {
}
}
binding.list.setOnScrollListener(object : AbsListView.OnScrollListener {
override fun onScrollStateChanged(view: AbsListView?, scrollState: Int) {}
override fun onScroll(
view: AbsListView?,
firstVisibleItem: Int,
visibleItemCount: Int,
totalItemCount: Int
) {
val topRowVerticalPosition = if (binding.list.childCount == 0) {
0
} else {
binding.list.getChildAt(0).top
}
binding.swipeRefreshLayout.isEnabled = firstVisibleItem == 0 && topRowVerticalPosition >= 0
}
})
return root
}
@ -101,65 +101,27 @@ class ManageMembersFragment : Fragment() {
showAnimation(false)
binding.list.setOnItemClickListener { _, _, index, _ ->
val dialogView = layoutInflater.inflate(R.layout.member_dialog, null)
dialogView.findViewById<TextInputEditText>(R.id.name).setText(memberNames[index])
dialogView.findViewById<SwitchMaterial>(R.id.admin).isChecked = members[index].admin
MaterialDialog(myContext, BottomSheet(LayoutMode.WRAP_CONTENT)).show {
title(text = "Techniker-Info")
customView(0, dialogView, horizontalPadding = true)
onDismiss {
val view = it.view
val newName = view.findViewById<TextInputEditText>(R.id.name).text.toString()
val newAdmin = view.findViewById<SwitchMaterial>(R.id.admin).isChecked
val member = members[index]
val adminChanged = newAdmin != member.admin
val nameChanged = newName != member.name
if (adminChanged || nameChanged) {
member.apply {
setAdmin(newAdmin)
setName(newName)
saveInBackground {
if (it == null) {
showSuccessToast("Gespeichert")
if (nameChanged) {
showAnimation(true)
loadMembers()
}
} else {
showErrorToast("Fehler bei der Speicherung")
logE("Fehler bei der Speicherung des neuen Technikers")
it.printStackTrace()
}
}
val member = members[index]
MemberBottomSheet(members[index]) { editedMember ->
if (editedMember == null) {
showAnimation(true)
loadMembers()
return@MemberBottomSheet
}
if (editedMember != member) {
editedMember.saveInBackground {
if (it == null) {
showSuccessToast("Gespeichert")
showAnimation(true)
loadMembers()
} else {
showErrorToast("Fehler bei der Speicherung")
logE("Fehler bei der Speicherung des neuen Technikers")
it.printStackTrace()
}
}
}
negativeButton(text = "Entlassen") {
val name = memberNames[index]
MaterialDialog(myContext).show {
title(text = "Entlassung")
message(text = "Soll '$name' wirklich entlassen werden?")
positiveButton(text = "Ja") {
members[index].apply {
retire()
saveInBackground {
if (it == null) {
showSuccessToast("Techniker entlassen")
showAnimation(true)
loadMembers()
} else {
showErrorToast("Fehler bei der Entlassung")
logE("Fehler bei der Entlassung des Technikers")
it.printStackTrace()
}
}
}
}
negativeButton(text = "Nein")
}
}
}
}.show(parentFragmentManager, MemberBottomSheet.TAG)
}
} else {
showAnimation(true, false)

@ -49,6 +49,10 @@ class YearsFragment : Fragment() {
yearAdapter = YearAdapter { year ->
Safe.storeKey(myContext, CURRENT_YEAR, year.objectId)
Safe.storeKey(myContext, CURRENT_YEAR_NAME, year.name)
var pop = true
while (pop) {
pop = findNavController().popBackStack()
}
findNavController().navigate(R.id.navigation_listing)
}
binding.recyclerView.apply {
@ -74,7 +78,7 @@ class YearsFragment : Fragment() {
.setTitle("Neues Schuljahr")
.setMessage("Möchtest du das Schuljahr $new hinzufügen?")
.setPositiveButton(android.R.string.ok) { _, _ ->
ParseObject("Jahr").apply {
Year().apply {
put("name", new)
save()
loadEntries()

@ -0,0 +1,94 @@
package com.cyb3rko.techniklogger.modals
import android.content.DialogInterface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.cyb3rko.techniklogger.data.objects.Member
import com.cyb3rko.techniklogger.databinding.MemberDialogBinding
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
internal class MemberBottomSheet(
private val member: Member? = null,
private val action: (member: Member?) -> Unit
): BottomSheetDialogFragment() {
private lateinit var binding: MemberDialogBinding
private var mode = -1
private lateinit var mMember: Member
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = MemberDialogBinding.inflate(layoutInflater)
if (member == null) {
mode = MODE_ADD
} else {
mMember = member.copy()
binding.name.setText(mMember.name)
binding.admin.isChecked = mMember.admin
mode = MODE_EDIT
}
if (mode == MODE_ADD) {
binding.title.text = "Techniker hinzufügen"
binding.button.text = "Hinzufügen"
} else {
binding.title.text = "Techniker-Info"
binding.button.text = "Entlassen"
}
binding.button.setOnClickListener {
val newName = binding.name.text.toString()
val newAdmin = binding.admin.isChecked
if (mode == MODE_ADD) {
Member().apply {
setAdmin(newAdmin)
setName(newName)
action(this)
dismiss()
}
} else {
MaterialAlertDialogBuilder(requireContext())
.setTitle("Entlassung")
.setMessage("Soll '${mMember.name}' wirklich entlassen werden?")
.setPositiveButton("Ja") { _ , _ ->
mMember.apply {
retire()
action(this)
dismiss()
}
}
.setNegativeButton("Nein", null)
.show()
}
}
return binding.root
}
override fun onDismiss(dialog: DialogInterface) {
if (mode == MODE_EDIT && ::binding.isInitialized) {
val newName = binding.name.text.toString()
val newAdmin = binding.admin.isChecked
val adminChanged = newAdmin != mMember.admin
val nameChanged = newName != mMember.name
if (adminChanged || nameChanged) {
mMember.apply {
setAdmin(newAdmin)
setName(newName)
action(this)
}
}
}
}
companion object {
const val TAG = "Member Bottom Sheet"
const val MODE_ADD = 0
const val MODE_EDIT = 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<bitmap
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/_icon_calendar"
android:tint="@color/adaptiveDrawableTint" />

@ -4,9 +4,9 @@
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">
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingTop="16dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/md_input_text"

@ -3,10 +3,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="@dimen/md_dialog_frame_margin_vertical"
android:paddingLeft="@dimen/md_dialog_frame_margin_horizontal"
android:paddingRight="@dimen/md_dialog_frame_margin_horizontal"
android:paddingBottom="@dimen/md_dialog_frame_margin_vertical">
android:padding="16dp">
<com.google.android.material.progressindicator.CircularProgressIndicator
android:layout_width="wrap_content"

@ -5,11 +5,20 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="35dp">
android:padding="16dp">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Techniker hinzufügen"
android:textStyle="bold"
android:textSize="24sp" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
app:helperTextEnabled="true">
<com.google.android.material.textfield.TextInputEditText
@ -38,4 +47,12 @@
android:layout_height="wrap_content"
android:layout_marginTop="5dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="12dp"
tools:text="Hinzufügen" />
</LinearLayout>

@ -6,7 +6,8 @@
<item
android:id="@+id/action_year"
android:title="Schuljahr wechseln"
app:showAsAction="never"
app:showAsAction="ifRoom"
android:icon="@drawable/colored_icon_calendar"
tools:ignore="HardcodedText" />
<item

@ -5,17 +5,17 @@ buildscript {
navigation_version = '2.5.3'
}
dependencies {
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.4'
classpath 'com.google.gms:google-services:4.3.15'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.9'
classpath 'com.google.gms:google-services:4.4.0'
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigation_version"
}
}
plugins {
id 'com.android.application' version '7.4.2' apply false
id 'com.android.application' version '8.1.2' apply false
id 'org.jetbrains.kotlin.android' version "$kotlin_version" apply false
}
task clean(type: Delete) {
tasks.register('clean', Delete) {
delete rootProject.buildDir
}

@ -22,4 +22,6 @@ android.enableJetifier=true
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
android.nonTransitiveRClass=true
android.defaults.buildfeatures.buildconfig=true
android.nonFinalResIds=true

Binary file not shown.

@ -1,7 +1,8 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distrubutionSha256Sum=6147605a23b4eff6c334927a86ff3508cb5d6722cd624c97ded4c2e8640f1f87
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip
distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
zipStorePath=wrapper/dists

29
gradlew vendored

@ -83,10 +83,8 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@ -133,10 +131,13 @@ location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@ -197,11 +198,15 @@ if "$cygwin" || "$msys" ; then
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \

Loading…
Cancel
Save