11 Commits

14 changed files with 145 additions and 69 deletions

View File

@ -15,8 +15,8 @@ android {
applicationId "fr.sanchezm.attestationsCovid19"
minSdkVersion 23
targetSdkVersion 30
versionCode 9
versionName "2.0.0"
versionCode 10
versionName "2.0.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

Binary file not shown.

View File

@ -3,7 +3,7 @@ package fr.sanchezm.attestationsCovid19.data.db.entity
import java.text.SimpleDateFormat
import java.util.*
const val PATTERN = "dd/MM/yyyy 'à' HH'h'mm"
const val PATTERN = "dd/MM/yyyy 'à' HH:mm"
//@Entity(tableName = "attestation")
data class Attestation(
@ -14,7 +14,7 @@ data class Attestation(
val reasons: List<Int>
) {
override fun toString(): String {
return "Cree le: ${getDate(createAt)}; $profile; Sortie: ${getExitDateFormatted()}; Motifs: ${getMotifsText()}"
return "Cree le: ${getDate(createAt)};\n$profile;\nSortie: ${getExitDateFormatted()};\nMotifs: ${getMotifsText()}"
}
fun getNameFormatted(): String = "${profile.firstName} ${profile.lastName}"
@ -28,8 +28,8 @@ data class Attestation(
private fun getMotifsText(): String {
val motifs = StringBuilder()
reasons.forEach { motifs.append(getMotifText(it), "-") }
return motifs.toString().dropLast(1)
reasons.forEach { motifs.append(getMotifText(it), ", ") }
return motifs.toString().dropLast(2)
}
private fun getDate(dateTime: Long): String = SimpleDateFormat(PATTERN, Locale.FRANCE).format(Date(dateTime))
@ -37,7 +37,7 @@ data class Attestation(
fun getMotifText(i: Int): String {
return when (i) {
1 -> "travail"
2 -> "achats"
2 -> "achats_culturel_culturel"
3 -> "sante"
4 -> "famille"
5 -> "handicap"

View File

@ -14,6 +14,6 @@ data class Profile(
// var id: Int = CURRENT_PROFILE_ID
override fun toString(): String {
return "Nom: $lastName; Prenom: $firstName; Naissance: $birthday a $birthPlace; Adresse: $address $postalCode $city"
return "Nom: $lastName;\nPrenom: $firstName;\nNaissance: $birthday a $birthPlace;\nAdresse: $address $postalCode $city"
}
}

View File

@ -1,6 +1,9 @@
package fr.sanchezm.attestationsCovid19.ui.add
import android.annotation.SuppressLint
import android.app.DatePickerDialog
import android.app.TimePickerDialog
import android.content.Context
import android.os.Build
import android.util.Log
import androidx.lifecycle.LiveData
@ -13,6 +16,7 @@ import fr.sanchezm.attestationsCovid19.data.repository.AttestationRepository
import fr.sanchezm.attestationsCovid19.data.repository.ConfigRepository
import fr.sanchezm.attestationsCovid19.data.repository.ProfileRepository
import fr.sanchezm.attestationsCovid19.utilities.Event
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
@ -21,7 +25,8 @@ import java.util.*
class AddViewModel(
private val configRepository: ConfigRepository,
private val profileRepository: ProfileRepository,
private val attestationRepository: AttestationRepository
private val attestationRepository: AttestationRepository,
private val app: Context
) : ViewModel() {
private val _errorMessage = MutableLiveData<Event<Int>>()
@ -60,7 +65,7 @@ class AddViewModel(
// endregion
private val datePattern = "dd/MM/yyyy"
private val timePattern = "HH'h'mm"
private val timePattern = "HH:mm"
@SuppressLint("LongLogTag")
fun onGenerateAttestationClick() {
@ -77,6 +82,68 @@ class AddViewModel(
}
}
fun onBirthdayClick() {
val c = Calendar.getInstance().also { it.set(1970, 0, 1) }
if (!birthday.value.isNullOrBlank()) {
birthday.value?.let { birthday ->
DateFormat.getDateInstance(DateFormat.SHORT, Locale.FRANCE).parse(birthday)
?.let { c.time = it }
}
}
val year = c.get(Calendar.YEAR)
val month = c.get(Calendar.MONTH)
val day = c.get(Calendar.DAY_OF_MONTH)
val dpd = DatePickerDialog(
app,
DatePickerDialog.OnDateSetListener { _, yearPicked, monthOfYear, dayOfMonth ->
birthday.value =
"${getFormattedDayOrMonth(dayOfMonth)}/${getFormattedDayOrMonth(monthOfYear + 1)}/$yearPicked"
},
year,
month,
day
)
dpd.show()
}
fun onExitDateClick() {
val c = Calendar.getInstance()
val year = c.get(Calendar.YEAR)
val month = c.get(Calendar.MONTH)
val day = c.get(Calendar.DAY_OF_MONTH)
val dpd = DatePickerDialog(
app,
DatePickerDialog.OnDateSetListener { _, yearPicked, monthOfYear, dayOfMonth ->
exitDate.value =
"${getFormattedDayOrMonth(dayOfMonth)}/${getFormattedDayOrMonth(monthOfYear + 1)}/$yearPicked"
},
year,
month,
day
)
dpd.show()
}
fun onExitHourClick() {
val c = Calendar.getInstance()
val hour = c.get(Calendar.HOUR_OF_DAY)
val minute = c.get(Calendar.MINUTE)
val tpd = TimePickerDialog(
app,
TimePickerDialog.OnTimeSetListener { _, hourPicked, minutePicked ->
exitHour.value =
"${getFormattedDayOrMonth(hourPicked)}:${getFormattedDayOrMonth(minutePicked)}"
},
hour,
minute,
true
)
tpd.show()
}
init {
setProfileValue()
setDateHourToday()
@ -170,4 +237,13 @@ class AddViewModel(
|| reason9.value!!
}
private fun getFormattedDayOrMonth(date: Int): String {
var formattedDate: String = date.toString()
if (formattedDate.length <= 1) {
formattedDate = "0$formattedDate"
}
return formattedDate
}
}

View File

@ -1,5 +1,6 @@
package fr.sanchezm.attestationsCovid19.ui.add
import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import fr.sanchezm.attestationsCovid19.data.repository.AttestationRepository
@ -9,12 +10,13 @@ import fr.sanchezm.attestationsCovid19.data.repository.ProfileRepository
class AddViewModelFactory(
private val configRepository: ConfigRepository,
private val profileRepository: ProfileRepository,
private val attestationRepository: AttestationRepository
private val attestationRepository: AttestationRepository,
private val app: Context
) :
ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return AddViewModel(configRepository, profileRepository, attestationRepository) as T
return AddViewModel(configRepository, profileRepository, attestationRepository, app) as T
}
}

View File

@ -20,6 +20,7 @@ class InfoFragment : Fragment() {
): View? {
val root = inflater.inflate(R.layout.fragment_info, container, false)
root.explication_4.movementMethod = LinkMovementMethod.getInstance()
root.credits_1.movementMethod = LinkMovementMethod.getInstance()
root.credits_2.movementMethod = LinkMovementMethod.getInstance()
root.credits_3.movementMethod = LinkMovementMethod.getInstance()

View File

@ -16,7 +16,8 @@ object InjectorUtils {
AddViewModelFactory(
getConfigRepo(context),
getProfileRepo(context),
getAttestationRepo(context)
getAttestationRepo(context),
context
)
fun provideAttestationViewModel(context: Context): AttestationsViewModelFactory =

View File

@ -54,7 +54,7 @@ class PdfUtils private constructor(
private fun addPageWithQrCode(document: PDDocument, attestation: Attestation) {
val size = 360
val margin = 40f
val margin = 25f
// Add QRCode on the first page
val page1: PDPage = document.pages.first()
@ -64,8 +64,8 @@ class PdfUtils private constructor(
page1,
attestation,
size / 3,
page1.mediaBox.upperRightX - margin - size / 3 - 30,
99f
page1.mediaBox.upperRightX - margin - size / 3,
25f
)
// Add QRCode on a new page
@ -122,7 +122,7 @@ class PdfUtils private constructor(
private fun setCheckboxFields(acroForm: PDAcroForm, attestation: Attestation) {
attestation.reasons.forEach {
(acroForm.getField(attestation.getMotifText(it)) as PDCheckbox).check()
(acroForm.getField("distinction Motif $it") as PDCheckbox).check()
}
}

View File

@ -1,10 +1,10 @@
<?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_marginBottom="15dp"
android:background="@drawable/custom_rectangle"
android:padding="15dp">
<TextView
@ -13,50 +13,50 @@
android:layout_height="wrap_content"
android:text="@string/people_item"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="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="3dp"
android:text="@string/date_item"
android:textStyle="bold"
app:layout_constraintTop_toBottomOf="@+id/people_item"
app:layout_constraintStart_toStartOf="parent" />
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:text="@string/reason_item"
android:layout_marginTop="3dp"
android:text="@string/reason_item"
android:textStyle="bold"
app:layout_constraintTop_toBottomOf="@+id/date_item"
app:layout_constraintStart_toStartOf="parent" />
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"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/date_item_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
app:layout_constraintTop_toBottomOf="@id/people_item_data"
app:layout_constraintEnd_toEndOf="parent" />
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="3dp"
app:layout_constraintTop_toBottomOf="@id/date_item_data"
app:layout_constraintEnd_toEndOf="parent" />
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/date_item_data" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<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:tools="http://schemas.android.com/tools"
tools:context=".ui.add.AddFragment">
<data>
@ -12,9 +12,9 @@
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraintContext"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/constraintContext">
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
@ -34,10 +34,10 @@
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:text="@string/attestation_title"
android:textColor="?attr/colorPrimary"
android:textSize="25sp" />
android:textSize="25sp"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
@ -88,9 +88,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="date"
android:focusable="false"
android:onClick="@{() -> viewModel.onBirthdayClick()}"
android:text="@={viewModel.birthday}"
app:validateDate='@{"dd/MM/yyyy"}'
app:validateDateMessage="@{@string/date_error_message}"/>
app:validateDateMessage="@{@string/date_error_message}" />
</com.google.android.material.textfield.TextInputLayout>
@ -164,10 +166,12 @@
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:inputType="date"
android:onClick="@{() -> viewModel.onExitDateClick()}"
android:text="@={viewModel.exitDate}"
app:validateDate='@{"dd/MM/yyyy"}'
app:validateDateMessage="@{@string/date_error_message}"/>
app:validateDateMessage="@{@string/date_error_message}" />
</com.google.android.material.textfield.TextInputLayout>
@ -181,7 +185,9 @@
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:inputType="time"
android:onClick="@{() -> viewModel.onExitHourClick()}"
android:text="@={viewModel.exitHour}" />
</com.google.android.material.textfield.TextInputLayout>
@ -273,7 +279,6 @@
android:layout_marginTop="20dp"
android:onClick="@{() -> viewModel.onGenerateAttestationClick()}"
android:text="@string/generate_attestation_button" />
<!-- android:enabled="false"-->
</LinearLayout>

View File

@ -22,7 +22,7 @@
<TextView
android:id="@+id/explication_1"
android:text="@string/explication_1"
android:layout_marginTop="40dp"
android:layout_marginTop="20dp"
style="@style/TextView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
@ -52,14 +52,6 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/explication_3" />
<TextView
android:id="@+id/explication_5"
android:text="@string/explication_5"
style="@style/TextView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/explication_4" />
<TextView
android:layout_marginTop="20dp"
android:id="@+id/credits_title"
@ -69,7 +61,7 @@
style="@style/TextViewCredits"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/explication_5" />
app:layout_constraintTop_toBottomOf="@+id/explication_4" />
<TextView
android:layout_marginTop="10dp"

View File

@ -1,12 +1,12 @@
<resources>
<string name="app_name">Attestation de déplacement dérogatoire (couvre-feu)</string>
<string name="app_name">Attestation de déplacement dérogatoire</string>
<string name="title_add">Nouvelle Attestation</string>
<string name="title_attestations">Mes Attestations</string>
<string name="title_info">À Propos</string>
<!-- Attestation Fragment -->
<string name="attestation_title">@string/app_name</string>
<string name="attestation_subtitle">Respectons les gestes barrières et les distanciations sociales et faisons barrière à la Covid-19</string>
<string name="attestation_subtitle">Restons chez nous pour nous protéger et protéger les autres</string>
<string name="attestation_generated">Attestation générée</string>
<string name="no_attestation">Aucune attestation, veuillez en générer une.</string>
@ -18,28 +18,27 @@
<string name="address">Adresse</string>
<string name="city">Ville</string>
<string name="postal_code">Code Postal</string>
<string name="reason">Je certifie que mon déplacement est lié au motif suivant (cocher la case) autorisé en application des mesures générales nécessaires pour faire face à l\'épidémie de Covid19 dans le cadre de l\'état d\'urgence sanitaire :</string>
<string name="exit_date">Date de sortie</string>
<string name="exit_hour">Heure de sortie</string>
<string name="generate_attestation_button">Générer l\'attestation</string>
<!-- Reasons for leaving house -->
<string name="reason_1">Déplacements entre le domicile et le lieu d\'exercice de l\'activité professionnelle ou les déplacements professionnels ne pouvant être différés.</string>
<string name="reason_2">Déplacements pour effectuer des achats de fournitures nécessaires à l\'activité professionnelle, des achats de première nécessité dans des établissements dont les activités demeurent autorisées (liste sur gouvernement.fr) et les livraisons à domicile.</string>
<string name="reason_3">Consultations et soins ne pouvant être assurés à distance et ne pouvant être différés et lachat de médicaments.</string>
<string name="reason_4">Déplacements pour motif familial impérieux, pour l\'assistance aux personnes vulnérables et précaires ou la garde d\'enfants.</string>
<string name="reason_5">Déplacements des personnes en situation de handicap et de leur accompagnant.</string>
<string name="reason_6">Déplacements brefs, dans la limite d\'une heure quotidienne et dans un rayon maximal d\'un kilomètre autour du domicile, liés soit à l\'activité physique individuelle des personnes, à l\'exclusion de toute pratique sportive collective et de toute proximité avec d\'autres personnes, soit à la promenade avec les seules personnes regroupées dans un même domicile, soit aux besoins des animaux de compagnie.</string>
<string name="reason_7">Convocation judiciaire ou administrative et rendez-vous dans un service public.</string>
<string name="reason_8">Participation à des missions d\'intérêt général sur demande de l\'autorité administrative.</string>
<string name="reason_9">Déplacement pour chercher les enfants à lécoles et à loccasion de leurs activités périscolaires.</string>
<string name="reason">Je certifie que mon déplacement est lié au(x) motif(s) suivant (cocher la case) autorisé en application des mesures générales nécessaires pour faire face à lépidémie de Covid19 dans le cadre de létat durgence sanitaire :</string>
<string name="reason_1">1. Déplacements entre le domicile et le lieu dexercice de lactivité professionnelle ou un établissement denseignement ou de formation ; déplacements professionnels ne pouvant être différés ; déplacements pour un concours ou un examen.</string>
<string name="reason_2">2. Déplacements pour se rendre dans un établissement culturel autorisé ou un lieu de culte ; déplacements pour effectuer des achats de biens, pour des services dont la fourniture est autorisée, pour les retraits de commandes et les livraisons à domicile.</string>
<string name="reason_3">3. Consultations, examens et soins ne pouvant être assurés à distance et lachat de médicaments.</string>
<string name="reason_4">4. Déplacements pour motif familial impérieux, pour lassistance aux personnes vulnérables et précaires ou la garde denfants.</string>
<string name="reason_5">5. Déplacements des personnes en situation de handicap et leur accompagnant.</string>
<string name="reason_6">6. Déplacements en plein air ou vers un lieu de plein air, sans changement du lieu de résidence, dans la limite de trois heures quotidiennes et dans un rayon maximal de vingt kilomètres autour du domicile, liés soit à lactivité physique ou aux loisirs individuels, à lexclusion de toute pratique sportive collective et de toute proximité avec dautres personnes, soit à la promenade avec les seules personnes regroupées dans un même domicile, soit aux besoins des animaux de compagnie.</string>
<string name="reason_7">7. Convocations judiciaires ou administratives et déplacements pour se rendre dans un service public.</string>
<string name="reason_8">8. Participation à des missions dintérêt général sur demande de lautorité administrative.</string>
<string name="reason_9">9. Déplacement pour chercher les enfants à lécole et à loccasion de leurs activités périscolaires.</string>
<!-- Info Fragment -->
<string name="explication_1">- Merci de n\'utiliser l\'application qu\'en cas de nécessité.</string>
<string name="explication_2">- Cette application n\'aura jamais de publicité.</string>
<string name="explication_3">- Toutes les données sont stockées uniquement sur votre téléphone, utilisable hors ligne.</string>
<string name="explication_4">- Application non gouvernementale ni officielle, développée par un étudiant.</string>
<string name="explication_5">- J\'ai, moi-même été touché par ce virus, même à 22 ans il m\'a mis KO 2\-3 jours, faites attention à vous et à vos proches.</string>
<string name="explication_1">- Cette application n\'aura jamais de publicité.</string>
<string name="explication_2">- Toutes les données sont stockées uniquement sur votre téléphone, utilisable hors ligne.</string>
<string name="explication_3">- Application non gouvernementale ni officielle, développée par un jeune diplômé.</string>
<string name="explication_4">- Si vous souhaitez m\'offrir une bière ou un café, c\'est <a href="https://buymeacoff.ee/sanchezm/">ici</a> ou vous pouvez me suivre sur <a href="https://www.twitch.tv/mathdieu">twitch</a>.</string>
<string name="credits_title">Petit remerciement pour l\'aide apportée au développement de l\'application :</string>
<string name="credits_1">TomRoush: <a href="https://github.com/TomRoush/PdfBox-Android">PdfBox-Android</a></string>
<string name="credits_2">Barteksc: <a href="https://github.com/barteksc/AndroidPdfViewer">AndroidPdfViewer</a></string>

View File

@ -37,9 +37,9 @@
<style name="TextView" parent="Widget.MaterialComponents.TextView">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginTop">@dimen/margin15dp</item>
<item name="android:layout_marginTop">@dimen/margin5dp</item>
<item name="android:textColor">@color/colorPrimary</item>
<item name="android:textSize">@dimen/textSize18sp</item>
<item name="android:textSize">@dimen/textSize14sp</item>
</style>
<style name="TextViewCredits" parent="TextView">