Changing attestation data to ArrayList

Adding PdfViewerActivity
Adding providePdfUtils in Injector
This commit is contained in:
Mathieu Sanchez 2020-04-16 14:32:46 +02:00
parent 9bbc364c11
commit f135425444
16 changed files with 240 additions and 28 deletions

View File

@ -67,6 +67,7 @@ dependencies {
// PDF // PDF
implementation 'com.tom_roush:pdfbox-android:1.8.10.1' implementation 'com.tom_roush:pdfbox-android:1.8.10.1'
implementation 'com.github.barteksc:android-pdf-viewer:3.2.0-beta.1'
// Gson // Gson
implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.code.gson:gson:2.8.6'

View File

@ -6,12 +6,13 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application <application
android:allowBackup="true" android:allowBackup="false"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme.Light"> android:theme="@style/AppTheme.Light">
<activity android:name=".PdfViewerActivity" />
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:label="@string/app_name"> android:label="@string/app_name">

View File

@ -6,7 +6,7 @@ import androidx.navigation.findNavController
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
import com.tom_roush.pdfbox.util.PDFBoxResourceLoader import com.tom_roush.pdfbox.util.PDFBoxResourceLoader
import fr.sanchezm.attestationsCovid19.utilities.PdfUtils import fr.sanchezm.attestationsCovid19.utilities.InjectorUtils
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
@ -19,12 +19,11 @@ class MainActivity : AppCompatActivity() {
val navController = findNavController(R.id.nav_host_fragment) val navController = findNavController(R.id.nav_host_fragment)
PDFBoxResourceLoader.init(applicationContext) PDFBoxResourceLoader.init(applicationContext)
PdfUtils.getInstance(assets, getMyFilesDir()) InjectorUtils.providePdfUtils(applicationContext)
actionBar?.hide() actionBar?.hide()
navView.setupWithNavController(navController) navView.setupWithNavController(navController)
navView.setBackgroundColor(resources.getColor(R.color.itemBackground, theme)) navView.setBackgroundColor(resources.getColor(R.color.itemBackground, theme))
} }
private fun getMyFilesDir(): String = filesDir.path
} }

View File

@ -0,0 +1,24 @@
package fr.sanchezm.attestationsCovid19
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.github.barteksc.pdfviewer.PDFView
import fr.sanchezm.attestationsCovid19.utilities.InjectorUtils
import java.io.File
class PdfViewerActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pdf_viewer)
val createAt = intent.getLongExtra("createAt", 0)
val pdfUtils = InjectorUtils.providePdfUtils(applicationContext)
findViewById<PDFView>(R.id.pdfView).also {
it.fromFile(File(pdfUtils.getPath(createAt)))
.enableAnnotationRendering(true)
.spacing(20)
.load()
}
}
}

View File

@ -9,11 +9,11 @@ import java.io.File
class AttestationDao(private val path: String) { class AttestationDao(private val path: String) {
private var _attestations = MutableLiveData<MutableCollection<Attestation>>() private var _attestations = MutableLiveData<ArrayList<Attestation>>()
private var fileName = "attestation.db" private var fileName = "attestation.db"
private val type = object : TypeToken<ArrayList<Attestation>>() {}.type private val type = object : TypeToken<ArrayList<Attestation>>() {}.type
fun getAttestations() : LiveData<MutableCollection<Attestation>> = fun getAttestations() : LiveData<ArrayList<Attestation>> =
_attestations _attestations
fun getAttestation(id: Int): Attestation? = fun getAttestation(id: Int): Attestation? =

View File

@ -3,6 +3,8 @@ package fr.sanchezm.attestationsCovid19.data.db.entity
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
const val PATTERN = "dd/MM/yyyy 'a' HH:mm"
//@Entity(tableName = "attestation") //@Entity(tableName = "attestation")
data class Attestation( data class Attestation(
val profile: Profile, val profile: Profile,
@ -11,8 +13,6 @@ data class Attestation(
var createAt: Long, var createAt: Long,
val reasons: List<Int> val reasons: List<Int>
) { ) {
private val pattern = "dd/MM/yyyy 'a' HH:mm"
override fun toString(): String { override fun toString(): String {
val motifs = StringBuilder() val motifs = StringBuilder()
@ -20,9 +20,9 @@ data class Attestation(
return "Cree le: ${getDate(createAt)}; $profile; Sortie: $exitDate a $exitHour; Motifs: $motifs" return "Cree le: ${getDate(createAt)}; $profile; Sortie: $exitDate a $exitHour; Motifs: $motifs"
} }
private fun getDate(dateTime: Long): String { fun getCreatedAtFormatedDate(): String = getDate(createAt)
return SimpleDateFormat(pattern, Locale.FRANCE).format(Date(dateTime))
} private fun getDate(dateTime: Long): String = SimpleDateFormat(PATTERN, Locale.FRANCE).format(Date(dateTime))
private fun getMotifText(i: Int): String { private fun getMotifText(i: Int): String {
return when (i) { return when (i) {

View File

@ -1,12 +1,15 @@
package fr.sanchezm.attestationsCovid19.ui.attestations package fr.sanchezm.attestationsCovid19.ui.attestations
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import fr.sanchezm.attestationsCovid19.PdfViewerActivity
import fr.sanchezm.attestationsCovid19.R import fr.sanchezm.attestationsCovid19.R
import fr.sanchezm.attestationsCovid19.databinding.FragmentAttestationsBinding import fr.sanchezm.attestationsCovid19.databinding.FragmentAttestationsBinding
import fr.sanchezm.attestationsCovid19.utilities.InjectorUtils import fr.sanchezm.attestationsCovid19.utilities.InjectorUtils
@ -31,9 +34,21 @@ class AttestationsFragment : Fragment() {
this.viewModel = attestationsViewModel this.viewModel = attestationsViewModel
} }
bindMessage()
return binding.root return binding.root
} }
private fun bindMessage() {
attestationsViewModel.startActivity.observe(viewLifecycleOwner, Observer {
it.getContentIfNotHandled()?.let { createAtPdf ->
val intent = Intent(context, PdfViewerActivity::class.java).apply {
putExtra("createAt", createAtPdf)
}
startActivity(intent)
}
})
}
private fun initializeUi() { private fun initializeUi() {
val factory = context?.let { InjectorUtils.provideAttestationViewModel(it) } val factory = context?.let { InjectorUtils.provideAttestationViewModel(it) }
attestationsViewModel = factory?.let { attestationsViewModel = factory?.let {

View File

@ -1,17 +1,30 @@
package fr.sanchezm.attestationsCovid19.ui.attestations package fr.sanchezm.attestationsCovid19.ui.attestations
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import fr.sanchezm.attestationsCovid19.data.repository.AttestationRepository import fr.sanchezm.attestationsCovid19.data.repository.AttestationRepository
import fr.sanchezm.attestationsCovid19.utilities.Event
class AttestationsViewModel(private val attestationRepository: AttestationRepository) : ViewModel() { class AttestationsViewModel(private val attestationRepository: AttestationRepository) : ViewModel() {
val attestationName = MutableLiveData<String>() private var attestationCreateAt: Long
private val _startActivity = MutableLiveData<Event<Long>>()
private val _attestationName = MutableLiveData<String>()
val startActivity: LiveData<Event<Long>> =
_startActivity
val attestationName: LiveData<String> =
_attestationName
fun onClick() { fun onClick() {
_startActivity.value = Event(attestationCreateAt)
} }
init { init {
attestationRepository.getAttestation(0) val attestation = attestationRepository.getAttestations().value?.last()
attestationCreateAt = attestation?.createAt!!
_attestationName.value = "${attestation.profile.firstName} ${attestation.profile.lastName}"
} }
} }

View File

@ -1,6 +1,7 @@
package fr.sanchezm.attestationsCovid19.utilities package fr.sanchezm.attestationsCovid19.utilities
import android.content.Context import android.content.Context
import android.os.Environment
import fr.sanchezm.attestationsCovid19.data.db.MyDatabase import fr.sanchezm.attestationsCovid19.data.db.MyDatabase
import fr.sanchezm.attestationsCovid19.data.repository.AttestationRepository import fr.sanchezm.attestationsCovid19.data.repository.AttestationRepository
import fr.sanchezm.attestationsCovid19.data.repository.ProfileRepository import fr.sanchezm.attestationsCovid19.data.repository.ProfileRepository
@ -25,4 +26,10 @@ object InjectorUtils {
return AttestationsViewModelFactory(attestationRepository) return AttestationsViewModelFactory(attestationRepository)
} }
fun providePdfUtils(context: Context): PdfUtils {
return PdfUtils.getInstance(context.assets, getMyFilesDir(context))
}
private fun getMyFilesDir(context: Context): String = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.path!!
} }

View File

@ -3,6 +3,11 @@ package fr.sanchezm.attestationsCovid19.utilities
import android.content.res.AssetManager import android.content.res.AssetManager
import android.util.Log import android.util.Log
import com.tom_roush.pdfbox.pdmodel.PDDocument import com.tom_roush.pdfbox.pdmodel.PDDocument
import com.tom_roush.pdfbox.pdmodel.PDPage
import com.tom_roush.pdfbox.pdmodel.PDPageContentStream
import com.tom_roush.pdfbox.pdmodel.common.PDRectangle
import com.tom_roush.pdfbox.pdmodel.font.PDType1Font
import com.tom_roush.pdfbox.pdmodel.graphics.image.LosslessFactory
import com.tom_roush.pdfbox.pdmodel.interactive.form.PDAcroForm import com.tom_roush.pdfbox.pdmodel.interactive.form.PDAcroForm
import com.tom_roush.pdfbox.pdmodel.interactive.form.PDCheckbox import com.tom_roush.pdfbox.pdmodel.interactive.form.PDCheckbox
import com.tom_roush.pdfbox.pdmodel.interactive.form.PDTextField import com.tom_roush.pdfbox.pdmodel.interactive.form.PDTextField
@ -20,6 +25,7 @@ class PdfUtils private constructor(
private val folderPath = "attestations" private val folderPath = "attestations"
private val checkboxListNames = ArrayList<String>() private val checkboxListNames = ArrayList<String>()
private val font = PDType1Font.HELVETICA
fun fillForm(attestation: Attestation) { fun fillForm(attestation: Attestation) {
checkFolder() checkFolder()
@ -33,17 +39,75 @@ class PdfUtils private constructor(
setFieldsData(acroForm, attestation) setFieldsData(acroForm, attestation)
setCheckboxFields(acroForm, attestation) setCheckboxFields(acroForm, attestation)
attestation.createAt = Date().time attestation.createAt = Date().time
addPageWithQrCode(document, attestation)
val savePath = "$path/$folderPath/${attestation.createAt}.pdf" Log.v("PdfUtils", "Save File to ${getPath(attestation.createAt)}")
document.save(getPath(attestation.createAt))
Log.v("PdfUtils", "Save File to $savePath")
document.save(savePath)
document.close() document.close()
} catch (e: IOException) { } catch (e: IOException) {
e.printStackTrace() e.printStackTrace()
} }
} }
fun getPath(createAt: Long): String = "$path/$folderPath/$createAt.pdf"
private fun addPageWithQrCode(document: PDDocument, attestation: Attestation) {
val size = 360
val margin = 40f
// Add QRCode on the first page
val page1: PDPage = document.pages.first()
Log.v("PdfUtils", "Width: ${page1.mediaBox.width}; Height: ${page1.mediaBox.height}")
addQrCode(
document,
page1,
attestation,
size / 3,
page1.mediaBox.upperRightX - margin - size / 3 - 30,
141f,
true
)
// Add QRCode on a new page
val page2 = PDPage(PDRectangle.A4)
document.addPage(page2)
addQrCode(document, page2, attestation, size, margin, page2.mediaBox.height - (size + margin))
}
private fun addQrCode(
document: PDDocument,
page: PDPage,
attestation: Attestation,
size: Int,
x: Float,
y: Float,
addText: Boolean = false
) {
val contentStream = PDPageContentStream(document, page, true, false)
val alphaXimage =
LosslessFactory.createFromImage(document, QRCodeUtils.getQrCode(attestation, size))
contentStream.drawImage(alphaXimage, x, y)
if (addText) {
contentStream.beginText()
contentStream.setFont(font, 7f)
contentStream.newLineAtOffset(page.mediaBox.upperRightX - size - 20, 145f)
contentStream.showText("Date de création:")
contentStream.endText()
contentStream.beginText()
contentStream.setFont(font, 7f)
contentStream.newLineAtOffset(page.mediaBox.upperRightX - size - 27, 138f)
contentStream.showText(attestation.getCreatedAtFormatedDate())
contentStream.endText()
}
contentStream.close()
}
private fun setFieldsData(acroForm: PDAcroForm, attestation: Attestation) { private fun setFieldsData(acroForm: PDAcroForm, attestation: Attestation) {
val profile = attestation.profile val profile = attestation.profile

View File

@ -9,9 +9,11 @@ import fr.sanchezm.attestationsCovid19.data.db.entity.Attestation
class QRCodeUtils { class QRCodeUtils {
fun getQrCode(attestation: Attestation): Bitmap = companion object {
BarcodeEncoder().createBitmap(getMultiFormatWriter(attestation)) fun getQrCode(attestation: Attestation, size: Int): Bitmap =
BarcodeEncoder().createBitmap(getMultiFormatWriter(attestation, size))
private fun getMultiFormatWriter(attestation: Attestation) = private fun getMultiFormatWriter(attestation: Attestation, size: Int) =
MultiFormatWriter().encode(attestation.toString(), BarcodeFormat.QR_CODE, 150, 150) MultiFormatWriter().encode(attestation.toString(), BarcodeFormat.QR_CODE, size, size)
}
} }

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="?attr/itemBackground" />
<corners android:radius="10dip" />
<stroke
android:width="1dp"
android:color="@android:color/white" />
</shape>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000"
tools:context=".PdfViewerActivity">
<com.github.barteksc.pdfviewer.PDFView
android:id="@+id/pdfView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/custom_rectangle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp">
<TextView
android:id="@+id/people_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/people_item"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/date_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/date_item"
android:layout_marginTop="5dp"
app:layout_constraintTop_toBottomOf="@+id/people_item"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/reason_item"
android:layout_marginTop="5dp"
app:layout_constraintTop_toBottomOf="@+id/date_item"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
android:paddingHorizontal="30dp"
android:paddingVertical="20dp">
<data> <data>
<variable <variable
@ -14,16 +16,28 @@
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".ui.attestations.AttestationsFragment"> tools:context=".ui.attestations.AttestationsFragment">
<com.google.android.material.button.MaterialButton <TextView
android:layout_width="wrap_content" android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.onClick()}" android:layout_marginTop="15dp"
android:text="@{viewModel.attestationName}" android:text="@string/title_attestations"
app:layout_constraintBottom_toBottomOf="parent" android:textColor="?attr/colorPrimaryDark"
android:textSize="30sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/attestation_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</layout> </layout>

View File

@ -40,4 +40,9 @@
<string name="develop_by">"Développer avec ❤️ par <string name="develop_by">"Développer avec ❤️ par
<a href="https://www.sanchezm.fr/">Mathieu Sanchez</a>"</string> <a href="https://www.sanchezm.fr/">Mathieu Sanchez</a>"</string>
<string name="version_number">Version :</string> <string name="version_number">Version :</string>
<!-- Attestation Items -->
<string name="people_item">Personne :</string>
<string name="date_item">Date de sortie :</string>
<string name="reason_item">Raison(s) :</string>
</resources> </resources>