Skip to content
Snippets Groups Projects
Commit 88cd16a6 authored by Jean-Marie Place's avatar Jean-Marie Place
Browse files

initial

parents
Branches
Tags
No related merge requests found
Showing
with 1225 additions and 0 deletions
README.md 0 → 100644
# Contrôle TP 2025
Ce contrôle TP est d'une durée de 2h. L’usage des documents papier et des précédents TP sont
autorisés. **Attention: toute modification de votre dépôt strictement postérieure à
l'heure limite de la remise sera ignorée**. Vous devez bien entendu soumettre un travail
original, dont vous êtes l'unique auteur.
# Déroulement du contrôle
## Mise en place
Commencez par `fork` ce dépôt. **Assurez-vous qu'il est privé** (sinon, sur la page de votre fork sur gitlab, allez dans `Settings` --> `General` --> `Visibility, project features, permissions` --> `expand` --> `Project visibility` --> sélectionner `Private`).
### Invitez immédiatement votre enseignant comme membre **DEVELOPPEUR** sur votre dépôt gitlab (que vous venez juste de fork)
Ce dépôt contient:
- ce sujet de c# Déroulement du contrôle
## Mise en place
Commencez par `fork` ce dépôt. **Assurez-vous qu'il est privé** (sinon, sur la page de votre fork sur gitlab, allez dans `Settings` --> `General` --> `Visibility, project features, ontrôle TP
- l'ensemble des sources et ressources dont vous aurez besoin pour ce contrôle.
Un jeu d'essai est préchargé à partir du fichier json (présent comme une ressource 'raw' de nom cand.json).
La commande
```{shell}
/usr/local/virtual_machine/infoetu/android/<login>/Sdk/platform-tools/adb install ./demo.apk
```
exécutée depuis le volet 'Terminal' installe une version complète du programme sur votre android virtuel (pensez évidemment à remplacer <login> par votre login).
## Restitution
**Vous enregistrerez l'état de votre projet après chaque question (votre projet doit compiler et être fonctionnel).**
```{shell}
git commit -am 'Question X'
git tag Qx
git push --tag
```
## Rappels git
La commande:
```{shell}
git config --global credential.helper cache
```
vous évitera d'avoir à ressaisir vos identifiants/mot de passe à chaque fois.
Les commandes suivantes vous permettront de compléter une question déjà validée (par exemple la Qx):
```{shell}
git tag -d Qx
git commit -am 'Question X (complément)'
git tag Qx
git push --tag # peut être éventuellement différé en fin de tp
```
# Description du projet
On va reprendre ici le thème des comptes de vacances.
Un certain nombre d'amis (appelés participants) font une sortie. L'application que l'on se propose de réaliser aura pour objet de gérer la comptabilité du groupe:
* Enregistrer chaque dépense (appelée opéartion) faite pour le compte du groupe par un participant.
* Afficher au fur et à mesure le montant total des dépenses par poste (il y a 5 postes: TRANSPORT, HEBERGEMENT, RESTAURATION, LOISIRS, AUTRES)
* Au final afficher pour chaque participant le montant qu'il doit payer (ou recevoir) pour que chacun ait participé équitablement.
L'exemple du jeu d'essai fourni est le suivant:
liste des participants
| Participants |
|:-------------|
| Ahmed |
| Bruno |
| Celine |
| Déborah |
liste des opérations
| Participant | Poste | Montant |
|:------------|:-------------|--------:|
| Ahmed | TRANSPORT | 15.00 € |
| Bruno | HEBERGEMENT | 65.00 € |
| Celine | TRANSPORT | 43.50 € |
| Ahmed | AUTRES | 12.00 € |
| Déborah | RESTAURATION | 14.50 € |
Les calculs sont effectués par le modèle déjà fourni.
Le cumul par poste donne:
| Poste | Cumul |
|:-------------|-------------:|
| TRANSPORT | 58.50 € |
| HEBERGEMENT | 65.00 € |
| RESTAURATION | 14.50 € |
| LOISIRS | 0.00 € |
| AUTRES | 12.00 € |
| **TOTAL** | **150.00 €** |
Et le solde par participant: (calculé comme 150/4 - ce qui a été payé par le participant)
| Participant | Solde | Détail du calcul |
|:-------------|---------:|:-----------------|
| Ahmed | 10.50 € | 37.50 - 27.00 |
| Bruno | -27.50 € | 37.50 + 27.50 |
| Celine | -6.00 € | 37.50 - 43.50 |
| Déborah | 23.00 € | 37.50 - 14.50 |
## Interface de l'application
L'interface de l'application se présente sous la forme de deux activités:
### Activité principale
<img src="img/Screenshot_1.png" alt="image" style="width:200px"/>
* La partie supérieure de l'activité principale présente le titre de l'application ainsi que les cumul des opérations par poste.
* La partie inférieure affiche la liste des participants. Pour chaque participant, on affiche le solde dû.
Cette partie de l'interface permet en outre
* d'ajouter un nouveau participant (en fournissant son pseudo)
* de supprimer un participant.
* Un bouton situé en bas de cette activité permet d'ouvrir la liste des opérations
### Écran des statistiques
<img src="img/Screenshot_2.png" alt="image" style="width:200px"/>
La seconde activité présente la liste des opérations et permet d'afficher
* les statistiques générales (nombre d'opérations et cumul des montants)
* les statistiques pour un poste (nombre d'opérations et cumul des montants)
## Description du modèle
Le modèle complet de données vous est fourni (vous n'aurez donc à priori à modifier aucune des classes `ModelApplication`, `Operation` ou `Participant`.
Ce modèle est intégré dans la version initiale (et partielle) de l'application android.
Vous pouvez utiliser les méthodes suivantes du modèle (la classe `ModelApplication`).
* `addParticipant(String)`: ajouter un participant.
* `getParticipant(int)`: accès à un participant par sa position.
* `removeParticipant(String)`: supprimer un participant.
* `getNbParticipants`: lire le nombre de participants.
* `addOperation(String, String, double)`: ajouter une opération.
* `getOperation(int)`: accès à une opération par son numéro.
* `removeOperation(int)`: supprimer une opération.
* `getNbOpérations`: lire le nombre d'opérations.
* `getSum(String)`: lire le cumul pour un poste (désigné par son nom).
* `computeBalance()`: calcule les soldes et totaux.
* `getNbOperations()`: affiche le nombre d'opérations totales.
* `getNbOperations(string)`: affiche le nombre d'opérations pour un poste.
* `getSumOperation()`: affiche le cumul des montants.
* `getSumOperations(string)`: affiche le cumul des opérations pour un poste.
## Tâches à réaliser
L'application qui vous est fournie avec ce projet est incomplète.
Dans le cadre de ce DS, vous devez réaliser les tickets suivants:
1. Afficher le cumul des dépenses par poste.
2. Completer les classes internes `ParticipantAdapter` et `ParticipantViewHolder` pour afficher les noms des participant dans `R.id.text1`.
3. Afficher le solde par participant en dessous de chaque nom.
Utiliser le champ `TextView` dont l'id est `R.id.text2`
et la méthode `getBalance(String participant)` de l'application.
4. Ajouter un participant. L'`EditText` `R.id.new_participant` permet la saisie du pseudo.
Le bouton `R.id.add_participant` effectuera l'ajout.
5. Si l'ajout de participant est impossible (si un participant de même pseudo existe déjà) une exception est levée.
Compléter la question précédente pour que ces exceptions soient récupérées et affichent une annonce (`Toast`) affichant le message lié à l'exception.
6. Ajouter un menu contextuel sur la liste des participants qui comporte un seul item qui permet de supprimer un participant.
7. Créer une seconde activité qui sera lancée par n'importe lequel des boutons libellé `STATS...`
Cette activité se présentera sous la forme affichée ci-dessus (faire le layout complet) mais seul les statistiques générales seront présentées.
8. Faire en sorte que l'appui sur le bouton `Fermer` fasse revenir sur l'activité initiale.
9. Complétez le projet de sorte que les statistiques propres à un poste soient affichées sur la partie inférieure (selon le bouton `STATS...` choisi).
/build
\ No newline at end of file
plugins {
alias(libs.plugins.android.application)
}
android {
namespace = "fr.iutlille.ctp_2025"
compileSdk = 35
defaultConfig {
applicationId = "fr.iutlille.ctp_2025"
minSdk = 27
targetSdk = 35
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildFeatures {
viewBinding=true
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
dependencies {
implementation(libs.appcompat)
implementation(libs.material)
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
}
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
\ No newline at end of file
package fr.iutlille.ctp_2025;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("fr.iutlille.ctp_2025", appContext.getPackageName());
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:name=".ModelApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.TP1"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
\ No newline at end of file
package fr.iutlille.ctp_2025;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import fr.iutlille.ctp_2025.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
public class ParticipantAdapter extends RecyclerView.Adapter<ParticipantViewHolder> {
@NonNull
@Override
public ParticipantViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(android.R.layout.simple_list_item_2, parent, false);
return new ParticipantViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull ParticipantViewHolder holder, int position) {
Participant participant = model.getParticipant(position);
// TODO Q2
}
@Override
public int getItemCount() {
return 0; // TODO Q2a
}
}
public class ParticipantViewHolder extends RecyclerView.ViewHolder {
TextView text1;
TextView text2;
public ParticipantViewHolder(View itemView) {
super(itemView);
}
}
private ActivityMainBinding ui;
private ModelApplication model;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ui = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(ui.getRoot());
model = (ModelApplication) getApplication();
ui.ParticipantList.setHasFixedSize(true);
ui.ParticipantList.setAdapter(new ParticipantAdapter());
RecyclerView.LayoutManager lm = new LinearLayoutManager(this, RecyclerView.VERTICAL, false);
ui.ParticipantList.setLayoutManager(lm);
ui.ParticipantList.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
refresh();
}
private void refresh() {
model.computeBalance();
// TODO Q1
ui.ParticipantList.getAdapter().notifyDataSetChanged();
}
}
\ No newline at end of file
package fr.iutlille.ctp_2025;
import android.app.Application;
import android.util.Log;
import androidx.annotation.NonNull;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.TreeMap;
import java.util.Map;
import java.util.Objects;
public class ModelApplication extends Application {
public static class ApplicationErrorException extends Exception {
public ApplicationErrorException(String message) {
super(message);
}
}
private Map<String, Double> postes;
private Map<String, Participant> participants;
private ArrayList<Operation> operations;
private ArrayList<String> participantsList;
@Override
public void onCreate() {
super.onCreate();
try {
initModel();
} catch (JSONException e) {
Log.e("CTP", "Erreur de lecture du fichier d'initialisation...");
// e.printStackTrace();
}
}
/**
* GESTION DES ERREURS
*/
private void error(String message) throws ApplicationErrorException {
Log.e ("CTP", message);
throw new ApplicationErrorException(message);
}
/**
* GESTION DES PARTICIPANTS
*/
/**
* Ajoute une opération
* @param participant Le nom du participant qui fait la dépense
* @param poste Le poste affecté
* @param amount Le montant en euros
* @throws ApplicationErrorException Levée si une erreur est détectée:
* <ul>
* <li>Participant inexistant</li>
* <li>Poste inexistant</li>
* </ul>
*/
/**
* Ajoute un participant au modèle.
* Le participant est défini par son nom
* @param name Le nom du participant ajouté
* @throws ApplicationErrorException Détecte la tentative d'ajout alors qu'unparticipant de même nom existe déjà
*/
public void addParticipant(String name) throws ApplicationErrorException {
if (participants.containsKey(name)) {
error(this.getString(R.string.ErrorInsertParticipantExists, name));
} else {
participants.put(name, new Participant(name));
participantsList = new ArrayList<>(participants.keySet());
}
}
/**
* Obtient l'objet Participant à partir de son rang dans la liste (alphabétique) des paticipants..
* @param position le rang demandé
* @return L'objet Participant demandé
*/
@NonNull
public Participant getParticipant(int position) {
return Objects.requireNonNull(participants.get(participantsList.get(position)));
}
/**
* Donne le nombre de participants
* @return Le nombre de participants
*/
public int getNbParticipants() {
return participantsList.size();
}
/**
* Supprime un participant désigné par son nom.
* @param name Le nom du participant à supprimer
* @throws ApplicationErrorException. Erreurs détectées:
* <ul>
* <li>Le participant n'existe pas</li>
* <li>Le participant a un ou des paiements à son crédit</li>
* </ul>
*/
public void removeParticipant(String name) throws ApplicationErrorException {
if (! participants.containsKey(name)) {
error(this.getString(R.string.ErrorDropUnknownParticipant, name));
} else if (operationExists(name)) {
error(this.getString(R.string.ErrorDropParticipantLinked));
} else {
participants.remove(name);
participantsList.remove(name);
}
}
/**
* ajoute une opération.
* @param participant le nom du participant qui a réglé la dépense.
* @param poste le poste de la dépense
* @param amount le montant de la dépense
* @throws ApplicationErrorException. Erreurs détectées:
* <ul>
* <li>Le participant n'existe pas</li>
* <li>Le poste n'existe pas</li>
* </ul>
*/
public void addOperation(String participant, String poste, double amount) throws ApplicationErrorException {
if (! participants.containsKey(participant)) {
error(this.getString(R.string.ErrorInsertUnknownParticipant, participant));
} else if (! postes.containsKey(poste)) {
error(this.getString(R.string.ErrorInsertUnknownPoste, poste));
} else{
Operation operation = new Operation(participant, poste, amount);
operations.add(operation);
}
}
/**
* Retourne une opération définie par sa position.
* @param position la position de l'opération demandée (entre 0 et le nombre d'opérations-1)
* @return l'opération recherchée.
* @throws ApplicationErrorException Erreurs détectées:
* <ul>
* <li>La position fournie est négative ou &ge; nombre d'opérations-1</li>
* </ul>
*/
@NonNull
public Operation getOperation(int position) throws ApplicationErrorException {
if ((position < 0) || (position >= operations.size())) {
error(this.getString(R.string.ErrorOperationIllegalPosition, position));
}
return operations.get(position);
}
/**
* Efface une opération définie par sa position.
* @param position la position de l'opération à supprimer (entre 0 et le nombre d'opérations-1)
* @throws ApplicationErrorException Erreurs détectées:
* <ul>
* <li>La position fournie est négative ou &ge; nombre d'opérations-1</li>
* </ul>
*/
void removeOperation(int position) throws ApplicationErrorException {
if ((position < 0) || (position >= operations.size())) {
error(this.getString(R.string.ErrorOperationIllegalPosition, position));
} else {
operations.remove(position);
}
}
/**
* Calcule et retourne le nombre d'opérations
* @return le nombre d'opérations enregistrées.
*/
public int getNbOperations() {
return operations.size();
}
public double getSumOperations() {
double sum = 0.00;
for (Operation operation: operations) {
sum += operation.getAmount();
}
return sum;
}
public int getNbOperations(String poste) {
int nbOp = 0;
for (Operation operation: operations) {
if (operation.getPoste().equals(poste)) {
nbOp++;
}
}
return nbOp;
}
public double getSumOperations(String poste) {
double sum = 0.00;
for (Operation operation: operations) {
if (operation.getPoste().equals(poste)) {
sum += operation.getAmount();
}
}
return sum;
}
/**
* GESTION DES POSTES
* La liste des postes est définie dans les ressources:
* <ul>
* <li><code>R.string.TRANSPORT</code></li>
* <li><code>R.string.HEBERGEMENT</code></li>
* <li><code>R.string.RESTAURATION</code></li>
* <li><code>R.string.LOISIRS</code></li>
* <li><code>R.string.AUTRES</code></li>
* </ul>
* et dans <code>R.array.postes</code>
*/
/**
* Lire le cumul des dépenses pour un poste donné par son nom.
* @param poste
* @return
*/
public double getSum(String poste) {
return Objects.requireNonNullElse(this.postes.get(poste), 0.0);
}
/**
* GESTION DES INITIALISATIONS
*/
private void initPostes() {
String[] values = this.getResources().getStringArray(R.array.postes);
this.postes =new TreeMap<>();
for (String poste: values) {
this.postes.put(poste, 0.0);
}
}
private StringBuilder getConfig() {
StringBuilder text = new StringBuilder();
BufferedReader br = new BufferedReader(new InputStreamReader(this.getResources().openRawResource(R.raw.initial)));
try {
String line;
while ((line = br.readLine()) != null) {
text.append(line);
text.append('\n');
}
br.close();
return text;
} catch (IOException e) {;
e.printStackTrace();
}
return text;
}
private void initParticipants(JSONArray jsonParticipants) throws JSONException, ApplicationErrorException {
participants = new TreeMap<>();
for (int i = 0; i < jsonParticipants.length(); i++) {
String name = jsonParticipants.getString(i);
this.addParticipant(name);
}
participantsList = new ArrayList<>(participants.keySet());
}
private void initOperations(JSONArray jsonOperations) throws JSONException, ApplicationErrorException {
operations = new ArrayList<>();
for (int i = 0; i < jsonOperations.length(); i++) {
JSONObject jsonOperation = jsonOperations.getJSONObject(i);
String participant = jsonOperation.getString("participant");
String poste = jsonOperation.getString("poste");
double amount = jsonOperation.getDouble("amount");
addOperation(participant, poste, amount);
}
}
private void initModel() throws JSONException {
initPostes();
StringBuilder text = getConfig() ;
JSONObject json = new JSONObject(text.toString());
try {
initParticipants(json.getJSONArray("participants"));
initOperations(json.getJSONArray("operations"));
} catch (ApplicationErrorException e) {
throw new RuntimeException(e);
}
}
private boolean operationExists(String name) {
for (Operation operation: operations) {
if (operation.getParticipant().equals(name)) return true;
}
return false;
}
/**
* Effectue tout les calculs à partir des participants et opérations enregistrées.
* Met à jour les objets Participant
*/
public void computeBalance() {
Participant.setNbParts(participants.size());
for (Participant participant: participants.values()) {
participant.clearBalance();
}
for (String poste: this.postes.keySet()) {
postes.put(poste, 0.0);
}
for (Operation operation: operations) {
Participant participant = participants.get(operation.getParticipant());
String poste = operation.getPoste();
double amount = Objects.requireNonNullElse(operation.getAmount(), 0.00);
participant.addExpense(amount);
postes.put(poste, amount + postes.get(poste));
}
}
}
package fr.iutlille.ctp_2025;
import org.json.JSONException;
import org.json.JSONObject;
public class Operation {
private double amount;
private String participant;
private String poste;
public Operation(String participant, String poste, double amount) {
this.amount = amount;
this.poste = poste;
this.participant = participant;
}
public double getAmount() {
return amount;
}
public String getParticipant() {
return participant;
}
public String getPoste() {
return poste;
}
}
package fr.iutlille.ctp_2025;
import android.provider.Telephony;
public class Participant {
private static int nbParts;
private final String name;
private double payed;
private static double totalAmount;
private double balance = 0;
public Participant(String name) {
this.name = name;
this.payed = 0;
}
public static void setNbParts(int nbParts) {
Participant.nbParts = nbParts;
Participant.totalAmount = 0;
}
public void clearBalance() {
this.balance = 0;
this.payed = 0;
}
public void addExpense(double amount) {
this.payed += amount;
Participant.totalAmount += amount;
}
public double getBalance() {
return (Participant.totalAmount / nbParts) - this.payed;
}
public String getName() {
return name;
}
}
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>
<?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:id="@+id/particip"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/Titre"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="64dp"
android:text="@string/ActivityTitle"
android:textSize="34sp"
app:layout_constraintBottom_toTopOf="@+id/tableLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TableLayout
android:id="@+id/tableLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="64dp"
app:layout_constraintBottom_toTopOf="@+id/divider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.74"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/Titre">
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="30dp">
<TextView
android:id="@+id/lblTransport"
android:layout_width="142dp"
android:layout_height="wrap_content"
android:text="@string/LibTransport" />
<TextView
android:id="@+id/SumTransport"
android:layout_width="90dp"
android:layout_height="wrap_content"
android:gravity="end"
android:paddingEnd="10dp"
android:text="@string/zeroEuro" />
<Button
android:id="@+id/stats_transport"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="@string/Lib_stats" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="30dp">
<TextView
android:id="@+id/lblHebergement"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="@string/LibHebergement" />
<TextView
android:id="@+id/SumHebergement"
android:layout_width="90dp"
android:layout_height="wrap_content"
android:gravity="end"
android:paddingEnd="10dp"
android:text="@string/zeroEuro" />
<Button
android:id="@+id/stats_hebergement"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="@string/Lib_stats" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="30dp">
<TextView
android:id="@+id/lblRestauration"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="@string/LibRestauration" />
<TextView
android:id="@+id/SumRestauration"
android:layout_width="90dp"
android:layout_height="wrap_content"
android:gravity="end"
android:paddingEnd="10dp"
android:text="@string/zeroEuro" />
<Button
android:id="@+id/bstats_restauration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="@string/Lib_stats" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="30dp">
<TextView
android:id="@+id/lblLoisirs"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:paddingEnd="20dp"
android:text="@string/LibLoisirs" />
<TextView
android:id="@+id/SumLoisirs"
android:layout_width="90dp"
android:layout_height="wrap_content"
android:gravity="end"
android:paddingEnd="10dp"
android:text="@string/zeroEuro" />
<Button
android:id="@+id/stats_loisirs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="@string/Lib_stats" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="30dp">
<TextView
android:id="@+id/lblAutres"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="@string/LibAutres" />
<TextView
android:id="@+id/SumAutres"
android:layout_width="90dp"
android:layout_height="wrap_content"
android:gravity="end"
android:paddingEnd="10dp"
android:text="@string/zeroEuro" />
<Button
android:id="@+id/stats_autres"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="@string/Lib_stats" />
</TableRow>
</TableLayout>
<View
android:id="@+id/divider"
android:layout_width="409dp"
android:layout_height="1dp"
android:layout_marginStart="1dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="1dp"
android:background="?android:attr/listDivider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tableLayout" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/ParticipantList"
android:layout_width="300dp"
android:layout_height="0dp"
android:layout_marginStart="32dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="32dp"
app:layout_constraintBottom_toTopOf="@+id/titre_new_participant"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/divider" />
<TextView
android:id="@+id/titre_new_participant"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:text="@string/txtAddParticipant"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@+id/new_participant"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent" />
<EditText
android:id="@+id/new_participant"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="64dp"
android:ems="10"
android:hint="@string/new_participant"
android:inputType="text|textCapWords"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/add_participant"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/add_participant"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:text="@string/txtAdd"
app:layout_constraintBaseline_toBaselineOf="@+id/new_participant"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
app/src/main/res/mipmap-hdpi/ic_launcher.webp

1.37 KiB

app/src/main/res/mipmap-hdpi/ic_launcher_round.webp

2.83 KiB

app/src/main/res/mipmap-mdpi/ic_launcher.webp

982 B

app/src/main/res/mipmap-mdpi/ic_launcher_round.webp

1.73 KiB

0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment