Füge Funktion zum Exportieren der Liste als PDF-Datei hinzu

master
Niko Diamadis 5 years ago
parent d6d44277e4
commit e5659e8908

@ -79,6 +79,7 @@ dependencies {
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.firebase:firebase-crashlytics-ktx:17.3.0'
implementation 'com.itextpdf:itextpdf:5.5.13.2'
implementation "com.mikepenz:aboutlibraries:$about_libraries_version"
implementation "com.mikepenz:aboutlibraries-core:$about_libraries_version"
implementation 'me.ibrahimyilmaz:kiel:1.1.0'

@ -1,5 +1,6 @@
package com.cyb3rko.techniklogger
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.content.SharedPreferences
@ -8,6 +9,7 @@ import android.util.Log
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupActionBarWithNavController
@ -18,12 +20,7 @@ import com.androidnetworking.AndroidNetworking
import com.androidnetworking.error.ANError
import com.androidnetworking.interfaces.StringRequestListener
import com.cyb3rko.techniklogger.databinding.ActivityMainBinding
import com.cyb3rko.techniklogger.databinding.FragmentListingBinding
import com.cyb3rko.techniklogger.fragments.EinsatzPusherFragment
import com.cyb3rko.techniklogger.utils.About
import com.parse.ParseObject
import com.parse.ParseQuery
import es.dmoral.toasty.Toasty
import java.util.*
class MainActivity : AppCompatActivity() {
@ -59,6 +56,13 @@ class MainActivity : AppCompatActivity() {
updateCheck()
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
ActivityCompat.requestPermissions(
this, arrayOf(Manifest.permission.INTERNET, Manifest.permission.WRITE_EXTERNAL_STORAGE), 1
)
}
private fun showNameDialog() {
val currentName = getSharedPreferences("Safe", 0).getString("name", "")
@ -118,8 +122,6 @@ class MainActivity : AppCompatActivity() {
when (item.itemId) {
R.id.action_rename -> showNameDialog()
R.id.action_about -> startActivity(Intent(applicationContext, About::class.java))
// R.id.action_privacy_policy ->
// R.id.action_terms_of_use ->
}
return super.onOptionsItemSelected(item)

@ -1,5 +1,6 @@
package com.cyb3rko.techniklogger.fragments
import android.animation.Animator
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
@ -14,6 +15,7 @@ import com.cyb3rko.techniklogger.R
import com.cyb3rko.techniklogger.databinding.FragmentListingBinding
import com.cyb3rko.techniklogger.recycler.ProjectEntryViewHolder
import com.cyb3rko.techniklogger.recycler.ProjectViewState
import com.cyb3rko.techniklogger.table.ExportBuilder
import com.parse.ParseObject
import com.parse.ParseQuery
import es.dmoral.toasty.Toasty
@ -28,6 +30,7 @@ class ListingFragment : Fragment() {
private var adminMode = false
private val data: MutableList<ProjectViewState.ProjectEntry> = mutableListOf()
private var isFABOpen = false
private lateinit var sharedPref: SharedPreferences
private lateinit var sharedPrefEditor: SharedPreferences.Editor
@ -97,6 +100,14 @@ class ListingFragment : Fragment() {
loadEntries(adapter)
}
binding.fab.setOnClickListener {
if (!isFABOpen) showFABMenu() else closeFABMenu()
}
binding.fabBgLayout.setOnClickListener {
closeFABMenu()
}
return root
}
@ -150,19 +161,23 @@ class ListingFragment : Fragment() {
if (techniker.getBoolean("admin")) {
adminMode = true
sharedPrefEditor.putBoolean("admin", adminMode)
binding.floatingActionButton.show()
binding.floatingActionButton.setOnClickListener {
binding.fabContainer.visibility = View.VISIBLE
binding.fab1.setOnClickListener {
val action = ListingFragmentDirections.navigateToPusher()
findNavController().navigate(action)
}
binding.fab2.setOnClickListener {
closeFABMenu()
ExportBuilder(myContext, data)
}
} else {
adminMode = false
sharedPrefEditor.putBoolean("admin", adminMode)
binding.floatingActionButton.hide()
binding.fabContainer.visibility = View.INVISIBLE
}
sharedPrefEditor.putString("technikerId", techniker.objectId).apply()
} else {
binding.floatingActionButton.hide()
binding.fabContainer.visibility = View.INVISIBLE
adminMode = false
sharedPrefEditor.putBoolean("admin", adminMode).apply()
Toasty.error(myContext, "Adminstatus unbekannt", Toasty.LENGTH_SHORT).show()
@ -170,4 +185,36 @@ class ListingFragment : Fragment() {
}
}
}
private fun showFABMenu() {
isFABOpen = true
binding.fabLayout1.visibility = View.VISIBLE
binding.fabLayout2.visibility = View.VISIBLE
binding.fabBgLayout.visibility = View.VISIBLE
binding.fab.animate().rotationBy(180f)
binding.fabLayout1.animate().translationY(-resources.getDimension(R.dimen.first_fab))
binding.fabLayout2.animate().translationY(-resources.getDimension(R.dimen.second_fab))
}
private fun closeFABMenu() {
isFABOpen = false
binding.fabBgLayout.visibility = View.GONE
binding.fab.animate().rotation(0f)
binding.fabLayout1.animate().translationY(0f)
binding.fabLayout2.animate().translationY(0f)
binding.fabLayout2.animate().translationY(0f).setListener(object: Animator.AnimatorListener {
override fun onAnimationStart(animation: Animator?) {}
override fun onAnimationEnd(animation: Animator?) {
if (!isFABOpen) {
binding.fabLayout1.visibility = View.GONE
binding.fabLayout2.visibility = View.GONE
}
}
override fun onAnimationCancel(animation: Animator?) {}
override fun onAnimationRepeat(animation: Animator?) {}
})
}
}

@ -0,0 +1,18 @@
package com.cyb3rko.techniklogger.table
import com.itextpdf.text.Element
import com.itextpdf.text.Font
import com.itextpdf.text.Phrase
import com.itextpdf.text.pdf.PdfPCell
class ColumnHeaderCell(title: String) : PdfPCell() {
init {
setPhrase(Phrase(
title,
Font(Font.FontFamily.HELVETICA, 14f, Font.BOLD)
))
setPadding(8f)
verticalAlignment = Element.ALIGN_MIDDLE
horizontalAlignment = Element.ALIGN_CENTER
}
}

@ -0,0 +1,103 @@
package com.cyb3rko.techniklogger.table
import android.content.Context
import android.os.Environment
import android.util.Log
import com.cyb3rko.techniklogger.recycler.ProjectViewState
import com.itextpdf.text.*
import com.itextpdf.text.pdf.PdfPTable
import com.itextpdf.text.pdf.PdfWriter
import com.parse.ParseObject
import com.parse.ParseQuery
import es.dmoral.toasty.Toasty
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.*
class ExportBuilder(val myContext: Context, data: MutableList<ProjectViewState.ProjectEntry>) : Document(PageSize.A4.rotate()) {
private val destination = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString() + "/"
private val directory = File(destination)
private val data = data.reversed()
private val table = PdfPTable(4)
private val techniker = mutableListOf<List<String>>()
private val time = SimpleDateFormat("dd.MM.yyyy, HH:mm", Locale.GERMANY).format(Date())
init {
if (!directory.exists()) {
directory.mkdirs()
}
try {
val file = File(destination, "Arbeitseinsätze Technik - ${time.split(",")[0]}.pdf")
file.createNewFile()
val fOut = FileOutputStream(file, false)
PdfWriter.getInstance(this, fOut)
} catch (e: DocumentException) {
e.printStackTrace()
Log.e("TechnikLogger.Export", e.toString())
} catch (e: FileNotFoundException) {
e.printStackTrace()
Log.e("TechnikLogger.Export", e.toString())
} catch (e: IOException) {
e.printStackTrace()
}
this.setMargins(0f, 0f, 36f, 36f)
table.totalWidth = PageSize.A4.rotate().width
try {
this.open()
table.addCell(TimeCell(time))
table.addCell(HeaderCell())
val columnHeaderTexts = listOf("Datum", "Veranstaltung", "Techniker", "Dauer insgesamt (h)")
repeat(4) {
table.addCell(ColumnHeaderCell(columnHeaderTexts[it]))
}
fetchTechniker(0)
} catch (e: DocumentException) {
e.printStackTrace()
Log.e("TechnikLogger.Export", e.toString())
}
}
private fun fetchTechniker(index: Int) {
if (index < data.size) {
val query = ParseQuery.getQuery<ParseObject>("Teilnahme")
query.whereEqualTo("an", ParseObject.createWithoutData("Einsatz", data[index].childKey))
query.include("von")
query.findInBackground { objects, e ->
if (e == null) {
val tempList = mutableListOf<String>()
var aktuellerTechniker: ParseObject
objects.forEach {
aktuellerTechniker = it.getParseObject("von")!!
tempList.add(aktuellerTechniker.getString("name")!!)
}
techniker.add(tempList.sorted())
fetchTechniker(index + 1)
}
}
} else {
export()
}
}
private fun export() {
data.forEachIndexed { index, projectEntry ->
val information = listOf(
SimpleDateFormat("dd.MM.yyyy", Locale.GERMANY).format(projectEntry.date),
projectEntry.text,
techniker[index].joinToString("\n"),
projectEntry.duration)
information.forEachIndexed { index2, string ->
table.addCell(NormalCell(string, index2 != 2))
}
}
this.add(table)
this.close()
Toasty.success(myContext, "Liste in Downloads gespeichert").show()
}
}

@ -0,0 +1,22 @@
package com.cyb3rko.techniklogger.table
import com.itextpdf.text.BaseColor
import com.itextpdf.text.Element
import com.itextpdf.text.Font
import com.itextpdf.text.Phrase
import com.itextpdf.text.pdf.PdfPCell
class HeaderCell : PdfPCell() {
init {
setPhrase(Phrase(
"\nAutomatisch erstellt über die Technik-Logger App\n(Entwickler: Niko Diamadis)\n ",
Font(Font.FontFamily.HELVETICA, 12f, Font.BOLD)
))
borderWidth = 0.2f
borderColor = BaseColor.GRAY
colspan = 4
rowspan = 3
verticalAlignment = Element.ALIGN_MIDDLE
horizontalAlignment = Element.ALIGN_CENTER
}
}

@ -0,0 +1,18 @@
package com.cyb3rko.techniklogger.table
import com.itextpdf.text.Element
import com.itextpdf.text.Font
import com.itextpdf.text.Phrase
import com.itextpdf.text.pdf.PdfPCell
class NormalCell(title: String, centered: Boolean) : PdfPCell() {
init {
setPhrase(Phrase(
title,
Font(Font.FontFamily.HELVETICA, 13f)
))
setPadding(6f)
verticalAlignment = Element.ALIGN_MIDDLE
if (centered) horizontalAlignment = Element.ALIGN_CENTER
}
}

@ -0,0 +1,23 @@
package com.cyb3rko.techniklogger.table
import com.itextpdf.text.BaseColor
import com.itextpdf.text.Element
import com.itextpdf.text.Font
import com.itextpdf.text.Phrase
import com.itextpdf.text.pdf.PdfPCell
class TimeCell(time: String) : PdfPCell() {
init {
setPhrase(Phrase(
"Erstellung: $time Uhr",
Font(Font.FontFamily.HELVETICA, 12f, Font.BOLD)
))
setPadding(8f)
borderWidth = 0.2f
borderColor = BaseColor.GRAY
colspan = 4
rowspan = 3
verticalAlignment = Element.ALIGN_MIDDLE
horizontalAlignment = Element.ALIGN_CENTER
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

@ -50,15 +50,93 @@
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/floatingActionButton"
<View
android:id="@+id/fab_bg_Layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/blurBackground"
android:visibility="gone" />
<LinearLayout
android:id="@+id/fab_layout1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
android:layout_marginBottom="20dp"
android:src="@drawable/_icon_add"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
app:layout_constraintEnd_toEndOf="parent"
android:padding="12dp"
android:clipToPadding="false"
android:layout_marginEnd="23dp"
android:gravity="center_vertical"
android:layout_marginBottom="23dp"
android:layout_gravity="bottom|end"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Neuer Eintrag" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
app:srcCompat="@drawable/_icon_add"
app:fabSize="mini" />
</LinearLayout>
<LinearLayout
android:id="@+id/fab_layout2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:padding="12dp"
android:clipToPadding="false"
android:layout_marginEnd="23dp"
android:gravity="center_vertical"
android:layout_marginBottom="23dp"
android:layout_gravity="bottom|end"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Liste exportieren" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
app:srcCompat="@drawable/_icon_export"
app:fabSize="mini" />
</LinearLayout>
<LinearLayout
android:id="@+id/fab_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/fab_margin"
android:gravity="center_vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:clipToPadding="false"
android:padding="12dp"
android:layout_marginBottom="@dimen/fab_margin"
android:layout_gravity="bottom|end">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:gravity="center_vertical"
app:srcCompat="@drawable/_icon_dot_menu"
app:fabSize="normal"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

@ -12,6 +12,18 @@
<item>https://www.flaticon.com/free-icon/calendar_609356</item>
</string-array>
<string-array name="icon_dot_menu">
<item>Those Icons</item>
<item>flaticon.com</item>
<item>https://www.flaticon.com/free-icon/menu_2089793</item>
</string-array>
<string-array name="icon_export">
<item>Freepik</item>
<item>flaticon.com</item>
<item>https://www.flaticon.com/premium-icon/export_2356643</item>
</string-array>
<string-array name="icon_launcher">
<item>Freepik</item>
<item>flaticon.com</item>

@ -4,4 +4,5 @@
<color name="colorPrimaryDark">#055A9C</color>
<color name="colorAccent">@color/colorPrimary</color>
<color name="cardBackground">#FFFFFF</color>
<color name="blurBackground">#92000000</color>
</resources>

@ -1,3 +1,6 @@
<resources>
<dimen name="fab_margin">16dp</dimen>
<dimen name="first_fab">60dp</dimen>
<dimen name="second_fab">120dp</dimen>
</resources>
Loading…
Cancel
Save