First version finished

This commit is contained in:
Mathieu Sanchez 2020-04-18 19:50:50 +02:00
parent 28e348c194
commit d45d9535e0
18 changed files with 325 additions and 83 deletions

View File

@ -56,6 +56,7 @@ dependencies {
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1' implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1'
implementation 'androidx.navigation:navigation-fragment:2.2.1' implementation 'androidx.navigation:navigation-fragment:2.2.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.2.1'
implementation 'androidx.navigation:navigation-ui:2.2.1' implementation 'androidx.navigation:navigation-ui:2.2.1'

View File

@ -12,8 +12,7 @@
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=".QrCodeActivity"></activity> <activity android:name=".QrCodeActivity" />
<activity android:name=".QrCodeViewerActivity" />
<activity android:name=".PdfViewerActivity" /> <activity android:name=".PdfViewerActivity" />
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"

View File

@ -4,23 +4,23 @@ import android.content.Context
import fr.sanchezm.attestationsCovid19.data.db.dao.AttestationDao import fr.sanchezm.attestationsCovid19.data.db.dao.AttestationDao
import fr.sanchezm.attestationsCovid19.data.db.dao.ProfileDao import fr.sanchezm.attestationsCovid19.data.db.dao.ProfileDao
class MyDatabase private constructor(private val path: String) { class MyDatabase private constructor(private val savePath: String, private val filesPath: String) {
private var _profileDao: ProfileDao? = null private var _profileDao: ProfileDao? = null
private var _attestationDao: AttestationDao? = null private var _attestationDao: AttestationDao? = null
fun profileDao(): ProfileDao = _profileDao ?: ProfileDao(path).also { _profileDao = it } fun profileDao(): ProfileDao = _profileDao ?: ProfileDao(savePath).also { _profileDao = it }
fun attestationDao(): AttestationDao = fun attestationDao(): AttestationDao =
_attestationDao ?: AttestationDao(path).also { _attestationDao = it } _attestationDao ?: AttestationDao(savePath, filesPath).also { _attestationDao = it }
companion object { companion object {
@Volatile @Volatile
private var instance: MyDatabase? = null private var instance: MyDatabase? = null
private val LOCK = Any() private val LOCK = Any()
operator fun invoke(context: Context) = instance ?: synchronized(LOCK) { operator fun invoke(context: Context, filesPath: String) = instance ?: synchronized(LOCK) {
instance ?: MyDatabase(context.filesDir.path).also { instance = it } instance ?: MyDatabase(context.filesDir.path, filesPath).also { instance = it }
} }
} }
} }

View File

@ -1,5 +1,6 @@
package fr.sanchezm.attestationsCovid19.data.db.dao package fr.sanchezm.attestationsCovid19.data.db.dao
import android.util.Log
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.google.gson.Gson import com.google.gson.Gson
@ -8,20 +9,24 @@ import fr.sanchezm.attestationsCovid19.data.db.entity.Attestation
import fr.sanchezm.attestationsCovid19.utilities.PdfUtils import fr.sanchezm.attestationsCovid19.utilities.PdfUtils
import java.io.File import java.io.File
class AttestationDao(private val path: String) { class AttestationDao(private val savePath: String, private val filesPath: String) {
private var _attestations = MutableLiveData<ArrayList<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<ArrayList<Attestation>> = @Suppress("UNCHECKED_CAST")
_attestations fun getAttestations(): LiveData<List<Attestation>> =
_attestations as LiveData<List<Attestation>>
fun getAttestation(id: Int): Attestation? = fun getAttestation(id: Int): Attestation? =
_attestations.value?.elementAt(id) _attestations.value?.elementAt(id)
fun addAttestation(attestation: Attestation) { fun addAttestation(attestation: Attestation) {
_attestations.value = _attestations.value ?: ArrayList()
_attestations.value?.add(attestation) _attestations.value?.add(attestation)
Log.d(TAG, "Add Attestation : $attestation")
save() save()
} }
@ -33,36 +38,50 @@ class AttestationDao(private val path: String) {
return null return null
} }
fun deleteAttestation(attestation: Attestation) {
deleteFile(attestation)
delete(attestation)
}
init { init {
load() load()
} }
private fun delete(attestation: Attestation) {
_attestations.value?.remove(attestation)
}
private fun deleteFile(attestation: Attestation) {
File(PdfUtils.getPath(filesPath, attestation.createAt)).also {
if (it.exists())
it.delete()
}
_attestations.value = _attestations.value
}
private fun load() { private fun load() {
val file = File("$path/$fileName") val file = File("$savePath/$fileName")
val removeList = ArrayList<Attestation>()
if (file.exists()) { if (file.exists()) {
_attestations.value = Gson().fromJson(file.readText(Charsets.UTF_8), type) _attestations.value = Gson().fromJson(file.readText(Charsets.UTF_8), type)
} }
_attestations.value?.forEach { _attestations.value?.forEach {
val filePath = PdfUtils.getInstance()?.getPath(it.createAt) val filePath = PdfUtils.getPath(filesPath, it.createAt)
if (filePath != null && !File(filePath).exists()) { if (!File(filePath).exists()) {
_attestations.value!!.remove(it) removeList.add(it)
} }
} }.also { _attestations.value?.removeAll(removeList) }
} }
private fun save() { private fun save() {
File("$path/$fileName").writeText(Gson().toJson(_attestations.value)) Log.d(TAG, "Save : ${Gson().toJson(_attestations.value)}")
File("$savePath/$fileName").writeText(Gson().toJson(_attestations.value))
} }
companion object {
private val TAG = AttestationDao::class.simpleName
}
} }
//@Dao
//interface AttestationDao {
// @Insert
// fun insert(attestation: Attestation)
//
// @Query(value = "SELECT * FROM attestation WHERE id = $CURRENT_ATTESTATION_ID")
// fun getAttestation(): LiveData<Attestation>
//}

View File

@ -14,12 +14,18 @@ data class Attestation(
val reasons: List<Int> val reasons: List<Int>
) { ) {
override fun toString(): String { override fun toString(): String {
return "Cree le: ${getDate(createAt)}; $profile; Sortie: $exitDate a $exitHour; Motifs: ${getMotifsText()}" return "Cree le: ${getDate(createAt)}; $profile; Sortie: ${getExitDateFormatted()}; Motifs: ${getMotifsText()}"
} }
fun getCreatedAtFormatedDate(): String = getDate(createAt) fun getNameFormatted(): String = "${profile.firstName} ${profile.lastName}"
fun getMotifsText(): String { fun getCreatedAtFormattedDate(): String = getDate(createAt)
fun getExitDateFormatted(): String = "$exitDate a $exitHour"
fun getMotifsFormatted(): String = getMotifsText().replace("-", " ")
private fun getMotifsText(): String {
val motifs = StringBuilder() val motifs = StringBuilder()
reasons.forEach { motifs.append(getMotifText(it), "-") } reasons.forEach { motifs.append(getMotifText(it), "-") }

View File

@ -6,8 +6,8 @@ import fr.sanchezm.attestationsCovid19.utilities.PdfUtils
class AttestationRepository private constructor(private val attestationDao: AttestationDao) { class AttestationRepository private constructor(private val attestationDao: AttestationDao) {
fun generateAttestation(attestation: Attestation) = fun generateAttestation(attestation: Attestation, callback: (Boolean) -> Unit) =
PdfUtils.getInstance()?.fillForm(attestation) PdfUtils.getInstance()?.fillForm(attestation, callback)
fun getAttestations() = attestationDao.getAttestations() fun getAttestations() = attestationDao.getAttestations()
@ -17,6 +17,8 @@ class AttestationRepository private constructor(private val attestationDao: Atte
fun addAttestation(attestation: Attestation) = attestationDao.addAttestation(attestation) fun addAttestation(attestation: Attestation) = attestationDao.addAttestation(attestation)
fun deleteAttestation(attestation: Attestation) = attestationDao.deleteAttestation(attestation)
companion object { companion object {
@Volatile @Volatile
private var instance: AttestationRepository? = null private var instance: AttestationRepository? = null

View File

@ -53,8 +53,8 @@ class AddFragment : Fragment() {
} }
}) })
addViewModel.startActivity.observe(viewLifecycleOwner, Observer { addViewModel.startActivity.observe(viewLifecycleOwner, Observer { event ->
it.getContentIfNotHandled()?.let { event.getContentIfNotHandled()?.let {
val intent = Intent(context, QrCodeActivity::class.java).apply { val intent = Intent(context, QrCodeActivity::class.java).apply {
putExtra("createAt", it) putExtra("createAt", it)
putExtra("toCreate", true) putExtra("toCreate", true)

View File

@ -67,8 +67,6 @@ class AddViewModel(
attestationRepository.addAttestation(attestation) attestationRepository.addAttestation(attestation)
_startActivity.value = Event(attestation.createAt) _startActivity.value = Event(attestation.createAt)
// attestationRepository.generateAttestation(attestation)
// _dataMessage.value = Event(R.string.attestation_generated)
} else { } else {
_errorMessage.value = Event(R.string.error_cannot_create_attestation) _errorMessage.value = Event(R.string.error_cannot_create_attestation)
Log.e("onGenerateAttestationClick", "Cannot generate Attestation") Log.e("onGenerateAttestationClick", "Cannot generate Attestation")

View File

@ -41,13 +41,10 @@ class AttestationsAdapter(
private val reasonItem = view.reason_item_data private val reasonItem = view.reason_item_data
fun bind(attestation: Attestation) { fun bind(attestation: Attestation) {
peopleItem.text = getName(attestation) peopleItem.text = attestation.getNameFormatted()
dateItem.text = attestation.getCreatedAtFormatedDate() dateItem.text = attestation.getExitDateFormatted()
reasonItem.text = attestation.getMotifsText() reasonItem.text = attestation.getMotifsFormatted()
view.setOnClickListener { itemClickListener.onItemClick(attestation) } view.setOnClickListener { itemClickListener.onItemClick(attestation) }
} }
private fun getName(attestation: Attestation) =
"${attestation.profile.firstName} ${attestation.profile.lastName}"
} }
} }

View File

@ -11,6 +11,7 @@ import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import fr.sanchezm.attestationsCovid19.PdfViewerActivity import fr.sanchezm.attestationsCovid19.PdfViewerActivity
import fr.sanchezm.attestationsCovid19.QrCodeActivity
import fr.sanchezm.attestationsCovid19.R import fr.sanchezm.attestationsCovid19.R
import fr.sanchezm.attestationsCovid19.data.db.entity.Attestation import fr.sanchezm.attestationsCovid19.data.db.entity.Attestation
import fr.sanchezm.attestationsCovid19.databinding.FragmentAttestationsBinding import fr.sanchezm.attestationsCovid19.databinding.FragmentAttestationsBinding
@ -24,8 +25,9 @@ class AttestationsFragment : Fragment() {
private val attestationsAdapter = AttestationsAdapter(arrayListOf(), private val attestationsAdapter = AttestationsAdapter(arrayListOf(),
object : ItemClickListener { object : ItemClickListener {
override fun onItemClick(item: Attestation) { override fun onItemClick(item: Attestation) {
val intent = Intent(context, PdfViewerActivity::class.java).apply { val intent = Intent(context, QrCodeActivity::class.java).apply {
putExtra("createAt", item.createAt) putExtra("createAt", item.createAt)
putExtra("toCreate", false)
} }
startActivity(intent) startActivity(intent)
} }
@ -56,8 +58,8 @@ class AttestationsFragment : Fragment() {
} }
private fun bindMessage() { private fun bindMessage() {
attestationsViewModel.startActivity.observe(viewLifecycleOwner, Observer { attestationsViewModel.startActivity.observe(viewLifecycleOwner, Observer { event ->
it.getContentIfNotHandled()?.let { createAtPdf -> event.getContentIfNotHandled()?.let { createAtPdf ->
val intent = Intent(context, PdfViewerActivity::class.java).apply { val intent = Intent(context, PdfViewerActivity::class.java).apply {
putExtra("createAt", createAtPdf) putExtra("createAt", createAtPdf)
} }

View File

@ -4,29 +4,20 @@ import android.view.View
import androidx.lifecycle.LiveData 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.db.entity.Attestation
import fr.sanchezm.attestationsCovid19.data.repository.AttestationRepository import fr.sanchezm.attestationsCovid19.data.repository.AttestationRepository
import fr.sanchezm.attestationsCovid19.utilities.Event import fr.sanchezm.attestationsCovid19.utilities.Event
class AttestationsViewModel(attestationRepository: AttestationRepository) : ViewModel() { class AttestationsViewModel(attestationRepository: AttestationRepository) : ViewModel() {
private val _startActivity = MutableLiveData<Event<Long>>() private val _startActivity = MutableLiveData<Event<Long>>()
private var _attestations = attestationRepository.getAttestations() as MutableLiveData<ArrayList<Attestation>>
private var _displayNoAttestation = MutableLiveData<Int>() private var _displayNoAttestation = MutableLiveData<Int>()
val attestations: LiveData<ArrayList<Attestation>> = val attestations = attestationRepository.getAttestations()
_attestations val startActivity: LiveData<Event<Long>> = _startActivity
val displayNoAttestation: LiveData<Int> = _displayNoAttestation
val startActivity: LiveData<Event<Long>> =
_startActivity
val displayNoAttestation: LiveData<Int> =
_displayNoAttestation
init { init {
if (_attestations.value == null || _attestations.value?.size == 0) { if (attestations.value == null || attestations.value?.size == 0) {
_displayNoAttestation.value = View.VISIBLE _displayNoAttestation.value = View.VISIBLE
} else { } else {
_displayNoAttestation.value = View.GONE _displayNoAttestation.value = View.GONE

View File

@ -1,15 +1,19 @@
package fr.sanchezm.attestationsCovid19.ui.qrcode package fr.sanchezm.attestationsCovid19.ui.qrcode
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment
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.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.QrCodeFragmentBinding import fr.sanchezm.attestationsCovid19.databinding.QrCodeFragmentBinding
import fr.sanchezm.attestationsCovid19.utilities.InjectorUtils import fr.sanchezm.attestationsCovid19.utilities.InjectorUtils
import kotlinx.android.synthetic.main.qr_code_fragment.*
class QrCodeFragment : Fragment() { class QrCodeFragment : Fragment() {
@ -41,10 +45,11 @@ class QrCodeFragment : Fragment() {
this.viewModel = qrCodeViewModel this.viewModel = qrCodeViewModel
} }
bindingData()
return binding.root return binding.root
} }
private fun initializeUi(createAt: Long, toCreate: Boolean) { private fun initializeUi(createAt: Long, toCreate: Boolean) {
val factory = context?.let { InjectorUtils.provideQrCodeViewModel(it) } val factory = context?.let { InjectorUtils.provideQrCodeViewModel(it) }
qrCodeViewModel = factory?.let { qrCodeViewModel = factory?.let {
@ -54,4 +59,28 @@ class QrCodeFragment : Fragment() {
qrCodeViewModel.addData(createAt, toCreate) qrCodeViewModel.addData(createAt, toCreate)
} }
private fun bindingData() {
qrCodeViewModel.qrCodeBitmap.observe(viewLifecycleOwner, Observer {
qrcode_image.setImageBitmap(it)
})
qrCodeViewModel.finish.observe(viewLifecycleOwner, Observer { event ->
event.getContentIfNotHandled()?.let {
if (it) {
activity?.finish()
}
}
})
qrCodeViewModel.startActivity.observe(viewLifecycleOwner, Observer { event ->
event.getContentIfNotHandled()?.let {
val intent = Intent(context, PdfViewerActivity::class.java).apply {
putExtra("createAt", it)
putExtra("toCreate", true)
}
startActivity(intent)
}
})
}
} }

View File

@ -1,24 +1,85 @@
package fr.sanchezm.attestationsCovid19.ui.qrcode package fr.sanchezm.attestationsCovid19.ui.qrcode
import android.graphics.Bitmap
import android.view.View
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.db.entity.Attestation import fr.sanchezm.attestationsCovid19.data.db.entity.Attestation
import fr.sanchezm.attestationsCovid19.data.repository.AttestationRepository import fr.sanchezm.attestationsCovid19.data.repository.AttestationRepository
import fr.sanchezm.attestationsCovid19.utilities.Event
import fr.sanchezm.attestationsCovid19.utilities.QRCodeUtils
class QrCodeViewModel(private val attestationRepository: AttestationRepository) : ViewModel() { class QrCodeViewModel(private val attestationRepository: AttestationRepository) : ViewModel() {
private val _attestation = MutableLiveData<Attestation>() private val _attestation = MutableLiveData<Attestation>()
private var toCreate: Boolean = false
private val _loading = MutableLiveData(View.GONE)
private val _displayQrCode = MutableLiveData(View.GONE)
private val _qrCodeBitmap = MutableLiveData<Bitmap>()
private val _peopleItem = MutableLiveData<String>()
private val _dateItem = MutableLiveData<String>()
private val _reasonItem = MutableLiveData<String>()
private val _startActivity = MutableLiveData<Event<Long>>()
private val _finish = MutableLiveData<Event<Boolean>>()
val attestation: LiveData<Attestation> = _attestation
val loading: LiveData<Int> = _loading
val displayQrCode: LiveData<Int> = _displayQrCode
val qrCodeBitmap: LiveData<Bitmap> = _qrCodeBitmap
val peopleItem: LiveData<String> = _peopleItem
val dateItem: LiveData<String> = _dateItem
val reasonItem: LiveData<String> = _reasonItem
val startActivity: LiveData<Event<Long>> = _startActivity
val finish: LiveData<Event<Boolean>> = _finish
fun addData(createAt: Long, toCreate: Boolean) { fun addData(createAt: Long, toCreate: Boolean) {
this.toCreate = toCreate val att = attestationRepository.getAttestation(createAt)
_attestation.value = attestationRepository.getAttestation(createAt)
_attestation.value = att
if (toCreate) { if (toCreate) {
_loading.value = View.VISIBLE
createPdf() createPdf()
} else {
displayQrCode()
} }
} }
fun createPdf() { fun displayAttestation() {
_startActivity.value = Event(_attestation.value!!.createAt)
}
fun deleteAttestation() {
attestationRepository.deleteAttestation(_attestation.value!!)
_finish.value = Event(true)
}
private fun createPdf() {
_attestation.value?.let { attesattion ->
attestationRepository.generateAttestation(
attesattion
) { success ->
if (success)
displayQrCode()
}
}
}
private fun displayQrCode() {
_qrCodeBitmap.value = _attestation.value?.let { QRCodeUtils.getQrCode(it, 300) }
_peopleItem.value = attestation.value?.getNameFormatted()
_dateItem.value = attestation.value?.getExitDateFormatted()
_reasonItem.value = attestation.value?.getMotifsFormatted()
_loading.value = View.GONE
_displayQrCode.value = View.VISIBLE
}
companion object {
private val TAG = QrCodeViewModel::class.simpleName
} }
} }

View File

@ -13,23 +13,31 @@ object InjectorUtils {
fun provideAddViewModelFactory(context: Context): AddViewModelFactory { fun provideAddViewModelFactory(context: Context): AddViewModelFactory {
val profileRepository = val profileRepository =
ProfileRepository.getInstance(MyDatabase.invoke(context).profileDao()) ProfileRepository.getInstance(
MyDatabase.invoke(context, getMyFilesDir(context)).profileDao()
)
val attestationRepository = val attestationRepository =
AttestationRepository.getInstance(MyDatabase.invoke(context).attestationDao()) AttestationRepository.getInstance(
MyDatabase.invoke(context, getMyFilesDir(context)).attestationDao()
)
return AddViewModelFactory(profileRepository, attestationRepository) return AddViewModelFactory(profileRepository, attestationRepository)
} }
fun provideAttestationViewModel(context: Context): AttestationsViewModelFactory { fun provideAttestationViewModel(context: Context): AttestationsViewModelFactory {
val attestationRepository = val attestationRepository =
AttestationRepository.getInstance(MyDatabase.invoke(context).attestationDao()) AttestationRepository.getInstance(
MyDatabase.invoke(context, getMyFilesDir(context)).attestationDao()
)
return AttestationsViewModelFactory(attestationRepository) return AttestationsViewModelFactory(attestationRepository)
} }
fun provideQrCodeViewModel(context: Context): QrCodeViewModelFactory { fun provideQrCodeViewModel(context: Context): QrCodeViewModelFactory {
val attestationRepository = val attestationRepository =
AttestationRepository.getInstance(MyDatabase.invoke(context).attestationDao()) AttestationRepository.getInstance(
MyDatabase.invoke(context, getMyFilesDir(context)).attestationDao()
)
return QrCodeViewModelFactory(attestationRepository) return QrCodeViewModelFactory(attestationRepository)
} }

View File

@ -14,8 +14,6 @@ import com.tom_roush.pdfbox.pdmodel.interactive.form.PDTextField
import fr.sanchezm.attestationsCovid19.data.db.entity.Attestation import fr.sanchezm.attestationsCovid19.data.db.entity.Attestation
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.util.*
import kotlin.collections.ArrayList
class PdfUtils private constructor( class PdfUtils private constructor(
@ -23,11 +21,10 @@ class PdfUtils private constructor(
private val path: String private val path: String
) { ) {
private val folderPath = "attestations"
private val checkboxListNames = ArrayList<String>() private val checkboxListNames = ArrayList<String>()
private val font = PDType1Font.HELVETICA private val font = PDType1Font.HELVETICA
fun fillForm(attestation: Attestation) { fun fillForm(attestation: Attestation, callback: (Boolean) -> Unit) {
checkFolder() checkFolder()
try { try {
// Load the document and get the AcroForm // Load the document and get the AcroForm
@ -43,12 +40,14 @@ class PdfUtils private constructor(
Log.v("PdfUtils", "Save File to ${getPath(attestation.createAt)}") Log.v("PdfUtils", "Save File to ${getPath(attestation.createAt)}")
document.save(getPath(attestation.createAt)) document.save(getPath(attestation.createAt))
document.close() document.close()
callback(true)
} catch (e: IOException) { } catch (e: IOException) {
e.printStackTrace() e.printStackTrace()
callback(false)
} }
} }
fun getPath(createAt: Long): String = "$path/$folderPath/$createAt.pdf" fun getPath(createAt: Long): String = getPath(path, createAt)
private fun addPageWithQrCode(document: PDDocument, attestation: Attestation) { private fun addPageWithQrCode(document: PDDocument, attestation: Attestation) {
val size = 360 val size = 360
@ -100,7 +99,7 @@ class PdfUtils private constructor(
contentStream.beginText() contentStream.beginText()
contentStream.setFont(font, 7f) contentStream.setFont(font, 7f)
contentStream.newLineAtOffset(page.mediaBox.upperRightX - size - 27, 138f) contentStream.newLineAtOffset(page.mediaBox.upperRightX - size - 27, 138f)
contentStream.showText(attestation.getCreatedAtFormatedDate()) contentStream.showText(attestation.getCreatedAtFormattedDate())
contentStream.endText() contentStream.endText()
} }
contentStream.close() contentStream.close()
@ -150,12 +149,16 @@ class PdfUtils private constructor(
@Volatile @Volatile
private var instance: PdfUtils? = null private var instance: PdfUtils? = null
private val LOCK = Any() private val LOCK = Any()
private const val folderPath = "attestations"
fun getInstance() = instance fun getInstance() = instance
fun getInstance(assetManager: AssetManager, path: String) = instance ?: synchronized(LOCK) { fun getInstance(assetManager: AssetManager, path: String) = instance ?: synchronized(LOCK) {
instance ?: PdfUtils(assetManager, path).also { instance = it; it.initList() } instance ?: PdfUtils(assetManager, path).also { instance = it; it.initList() }
} }
fun getPath(path: String, createAt: Long): String = "$path/$folderPath/$createAt.pdf"
} }
private fun initList() { private fun initList() {

View File

@ -1,9 +1,10 @@
<?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:tools="http://schemas.android.com/tools" 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">
<data> <data>
<variable <variable
name="viewModel" name="viewModel"
type="fr.sanchezm.attestationsCovid19.ui.qrcode.QrCodeViewModel" /> type="fr.sanchezm.attestationsCovid19.ui.qrcode.QrCodeViewModel" />
@ -15,13 +16,132 @@
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".ui.qrcode.QrCodeFragment"> tools:context=".ui.qrcode.QrCodeFragment">
<androidx.core.widget.ContentLoadingProgressBar <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent" android:visibility="@{viewModel.loading}">
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" <ProgressBar
app:layout_constraintStart_toStartOf="parent" /> android:id="@+id/progress_circular"
android:layout_width="60dp"
android:layout_height="60dp"
android:visibility="@{viewModel.loading}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/generating_attestation"
android:textSize="18sp"
android:visibility="@{viewModel.loading}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/progress_circular" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="@{viewModel.displayQrCode}">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/qrcode_image"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_marginTop="60dp"
android:contentDescription="@string/qrcode_attestation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/people_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="60dp"
android:layout_marginTop="20dp"
android:text="@string/people_item"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/qrcode_image" />
<TextView
android:id="@+id/date_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="60dp"
android:layout_marginTop="5dp"
android:text="@string/date_item"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/people_item" />
<TextView
android:id="@+id/reason_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="60dp"
android:layout_marginTop="5dp"
android:text="@string/reason_item"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/date_item" />
<TextView
android:id="@+id/people_item_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginEnd="60dp"
android:text="@{viewModel.peopleItem}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/qrcode_image" />
<TextView
android:id="@+id/date_item_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginEnd="60dp"
android:text="@{viewModel.dateItem}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/people_item_data" />
<TextView
android:id="@+id/reason_item_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginEnd="60dp"
android:text="@{viewModel.reasonItem}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/date_item_data" />
<com.google.android.material.button.MaterialButton
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:onClick="@{() -> viewModel.displayAttestation()}"
android:text="@string/display_attestation"
app:layout_constraintBottom_toTopOf="@+id/delete"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/delete"
style="?attr/materialButtonOutlinedStyle"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_marginBottom="40dp"
android:onClick="@{() -> viewModel.deleteAttestation()}"
android:text="@string/delete_attestation"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -46,4 +46,10 @@
<string name="people_item">Personne :</string> <string name="people_item">Personne :</string>
<string name="date_item">Date de sortie :</string> <string name="date_item">Date de sortie :</string>
<string name="reason_item">Raison(s) :</string> <string name="reason_item">Raison(s) :</string>
<!-- QR Fragment -->
<string name="generating_attestation">Création de votre attesation</string>
<string name="qrcode_attestation">QrCode Attestation</string>
<string name="display_attestation">Afficher l\'attestation</string>
<string name="delete_attestation">Supprimer l\'attestation</string>
</resources> </resources>

View File

@ -8,7 +8,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.6.2' classpath 'com.android.tools.build:gradle:3.6.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong