Version avec integration slave master

This commit is contained in:
OnlyPapy98
2026-04-01 19:51:19 +02:00
parent acc5ec1b70
commit 4eaca7e1d8
66 changed files with 3229 additions and 218 deletions

View File

@@ -43,7 +43,13 @@ android {
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
implementation(libs.room.runtime)
implementation("com.github.NaikSoftware:StompProtocolAndroid:1.6.6")
// RxJava (déjà présent normalement)
implementation("io.reactivex.rxjava2:rxjava:2.2.21")
implementation("io.reactivex.rxjava2:rxandroid:2.1.1")
annotationProcessor(libs.room.compiler)
implementation(libs.stompprotocolandroid)
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.camera:camera-camera2:1.2.3")
implementation("androidx.camera:camera-lifecycle:1.2.3")

View File

@@ -14,6 +14,8 @@
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<!-- ✅ Location Permissions -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
@@ -50,10 +52,6 @@
android:theme="@style/Theme.Quiz"
tools:targetApi="31"
android:usesCleartextTraffic="true">
<activity
android:name=".PageQuiz"
android:exported="true"
android:theme="@style/Theme.Quiz" />
<!--
TODO: Before you run your application, you need a Google Maps API key.
@@ -71,7 +69,7 @@
<activity
android:name=".MainActivity"
android:name=".PageQuiz"
android:exported="true"
android:theme="@style/Theme.Quiz">
<intent-filter>

View File

@@ -0,0 +1,331 @@
package com.example.quiz;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.quiz.data.adapter.MultiTypeOfBetsAdapter;
import com.example.quiz.data.model.Course;
import com.example.quiz.data.model.Restriction;
import com.example.quiz.data.model.TypeOfBet;
import com.example.quiz.data.model.dtos.auth.User;
import com.example.quiz.databinding.FragmentAgentDetailsBinding;
import com.example.quiz.utils.LoaderDialog;
import com.example.quiz.utils.MessageDialog;
import com.example.quiz.utils.Result;
import com.example.quiz.utils.SharedPrefsHelper;
import com.example.quiz.viewModel.AgentViewModel;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import dagger.hilt.android.AndroidEntryPoint;
/**
* A simple {@link Fragment} subclass.
* Use the {@link AgentDetails#newInstance} factory method to
* create an instance of this fragment.
*/
@AndroidEntryPoint
public class AgentDetails extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private String agentId;
private FragmentAgentDetailsBinding binding;
MultiTypeOfBetsAdapter multiTypeOfBetsAdapter;
private AgentViewModel agentViewModel;
private boolean userInteraction = false;
private User agent;
private Restriction allowedBetTypes;
private SharedPrefsHelper prefsHelper;
private LoaderDialog loaderDialog;
public AgentDetails() {
// Required empty public constructor
}
private static final String AGENT_ID = "agentId";
// TODO: Rename and change types and number of parameters
public static AgentDetails newInstance(User agent) {
AgentDetails fragment = new AgentDetails();
Bundle args = new Bundle();
args.putString(AGENT_ID, String.valueOf(agent.getId()));
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
agentId = getArguments().getString(AGENT_ID);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
binding = FragmentAgentDetailsBinding.inflate(inflater, container, false);
agentViewModel = new ViewModelProvider(this).get(AgentViewModel.class);
allowedBetTypes = new Restriction();
prefsHelper = SharedPrefsHelper.getInstance(getContext());
loaderDialog = new LoaderDialog(getContext());
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
loadAgentDetails(agentId);
binding.switchAccess.setOnCheckedChangeListener((compoundButton, b) -> {
if(!userInteraction){
return;
}
String message = b ? "Voulez-vous activer l'accès à l'agent ?" : "Voulez-vous désactiver l'accès à l'agent ?";
new AlertDialog.Builder(getContext())
.setTitle("Activation de l'accès")
.setMessage(message)
.setPositiveButton("Oui", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
if(prefsHelper.get("id")== null){
MessageDialog.showError(getContext(), "Veuillez vous connecter");
return;
}
String masterId = prefsHelper.get("id");
agentViewModel.setAccess(masterId, agentId, !b).observe(getViewLifecycleOwner(), new Observer<Result<Void>>() {
@Override
public void onChanged(Result<Void> voidResult) {
switch (voidResult.status){
case LOADING:{
loaderDialog.show("Chargement des agents");
break;
}
case ERROR:{
loaderDialog.dismiss();
MessageDialog.showError(getContext(), voidResult.message);
break;
}
case SUCCESS:{
loaderDialog.dismiss();
MessageDialog.showSuccess(getContext(), "L'accès a été mis à jour");
loadAgentDetails(agentId);
break;
}
}
}
});
}
}).setNegativeButton("Non", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
}).show();
});
binding.btnValidate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(prefsHelper.get("id")== null){
MessageDialog.showError(getContext(), "Veuillez vous connecter");
return;
}
if(allowedBetTypes.getAllowedBetTypes() == null || allowedBetTypes.getAllowedBetTypes().isEmpty()){
MessageDialog.showError(getContext(), "Veuillez sélectionner au moins un type de paris");
return;
}
if(agent == null){
MessageDialog.showError(getContext(), "Veuillez charger les détails de l'agent");
return;
}
String masterId = prefsHelper.get("id");
new AlertDialog.Builder(getContext())
.setTitle("Valider la sélection")
.setMessage("Êtes-vous sûr de vouloir valider la sélection ?")
.setPositiveButton("Oui", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
agentViewModel.setRestrictions(masterId, agentId, allowedBetTypes).observe(getViewLifecycleOwner(), new Observer<Result<Void>>() {
@Override
public void onChanged(Result<Void> voidResult) {
switch (voidResult.status){
case LOADING:{
loaderDialog.show("Chargement des agents");
break;
}
case ERROR:{
loaderDialog.dismiss();
MessageDialog.showError(getContext(), voidResult.message);
break;
}
case SUCCESS:{
loaderDialog.dismiss();
MessageDialog.showSuccess(getContext(), "Les types de paris ont été mises à jour");
loadAgentDetails(agentId);
break;
}
default:{
}
}
}
});
}
})
.setNegativeButton("Non", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
})
.show();
}
});
}
private void loadAgentDetails(String slaveId){
agentViewModel.getAgentById(slaveId).observe(getViewLifecycleOwner(), new Observer<Result<User>>() {
@Override
public void onChanged(Result<User> userResult) {
switch (userResult.status){
case LOADING:{
loaderDialog.show("Chargement des agents");
break;
}
case ERROR:{
loaderDialog.dismiss();
MessageDialog.showError(getContext(), userResult.message);
break;
}
case SUCCESS:{
loaderDialog.dismiss();
agent = userResult.data;
binding.txtCode.setText(agent.getCode());
String adresse;
if(agent.getVille() != null && agent.getAdresse() != null){
adresse = agent.getVille()+"; "+agent.getAdresse();
}else{
adresse = "Pas d'adresse";
}
binding.txtAdresse.setText(adresse);
userInteraction = false;
binding.switchAccess.setChecked(agent.getStatut().equals("ACTIF"));
userInteraction = true;
loadAvailableBets(String.valueOf(agent.getId()));
break;
}
default:{
break;
}
}
}
});
}
private void loadAvailableBets(String id){
agentViewModel.getAvailableBets(id).observe(getViewLifecycleOwner(), new Observer<Result<List<Course.TypeParis>>>() {
@Override
public void onChanged(Result<List<Course.TypeParis>> listResult) {
switch (listResult.status){
case LOADING:{
loaderDialog.show("Chargement des paris disponibles");
break;
}
case ERROR:{
loaderDialog.dismiss();
MessageDialog.showError(getContext(), listResult.message);
}
case SUCCESS:{
loaderDialog.dismiss();
List<TypeOfBet> types = createTypeOfBetList();
multiTypeOfBetsAdapter = new MultiTypeOfBetsAdapter(types);
multiTypeOfBetsAdapter.preSelectAvailableBets(listResult.data);
binding.betsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
binding.betsRecyclerView.setAdapter(multiTypeOfBetsAdapter);
List<Course.TypeParis> selectedTypes = multiTypeOfBetsAdapter.getSelectedItems().stream().map(TypeOfBet::getName).collect(Collectors.toList());
allowedBetTypes.setAllowedBetTypes(selectedTypes);
multiTypeOfBetsAdapter.setOnItemClickListener(new MultiTypeOfBetsAdapter.onItemClickListener() {
@Override
public void onItemClick(TypeOfBet type, boolean isChecked) {
List<Course.TypeParis> selectedTypes = multiTypeOfBetsAdapter.getSelectedItems().stream().map(TypeOfBet::getName).collect(Collectors.toList());
allowedBetTypes.setAllowedBetTypes(selectedTypes);
}
@Override
public void onItemsSelected(List<TypeOfBet> selectedItems) {
}
});
}
default:{
break;
}
}
}
});
}
private List<TypeOfBet> createTypeOfBetList() {
List<TypeOfBet> types = new ArrayList<>();
// 1. COUPLE GAGNANT
TypeOfBet coupleGagnant = new TypeOfBet(
"Couple Gagnant", // label
Course.TypeParis.COUPLE_GAGNANT, // name
2 // numberOfHorse (2 chevaux)
);
types.add(coupleGagnant);
// 2. COUPLE PLACE
TypeOfBet couplePlace = new TypeOfBet(
"Couple Place", // label
Course.TypeParis.COUPLE_PLACE, // name
2 // numberOfHorse (2 chevaux)
);
types.add(couplePlace);
// 3. TIERCE
TypeOfBet tierce = new TypeOfBet(
"Tiercé", // label
Course.TypeParis.TIERCE, // name
3 // numberOfHorse (3 chevaux)
);
types.add(tierce);
// 4. QUARTE
TypeOfBet quarte = new TypeOfBet(
"Quarté", // label
Course.TypeParis.QUARTE, // name
4 // numberOfHorse (4 chevaux)
);
types.add(quarte);
// 5. QUINTE (optionnel)
TypeOfBet quinte = new TypeOfBet(
"Quinté", // label
Course.TypeParis.QUINTE, // name
5 // numberOfHorse (5 chevaux)
);
types.add(quinte);
return types;
}
}

View File

@@ -0,0 +1,135 @@
package com.example.quiz;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.quiz.data.adapter.AgentItemAdapter;
import com.example.quiz.data.model.dtos.auth.User;
import com.example.quiz.databinding.FragmentAgentManagementBinding;
import com.example.quiz.utils.AuthNavigator;
import com.example.quiz.utils.LoaderDialog;
import com.example.quiz.utils.MessageDialog;
import com.example.quiz.utils.Result;
import com.example.quiz.utils.SessionManager;
import com.example.quiz.utils.SharedPrefsHelper;
import com.example.quiz.viewModel.AgentViewModel;
import com.example.quiz.viewModel.LoginViewModel;
import com.example.quiz.viewModel.LogsViewModel;
import com.google.android.material.appbar.MaterialToolbar;
import java.util.List;
import dagger.hilt.android.AndroidEntryPoint;
/**
* A simple {@link Fragment} subclass.
* Use the {@link AgentManagement#newInstance} factory method to
* create an instance of this fragment.
*/
@AndroidEntryPoint
public class AgentManagement extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private FragmentAgentManagementBinding binding;
private LoaderDialog loaderDialog;
private AgentViewModel agentViewModel;
SharedPrefsHelper prefsHelper;
AgentItemAdapter agentItemAdapter;
AuthNavigator authNavigator;
public AgentManagement() {
// Required empty public constructor
}
// TODO: Rename and change types and number of parameters
public static AgentManagement newInstance() {
AgentManagement fragment = new AgentManagement();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
authNavigator = new AuthNavigator(getContext(), getParentFragmentManager(), SessionManager.newInstance(getContext()),new ViewModelProvider(this).get(LoginViewModel.class),new ViewModelProvider(this).get(LogsViewModel.class), this);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
binding = FragmentAgentManagementBinding.inflate(inflater, container, false);
agentViewModel = new ViewModelProvider(this).get(AgentViewModel.class);
loaderDialog = new LoaderDialog(getContext());
prefsHelper = SharedPrefsHelper.getInstance(getContext());
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
AppCompatActivity activity = (AppCompatActivity) getActivity();
if(activity != null){
MaterialToolbar toolbar = activity.findViewById(R.id.toolbar);
activity.setSupportActionBar(toolbar);
if (activity.getSupportActionBar() != null) {
activity.getSupportActionBar().show();
activity.getSupportActionBar().setTitle("Gestion aides");
}
toolbar.setBackgroundColor(getResources().getColor(R.color.primary_green, null));
toolbar.setTitleTextColor(getResources().getColor(R.color.white, null));
}
String agentId = prefsHelper.get("id");
if(agentId == null){
return;
}
agentViewModel.getAgents(agentId).observe(getViewLifecycleOwner(), new Observer<Result<List<User>>>() {
@Override
public void onChanged(Result<List<User>> listResult) {
switch (listResult.status){
case LOADING:{
loaderDialog.show("Chargement des agents");
break;
}
case ERROR:{
loaderDialog.dismiss();
MessageDialog.showError(getContext(), listResult.message);
}
case SUCCESS:{
loaderDialog.dismiss();
agentItemAdapter = new AgentItemAdapter(listResult.data);
binding.agentList.setLayoutManager(new LinearLayoutManager(getContext()));
binding.agentList.setAdapter(agentItemAdapter);
agentItemAdapter.setOnItemClickListener(new AgentItemAdapter.onItemClickListener() {
@Override
public void onItemClick(User agent) {
AgentDetails agentDetails = AgentDetails.newInstance(agent);
authNavigator.navigate(agentDetails);
}});
}
}
}
});
}
}

View File

@@ -37,13 +37,16 @@ import android.widget.Toast;
import com.anggastudio.printama.Printama;
import com.example.quiz.data.model.Course;
import com.example.quiz.data.model.MiseInitiale;
import com.example.quiz.data.model.Pari;
import com.example.quiz.data.model.PariMise;
import com.example.quiz.data.model.ParisResponse;
import com.example.quiz.data.model.Reunion;
import com.example.quiz.data.model.TypeOfBet;
import com.example.quiz.data.model.dtos.NotifPayload;
import com.example.quiz.data.model.dtos.PariCourseDto;
import com.example.quiz.data.model.enums.PariStatut;
import com.example.quiz.data.remote.StompManager;
import com.example.quiz.databinding.FragmentBetValidationBinding;
import com.example.quiz.utils.BluetoothUtils;
import com.example.quiz.utils.LoaderDialog;
@@ -51,14 +54,18 @@ import com.example.quiz.utils.MessageDialog;
import com.example.quiz.utils.Result;
import com.example.quiz.utils.SharedPrefsHelper;
import com.example.quiz.viewModel.LogsViewModel;
import com.example.quiz.viewModel.PariMiseViewModel;
import com.example.quiz.viewModel.PariViewModel;
import com.example.quiz.viewModel.SharedViewModel;
import com.google.android.material.appbar.MaterialToolbar;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
@@ -69,6 +76,9 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
/**
@@ -84,8 +94,15 @@ public class BetValidation extends Fragment {
SharedViewModel shared;
PariMiseViewModel pariMiseViewModel;
List<MiseInitiale> misesInitiales;
private AlertDialog dialog;
@Inject
StompManager stompManager;
private int nombreX;
LogsViewModel logsViewModel;
@@ -253,6 +270,40 @@ public class BetValidation extends Fragment {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
shared = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
pariMiseViewModel = new ViewModelProvider(this).get(PariMiseViewModel.class);
pariMiseViewModel.getBetInitMise().observe(
getViewLifecycleOwner(),
result->{
switch (result.status){
case LOADING: {
loader.show("Chargement des mise");
break;
}
case SUCCESS: {
misesInitiales = result.data;
loader.dismiss();
break;
}
case ERROR: {
loader.dismiss();
MessageDialog.showError(getContext(), result.message);
}
default:
break;
}
}
);
if(shared.selectedCourse.getValue() != null){
stompManager.subscribe("courses/"+shared.selectedCourse.getValue().getId(), json->{
Type type = new TypeToken<NotifPayload<Course>>(){}.getType();
NotifPayload<Course> notif = new Gson().fromJson(json, type);
Course updatedCourse = notif.getPayload();
if(shared.selectedCourse.getValue().getId() == updatedCourse.getId()){
shared.setSelectedCourse(updatedCourse);
setupNumberGrid(binding.gridNumbers);
}
});
}
setSelectedTypeOfBetImage();
pariViewModel = new ViewModelProvider(this).get(PariViewModel.class);
typeOfBet = shared.typeOfBet.getValue();
@@ -266,7 +317,6 @@ public class BetValidation extends Fragment {
activity.getSupportActionBar().setTitle("Pari "+shared.selectedCourse.getValue().getNom());
}
}
setupNumberGrid(binding.gridNumbers);
binding.paymentType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
@@ -323,8 +373,32 @@ public class BetValidation extends Fragment {
});
binding.betValidateBtn.setOnClickListener(v->{
if (ActivityCompat.checkSelfPermission(getContext(), android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
int paperStatus = Printama.with(getContext()).checkPaperStatus();
Log.d("PAPER_STATUS", String.valueOf(paperStatus));
switch (paperStatus){
case 2 :{
MessageDialog.showError(getContext(), "Le papier d'impression est vide");
return;
}
case 1:{
MessageDialog.showError(getContext(), "Le papier d'impression est presque vide");
break;
}
default:
break;
}
if(binding.paymentType.getSelectedItem().toString().equals("Orange Money") && binding.phoneNumber.getText().toString().isEmpty()){
Toast.makeText(getContext(), "Veuillez entrer un numéro de téléphone", Toast.LENGTH_SHORT).show();
MessageDialog.showError(getContext(), "Veuillez saisir le numéro de téléphone");
return;
}
@@ -444,7 +518,6 @@ public class BetValidation extends Fragment {
String formattedDate = dateTime.format(formatter);
tspl.append(formattedDate).append("\n");
tspl.append("Course ").append(String.valueOf(shared.selectedCourse.getValue().getId())).append("\n");
tspl.append(shared.selectedCourse.getValue().getTypesParisOuverts().get(0)).append("\n");
tspl.append(printama.lineSeparator()+"\n");
boolean isElargie = selectedHorses.getValue().size()>shared.typeOfBet.getValue().getNumberOfHorse();
String typePari = pari.getTypesParisMises().get(0).getTypePari();
@@ -564,19 +637,44 @@ public class BetValidation extends Fragment {
}
int typeOfBetHorses = shared.typeOfBet.getValue().getNumberOfHorse();
int partants = shared.selectedCourse.getValue().getNombrePartants();
Course.TypeParis courseName = shared.typeOfBet.getValue().getName();
if(shared.typeOfBet.getValue().getNumberOfHorse() > nombreChevauxSelectionnes){
binding.mise.setText(mise+" CFA");
return;
}
if(typeOfBetHorses == 2){
mise = 500;
}else{
if(typeOfBetHorses == 5){
mise = 300;
}else{
mise = 200;
MiseInitiale miseModel = misesInitiales.stream()
.filter(miseInitiale-> miseInitiale.getTypePari().equals(courseName))
.findFirst()
.orElse(null);
if(miseModel == null){
MessageDialog.showError(getContext(), "Erreur lors de l'initialisation de la mise");
_initializeToZero();
return;
}
mise = miseModel.getMiseInitialeMin();
Course.TypeParis typePari = shared.typeOfBet.getValue().getName();
boolean estCoupleGagnantOuPlace = typePari.equals(Course.TypeParis.COUPLE_GAGNANT) || typePari.equals(Course.TypeParis.COUPLE_PLACE);
Log.d("TYPE_PARI", typePari.toString());
if (estCoupleGagnantOuPlace) {
// Pour COUPLE_GAGNANT ou COUPLE_PLACE: coefficient < 200
if (coeff > 200) {
// Erreur: coefficient trop élevé
MessageDialog.showError(getContext(),"Le coefficient doit être au plus 200 pour "+typePari.toString());
_initializeToZero();
return;
}
} else {
// Pour les autres types: coefficient <= 20
if (coeff > 20) {
// Erreur: coefficient trop élevé
MessageDialog.showError(getContext(), "Le coefficient doit être inférieur ou égal à 20 pour "+typePari.toString());
_initializeToZero();
return;
}
}
mise = mise * coeff;
if(nombreX>0){
if(nombreChevauxSelectionnes == typeOfBetHorses){
@@ -651,4 +749,10 @@ public class BetValidation extends Fragment {
super.onDestroyView();
binding = null;
}
@Override
public void onDestroy() {
super.onDestroy();
stompManager.disconnect();
}
}

View File

@@ -18,11 +18,17 @@ import com.example.quiz.viewModel.LoginViewModel;
import com.example.quiz.viewModel.LogsViewModel;
import com.google.android.material.appbar.MaterialToolbar;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
/**
* A simple {@link Fragment} subclass.
* Use the {@link Caisse#newInstance} factory method to
* create an instance of this fragment.
*/
@AndroidEntryPoint
public class Caisse extends Fragment {
FragmentCaisseBinding binding;
@@ -47,14 +53,13 @@ public class Caisse extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sessionManager = SessionManager.newInstance(getContext());
FragmentManager fragmentManager = getParentFragmentManager();
authNavigator = new AuthNavigator(getContext(),fragmentManager, sessionManager,new ViewModelProvider(requireActivity()).get(LoginViewModel.class), new ViewModelProvider(requireActivity()).get(LogsViewModel.class));
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentCaisseBinding.inflate(inflater, container, false);
authNavigator = new AuthNavigator(getContext(), getParentFragmentManager(), sessionManager,new ViewModelProvider(this).get(LoginViewModel.class),new ViewModelProvider(this).get(LogsViewModel.class), this);
return binding.getRoot();
}

View File

@@ -29,6 +29,7 @@ import com.anggastudio.printama.Printama;
import com.example.quiz.data.adapter.BetsAdapter;
import com.example.quiz.data.model.Course;
import com.example.quiz.data.model.PagedModel;
import com.example.quiz.data.remote.StompManager;
import com.example.quiz.databinding.FragmentListOFBettingBinding;
import com.example.quiz.utils.AuthNavigator;
import com.example.quiz.utils.BluetoothUtils;
@@ -46,6 +47,8 @@ import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
/**
@@ -56,15 +59,15 @@ import dagger.hilt.android.AndroidEntryPoint;
@AndroidEntryPoint
public class ListOFBets extends Fragment {
FragmentListOFBettingBinding binding;
LoaderDialog loader;
private SharedViewModel shared;
private CourseViewModel viewModel;
@Inject
StompManager stompManager;
AuthNavigator authNavigator;
private BetsAdapter adapter;
@@ -85,31 +88,8 @@ public class ListOFBets extends Fragment {
super.onCreate(savedInstanceState);
SessionManager sessionManager = SessionManager.newInstance(getContext());
FragmentManager fragmentManager = getParentFragmentManager();
authNavigator = new AuthNavigator(getContext(), fragmentManager, sessionManager, new ViewModelProvider(requireActivity()).get(LoginViewModel.class), new ViewModelProvider(requireActivity()).get(LogsViewModel.class));
requestPermission();
}
private void requestPermission(){
Pref.init(getContext());
if (BluetoothUtils.needsBluetoothPermissions()) {
if (!BluetoothUtils.hasBluetoothPermission(getContext())) {
// Demande la permission si non accordée
BluetoothUtils.requestBluetoothPermission(requireActivity());
return; // arrête ici, la popup va apparaître
}
}
// 2⃣ Permission OK, on peut afficher la liste
try {
Printama printama = Printama.with(getContext());
if(!printama.isConnected()){
BluetoothUtils.showPrinterList(getContext(), requireActivity());
}
} catch (SecurityException e) {
Toast.makeText(getContext(),
"Permission Bluetooth non accordée", Toast.LENGTH_SHORT).show();
}
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
@@ -121,7 +101,7 @@ public class ListOFBets extends Fragment {
binding.recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
binding.recyclerView.setAdapter(adapter);
viewModel = new ViewModelProvider(this).get(CourseViewModel.class);
authNavigator = new AuthNavigator(getContext(), getParentFragmentManager(), SessionManager.newInstance(getContext()),new ViewModelProvider(this).get(LoginViewModel.class),new ViewModelProvider(this).get(LogsViewModel.class), this);
/*viewModel.bets.observe(getViewLifecycleOwner(), bets -> {
@@ -173,8 +153,12 @@ public class ListOFBets extends Fragment {
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
MenuHost menuHost = requireActivity();
stompManager.subscribe("courses", json->{
requireActivity().runOnUiThread(this::observe);
});
MenuHost menuHost = requireActivity();
// 🔹 On enlève d'abord les anciens menu providers si ce fragment est recréé
menuHost.invalidateMenu();
@@ -222,4 +206,10 @@ public class ListOFBets extends Fragment {
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
stompManager.disconnect();
}
}

View File

@@ -7,9 +7,11 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -18,18 +20,33 @@ import android.widget.Toast;
import com.example.quiz.data.adapter.TypeOfBetAdapter;
import com.example.quiz.data.model.Course;
import com.example.quiz.data.model.PariMise;
import com.example.quiz.data.model.TypeOfBet;
import com.example.quiz.data.model.dtos.NotifPayload;
import com.example.quiz.data.remote.StompManager;
import com.example.quiz.databinding.FragmentListOfTypeOfBetsBinding;
import com.example.quiz.utils.AuthNavigator;
import com.example.quiz.utils.LoaderDialog;
import com.example.quiz.utils.MessageDialog;
import com.example.quiz.utils.Result;
import com.example.quiz.utils.SessionManager;
import com.example.quiz.utils.SharedPrefsHelper;
import com.example.quiz.viewModel.AgentViewModel;
import com.example.quiz.viewModel.LoginViewModel;
import com.example.quiz.viewModel.LogsViewModel;
import com.example.quiz.viewModel.PariMiseViewModel;
import com.example.quiz.viewModel.SharedViewModel;
import com.google.android.material.appbar.MaterialToolbar;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
@@ -44,13 +61,25 @@ public class ListOfTypeOfBets extends Fragment {
private FragmentListOfTypeOfBetsBinding binding;
SessionManager sessionManager;
AuthNavigator authNavigator;
@Inject
StompManager stompManager;
private SharedViewModel shared;
private TypeOfBetAdapter adapter;
private SharedPrefsHelper prefsHelper;
private AgentViewModel agentViewModel;
private LoaderDialog loaderDialog;
public ListOfTypeOfBets() {
// Required empty public constructor
}
@@ -65,28 +94,74 @@ public class ListOfTypeOfBets extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sessionManager = SessionManager.newInstance(getContext());
authNavigator = new AuthNavigator(getContext(), getParentFragmentManager(), sessionManager, new ViewModelProvider(this).get(LoginViewModel.class), new ViewModelProvider(this).get(LogsViewModel.class));
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentListOfTypeOfBetsBinding.inflate(inflater, container, false);
authNavigator = new AuthNavigator(getContext(), getParentFragmentManager(), SessionManager.newInstance(getContext()),new ViewModelProvider(this).get(LoginViewModel.class),new ViewModelProvider(this).get(LogsViewModel.class), this);
prefsHelper = SharedPrefsHelper.getInstance(getContext());
loaderDialog = new LoaderDialog(getContext());
agentViewModel = new ViewModelProvider(this).get(AgentViewModel.class);
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if(prefsHelper.get("id") == null){
MessageDialog.showError(getContext(), "Veuillez vous connecter");
return;
}
agentViewModel.getAvailableBets(prefsHelper.get("id")).observe(getViewLifecycleOwner(), new Observer<Result<List<Course.TypeParis>>>() {
@Override
public void onChanged(Result<List<Course.TypeParis>> listResult) {
switch (listResult.status){
case LOADING:{
loaderDialog.show("Chargement des types de paris");
break;
}
case ERROR:{
loaderDialog.dismiss();
MessageDialog.showError(getContext(), listResult.message);
break;
}
case SUCCESS:{
loaderDialog.dismiss();
syncAuthorizedBets(listResult.data);
break;
}
}
}
});
}
public void syncAuthorizedBets(List<Course.TypeParis> authorizedBets){
binding.typeOfBetRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
// ViewModels
//viewModel = new ViewModelProvider(this).get(BetViewModel.class);
shared = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
// Observers
if(shared.selectedCourse.getValue() == null){
return;
}
Course sCourse = shared.selectedCourse.getValue();
if(sCourse != null){
stompManager.subscribe("courses/"+sCourse.getId(), json->{
requireActivity().runOnUiThread(()->{
Type type = new TypeToken<NotifPayload<Course>>(){}.getType();
NotifPayload<Course> notif = new Gson().fromJson(json, type);
Course updatedCourse = notif.getPayload();
if(sCourse.getId() == updatedCourse.getId()){
shared.setSelectedCourse(updatedCourse);
}
});
});
}
// ⚡ Initialiser ladapter UNE SEULE FOIS
adapter = new TypeOfBetAdapter(new ArrayList<>());
binding.typeOfBetRecyclerView.setAdapter(adapter);
List<Course.TypeParis> betType = shared.selectedCourse.getValue().getTypesParisOuverts();
List<Course.TypeParis> betType = sCourse.getTypesParisOuverts();
List<TypeOfBet> useList = new ArrayList<>();
if(betType.contains(Course.TypeParis.QUINTE)){
useList.add(new TypeOfBet("Quinte", Course.TypeParis.QUINTE, 5));
@@ -97,14 +172,15 @@ public class ListOfTypeOfBets extends Fragment {
if(betType.contains(Course.TypeParis.TIERCE)){
useList.add(new TypeOfBet("Tierce", Course.TypeParis.TIERCE, 3));
}
if(betType.contains(Course.TypeParis.COUPLE_PLACE)){
useList.add(new TypeOfBet("Couple Place", Course.TypeParis.COUPLE_PLACE, 2));
}
if(betType.contains(Course.TypeParis.COUPLE_PLACE)){
useList.add(new TypeOfBet("Couple Gagnant", Course.TypeParis.COUPLE_GAGNANT, 2));
}
AtomicReference<TypeOfBet> typeOfBet = new AtomicReference<>();
adapter.setTypes(useList);
if(betType.contains(Course.TypeParis.COUPLE_PLACE)){
useList.add(new TypeOfBet("Couple Place", Course.TypeParis.COUPLE_PLACE, 2));
}
if(betType.contains(Course.TypeParis.COUPLE_PLACE)){
useList.add(new TypeOfBet("Couple Gagnant", Course.TypeParis.COUPLE_GAGNANT, 2));
}
AtomicReference<TypeOfBet> typeOfBet = new AtomicReference<>();
List<TypeOfBet> availableTypeOfBets = useList.stream().filter(type -> authorizedBets.contains(type.getName())).collect(Collectors.toList());
adapter.setTypes(availableTypeOfBets);
adapter.setOnItemClickListener(type -> {
typeOfBet.set(type);
});
@@ -133,4 +209,10 @@ public class ListOfTypeOfBets extends Fragment {
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
stompManager.disconnect();
}
}

View File

@@ -19,11 +19,14 @@ import com.example.quiz.utils.SharedPrefsHelper;
import com.example.quiz.viewModel.LoginViewModel;
import com.example.quiz.viewModel.LogsViewModel;
import dagger.hilt.android.AndroidEntryPoint;
/**
* A simple {@link Fragment} subclass.
* Use the {@link Login#newInstance} factory method to
* create an instance of this fragment.
*/
@AndroidEntryPoint
public class Login extends Fragment {
@@ -52,6 +55,7 @@ public class Login extends Fragment {
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentLoginBinding.inflate(inflater, container, false);
authNavigator = new AuthNavigator(getContext(), getParentFragmentManager(), SessionManager.newInstance(getContext()),new ViewModelProvider(this).get(LoginViewModel.class),new ViewModelProvider(this).get(LogsViewModel.class), this);
return binding.getRoot();
}
@@ -60,7 +64,6 @@ public class Login extends Fragment {
super.onViewCreated(view, savedInstanceState);
SessionManager sessionManager = SessionManager.newInstance(getContext());
FragmentManager fragmentManager = getParentFragmentManager();
authNavigator = new AuthNavigator(getContext(), fragmentManager, sessionManager, new ViewModelProvider(requireActivity()).get(LoginViewModel.class), new ViewModelProvider(requireActivity()).get(LogsViewModel.class));
AppCompatActivity activity = (AppCompatActivity) getActivity();
if(activity != null){
activity.getSupportActionBar().hide();

View File

@@ -2,12 +2,14 @@ package com.example.quiz;
import android.graphics.Color;
import android.os.Bundle;
import com.anggastudio.printama.Pref;
import com.anggastudio.printama.Printama;
import com.example.quiz.utils.AuthNavigator;
import com.example.quiz.utils.BluetoothUtils;
import com.example.quiz.utils.MessageDialog;
import com.example.quiz.utils.SessionManager;
import com.example.quiz.utils.SharedPrefsHelper;
import com.example.quiz.viewModel.LoginViewModel;
import com.example.quiz.viewModel.LogsViewModel;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Handler;
import android.os.Looper;
@@ -20,6 +22,8 @@ import androidx.navigation.ui.NavigationUI;
import com.example.quiz.databinding.ActivityPageQuizBinding;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
@AndroidEntryPoint
@@ -46,7 +50,7 @@ public class PageQuiz extends AppCompatActivity {
setContentView(binding.getRoot());
logsViewModel = new ViewModelProvider(this).get(LogsViewModel.class);
viewModel = new ViewModelProvider(this).get(LoginViewModel.class);
authNavigator = new AuthNavigator(this, getSupportFragmentManager(), sessionManager, viewModel, logsViewModel);
authNavigator = new AuthNavigator(this, getSupportFragmentManager(), sessionManager, viewModel, logsViewModel, this);
setSupportActionBar(binding.toolbar);
binding.toolbar.setBackgroundColor(Color.parseColor("#501C5A29"));
}
@@ -117,12 +121,32 @@ public class PageQuiz extends AppCompatActivity {
// }
private void checkPermission(){
Pref.init(this);
if (BluetoothUtils.needsBluetoothPermissions()) {
if (!BluetoothUtils.hasBluetoothPermission(this)) {
// Demande la permission si non accordée
BluetoothUtils.requestBluetoothPermission(this);
return; // arrête ici, la popup va apparaître
}
}
// 2⃣ Permission OK, on peut afficher la liste
try {
Printama printama = Printama.with(this);
if(printama.getConnectedPrinter() == null){
BluetoothUtils.showPrinterList(this, this);
}
} catch (SecurityException e) {
MessageDialog.showError(this, "Permission refusée");
}
}
@Override
protected void onResume() {
checkPermission();
super.onResume();
handler = new Handler(Looper.getMainLooper());
checkRunnable = new Runnable() {
@Override
public void run() {

View File

@@ -6,19 +6,30 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.quiz.databinding.FragmentSettingsBinding;
import com.example.quiz.utils.AuthNavigator;
import com.example.quiz.utils.SessionManager;
import com.example.quiz.utils.SharedPrefsHelper;
import com.example.quiz.viewModel.LoginViewModel;
import com.example.quiz.viewModel.LogsViewModel;
import com.google.android.material.appbar.MaterialToolbar;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
/**
* A simple {@link Fragment} subclass.
* Use the {@link Settings#newInstance} factory method to
* create an instance of this fragment.
*/
@AndroidEntryPoint
public class Settings extends Fragment {
// TODO: Rename parameter arguments, choose names that match
@@ -28,6 +39,10 @@ public class Settings extends Fragment {
FragmentSettingsBinding binding;
AuthNavigator authNavigator;
SharedPrefsHelper prefsHelper;
public Settings() {
// Required empty public constructor
}
@@ -42,14 +57,16 @@ public class Settings extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
prefsHelper = SharedPrefsHelper.getInstance(getContext());
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentSettingsBinding.inflate(inflater, container, false);
// Inflate the layout for this fragment
authNavigator = new AuthNavigator(getContext(), getParentFragmentManager(), SessionManager.newInstance(getContext()),new ViewModelProvider(this).get(LoginViewModel.class),new ViewModelProvider(this).get(LogsViewModel.class), this);
return binding.getRoot();
}
@@ -57,6 +74,16 @@ public class Settings extends Fragment {
public void onViewCreated(@NonNull View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
AppCompatActivity activity = (AppCompatActivity) getActivity();
if(prefsHelper.get("isSupAgent") == null){
binding.agentManagement.setVisibility(View.GONE);
}else{
String isSubAgent = prefsHelper.get("isSupAgent");
if(isSubAgent.equals("true")){
binding.agentManagement.setVisibility(View.GONE);
}else{
binding.agentManagement.setVisibility(View.VISIBLE);
}
}
if(activity != null){
MaterialToolbar toolbar = activity.findViewById(R.id.toolbar);
activity.setSupportActionBar(toolbar);
@@ -92,6 +119,21 @@ public class Settings extends Fragment {
.commit();
}
});
binding.changePin.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
UpdatePin updatePin = UpdatePin.newInstance();
authNavigator.navigate(updatePin);
}
});
binding.agentManagement.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AgentManagement agentManagement = AgentManagement.newInstance();
authNavigator.navigate(agentManagement);
}
});
}

View File

@@ -2,11 +2,15 @@ package com.example.quiz;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.LifecycleOwner;
@@ -19,6 +23,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.anggastudio.printama.Printama;
import com.example.quiz.data.model.dtos.paris.SoldeResponse;
import com.example.quiz.databinding.FragmentSoldBinding;
import com.example.quiz.utils.LoaderDialog;
import com.example.quiz.utils.MessageDialog;
@@ -28,6 +34,8 @@ import com.example.quiz.viewModel.LogsViewModel;
import com.example.quiz.viewModel.PariViewModel;
import com.google.android.material.appbar.MaterialToolbar;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import dagger.hilt.android.AndroidEntryPoint;
@@ -116,9 +124,9 @@ public class Sold extends Fragment {
getContext(),
(view, year, month, dayOfMonth) -> {
String date = year + "-" + _reformatDateForDate(month + 1) + "-" + _reformatDateForDate(dayOfMonth);
pariViewModel.getSoldeByDay(prefsHelper.get("id"), date).observe(getViewLifecycleOwner(), new Observer<Result<Double>>() {
pariViewModel.getSoldeByDay(prefsHelper.get("id"), date).observe(getViewLifecycleOwner(), new Observer<Result<SoldeResponse>>() {
@Override
public void onChanged(Result<Double> doubleResult) {
public void onChanged(Result<SoldeResponse> doubleResult) {
switch (doubleResult.status){
case LOADING:{
dialog.show("Solde du jour");
@@ -131,7 +139,8 @@ public class Sold extends Fragment {
}
case SUCCESS:{
dialog.dismiss();
_showSold(doubleResult.data);
_showSold(doubleResult.data, date);
logsViewModel.insertLog(prefsHelper.get("id"), "SOLDE JOUR", "Solde du "+date, System.currentTimeMillis());
break;
}
@@ -147,12 +156,44 @@ public class Sold extends Fragment {
datePickerDialog.show();
}
void _showSold(double solde){
void _showSold(SoldeResponse soldeResponse, String date){
int solde = soldeResponse.getMontantParis() - soldeResponse.getMontantAnnulations() - soldeResponse.getMontantPaiements();
new AlertDialog.Builder(getContext())
.setTitle("Solde")
.setMessage("Solde la course "+solde)
.setPositiveButton("Ok", (dialog, which)->{
.setNeutralButton("Ok", (dialog, which)->{
dialog.dismiss();
}).show();
})
.setPositiveButton("Imprimer", (dialog, which)->{
if (ActivityCompat.checkSelfPermission(getContext(), android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
Printama printama = Printama.with(getContext());
Bitmap logo = BitmapFactory.decodeResource(getResources(), R.drawable.pmu_logo);
String title = "SOLDE AU "+date;
StringBuilder text = new StringBuilder();
text.append("VENTES HIPPIQUES: ").append(String.valueOf(soldeResponse.getMontantParis())).append(" XOF").append("\n");
text.append("NBR. PAIEMENTS: ").append(soldeResponse.getNombrePaiements()).append("\n");
text.append("PAIEMENTS: ").append(String.valueOf(soldeResponse.getMontantPaiements())).append(" XOF").append("\n");
text.append("NBR. ANNULATIONS: ").append(soldeResponse.getNombreAnnulations()).append("\n");
text.append("ANNULATIONS: ").append(String.valueOf(soldeResponse.getMontantAnnulations())).append(" XOF").append("\n");
text.append(printama.lineSeparator()).append("\n");
text.append("SOLDE: ").append(String.valueOf(solde)).append(" XOF").append("\n");
text.append(printama.lineSeparator()).append("\n");
text.append("AGENT : ").append(prefsHelper.get("code")).append("\n");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
String now = formatter.format(LocalDateTime.now());
text.append("DATE : ").append(now).append("\n");
printama.printSold(logo, title, text);
})
.show();
}
}

View File

@@ -1,10 +1,14 @@
package com.example.quiz;
import android.app.AlertDialog;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
@@ -14,6 +18,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.anggastudio.printama.Printama;
import com.example.quiz.data.model.dtos.paris.SoldeResponse;
import com.example.quiz.databinding.FragmentSoldByCourseBinding;
import com.example.quiz.utils.LoaderDialog;
import com.example.quiz.utils.MessageDialog;
@@ -22,6 +28,9 @@ import com.example.quiz.utils.SharedPrefsHelper;
import com.example.quiz.viewModel.LogsViewModel;
import com.example.quiz.viewModel.PariViewModel;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import dagger.hilt.android.AndroidEntryPoint;
/**
@@ -78,9 +87,9 @@ public class SoldByCourse extends Fragment {
binding.etRaceNumber.setError("Veuillez entrer un numéro de course");
return;
}
pariViewModel.getSoldeByCourse(prefsHelper.get("id"), binding.etRaceNumber.getText().toString()).observe(getViewLifecycleOwner(), new Observer<Result<Double>>() {
pariViewModel.getSoldeByCourse(prefsHelper.get("id"), binding.etRaceNumber.getText().toString()).observe(getViewLifecycleOwner(), new Observer<Result<SoldeResponse>>() {
@Override
public void onChanged(Result<Double> doubleResult) {
public void onChanged(Result<SoldeResponse> doubleResult) {
switch (doubleResult.status){
case LOADING:{
loader.show("Chargement du solde");
@@ -93,7 +102,7 @@ public class SoldByCourse extends Fragment {
}
case SUCCESS:{
loader.dismiss();
_showPariDialog(doubleResult.data);
_showPariDialog(doubleResult.data, binding.etRaceNumber.getText().toString());
logsViewModel.insertLog(prefsHelper.get("id"), "SOLDE COURSE", "Solde de la course "+binding.etRaceNumber.getText(), System.currentTimeMillis());
break;
}
@@ -103,14 +112,44 @@ public class SoldByCourse extends Fragment {
});
}
void _showPariDialog(double solde){
void _showPariDialog(SoldeResponse soldeResponse, String numero){
int solde = soldeResponse.getMontantParis() - soldeResponse.getMontantAnnulations() - soldeResponse.getMontantPaiements();
new AlertDialog.Builder(getContext())
.setTitle("Solde")
.setMessage("Solde la course "+solde)
.setPositiveButton("Ok", (dialog, which) -> {
.setNeutralButton("Ok", (dialog, which) -> {
binding.etRaceNumber.setText("");
dialog.dismiss();
})
.setPositiveButton("Imprimer", (dialog, which)->{
if (ActivityCompat.checkSelfPermission(getContext(), android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
Printama printama = Printama.with(getContext());
Bitmap logo = BitmapFactory.decodeResource(getResources(), R.drawable.pmu_logo);
String title = "SOLDE DE LA COURSE "+numero;
StringBuilder text = new StringBuilder();
text.append("VENTES HIPPIQUES: ").append(String.valueOf(soldeResponse.getMontantParis())).append(" XOF").append("\n");
text.append("NBR. PAIEMENTS: ").append(soldeResponse.getNombrePaiements()).append("\n");
text.append("PAIEMENTS: ").append(String.valueOf(soldeResponse.getMontantPaiements())).append(" XOF").append("\n");
text.append("NBR. ANNULATIONS: ").append(soldeResponse.getNombreAnnulations()).append("\n");
text.append("ANNULATIONS: ").append(String.valueOf(soldeResponse.getMontantAnnulations())).append(" XOF").append("\n");
text.append(printama.lineSeparator()).append("\n");
text.append("SOLDE: ").append(String.valueOf(solde)).append(" XOF").append("\n");
text.append(printama.lineSeparator()).append("\n");
text.append("AGENT : ").append(prefsHelper.get("code")).append("\n");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
String now = formatter.format(LocalDateTime.now());
text.append("DATE : ").append(now).append("\n");
printama.printSold(logo, title, text);
})
.show();
}
}

View File

@@ -0,0 +1,24 @@
package com.example.quiz;
import com.example.quiz.data.remote.NotificationHelper;
import com.example.quiz.data.remote.StompManager;
import com.example.quiz.data.remote.TokenManager;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import dagger.hilt.InstallIn;
import dagger.hilt.components.SingletonComponent;
@Module
@InstallIn(SingletonComponent.class)
public class StompModule {
@Provides
@Singleton
public StompManager provideStompManager(TokenManager tokenManager,
NotificationHelper notificationHelper) {
return new StompManager(tokenManager, notificationHelper);
}
}

View File

@@ -0,0 +1,132 @@
package com.example.quiz;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.quiz.data.model.dtos.auth.User;
import com.example.quiz.databinding.FragmentUpdatePinBinding;
import com.example.quiz.utils.AuthNavigator;
import com.example.quiz.utils.LoaderDialog;
import com.example.quiz.utils.MessageDialog;
import com.example.quiz.utils.Result;
import com.example.quiz.utils.SessionManager;
import com.example.quiz.viewModel.LoginViewModel;
import com.example.quiz.viewModel.LogsViewModel;
import dagger.hilt.android.AndroidEntryPoint;
/**
* A simple {@link Fragment} subclass.
* Use the {@link UpdatePin#newInstance} factory method to
* create an instance of this fragment.
*/
@AndroidEntryPoint
public class UpdatePin extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
// TODO: Rename and change types of parameters
FragmentUpdatePinBinding binding;
String oldPin;
String newPin;
String confirmation;
AuthNavigator authNavigator;
LoginViewModel viewModel;
LoaderDialog loaderDialog;
public UpdatePin() {
// Required empty public constructor
}
// TODO: Rename and change types and number of parameters
public static UpdatePin newInstance() {
UpdatePin fragment = new UpdatePin();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
viewModel = new ViewModelProvider(this).get(LoginViewModel.class);
loaderDialog = new LoaderDialog(getContext());
authNavigator = new AuthNavigator(getContext(), getParentFragmentManager(), new SessionManager(getContext()), new ViewModelProvider(this).get(LoginViewModel.class),new ViewModelProvider(this).get(LogsViewModel.class),this);
// Inflate the layout for this fragment
binding = FragmentUpdatePinBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.validate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
oldPin = binding.oldPin.getText().toString();
newPin = binding.newPin.getText().toString();
confirmation = binding.pinConfirmation.getText().toString();
if(oldPin.length()<4 || newPin.length()<4 || confirmation.length()<4){
MessageDialog.showError(getContext(), "Le pin doit au mois avoir 4 Caractères!");
return;
}
if(!newPin.equals(confirmation)){
MessageDialog.showError(getContext(), "Les pins ne sont pas identiques!");
return;
}
viewModel.changePin(oldPin, newPin).observe(getViewLifecycleOwner(), new Observer<Result<User>>(){
@Override
public void onChanged(Result<User> userResult) {
switch (userResult.status){
case LOADING:{
loaderDialog.show("Mis à jour du pin");
break;
}
case ERROR:{
loaderDialog.dismiss();
MessageDialog.showError(getContext(), userResult.message);
break;
}
case SUCCESS:{
loaderDialog.dismiss();
MessageDialog.showSuccess(getContext(), "Pin mis à jour avec succès");
SessionManager sessionManager = new SessionManager(getContext());
authNavigator.showPinDialog(()->{
sessionManager.updateLastExpiredDate();
getParentFragmentManager().popBackStack();
});
}
}
}
});
}
});
binding.cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getParentFragmentManager().popBackStack();
}
});
}
}

View File

@@ -0,0 +1,94 @@
package com.example.quiz.data.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.quiz.R;
import com.example.quiz.data.model.dtos.auth.User;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class AgentItemAdapter extends RecyclerView.Adapter<AgentItemAdapter.AgentItemViewHolder> {
private List<User> agents = new ArrayList<>();
public AgentItemAdapter(){
}
public AgentItemAdapter(List<User> agents) {
this.agents = agents;
}
public interface onItemClickListener{
void onItemClick(User agent);
}
private onItemClickListener listener;
public void setOnItemClickListener(onItemClickListener listener){
this.listener = listener;
}
static class AgentItemViewHolder extends RecyclerView.ViewHolder{
TextView txtCode, txtDate, txtAdresse;
ImageButton details;
public AgentItemViewHolder(@NonNull View itemView) {
super(itemView);
txtCode = itemView.findViewById(R.id.txtCode);
txtDate = itemView.findViewById(R.id.txtDate);
txtAdresse = itemView.findViewById(R.id.txtAdresse);
details = itemView.findViewById(R.id.details);
}
}
@Override
public int getItemCount() {
return agents.size();
}
@NonNull
@Override
public AgentItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.agent_item, parent, false);
return new AgentItemViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull AgentItemViewHolder holder, int position) {
User agent = agents.get(position);
holder.txtCode.setText(agent.getCode());
if(agent.getDateEmbauche() == null){
holder.txtDate.setText("Pas de date");
}else{
LocalDate date = LocalDate.parse(agent.getDateEmbauche());
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
holder.txtDate.setText(dateTimeFormatter.format(date));
}
if(agent.getVille() == null || agent.getAdresse() == null){
holder.txtAdresse.setText("Pas d'adresse");
}else{
String address = agent.getVille()+"; "+agent.getAdresse();
holder.txtAdresse.setText(address);
}
holder.details.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(listener != null){
listener.onItemClick(agent);
}
}
});
}
}

View File

@@ -0,0 +1,149 @@
package com.example.quiz.data.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.quiz.R;
import com.example.quiz.data.model.Course;
import com.example.quiz.data.model.TypeOfBet;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class MultiTypeOfBetsAdapter extends RecyclerView.Adapter<MultiTypeOfBetsAdapter.TypeOfBetViewHolder> {
private List<TypeOfBet> types;
private Set<Integer> selectedPositions = new HashSet<>();
private onItemClickListener listener;
public interface onItemClickListener {
void onItemClick(TypeOfBet type, boolean isChecked);
void onItemsSelected(List<TypeOfBet> selectedItems);
}
public void setOnItemClickListener(onItemClickListener listener) {
this.listener = listener;
}
public MultiTypeOfBetsAdapter(List<TypeOfBet> types) {
this.types = types;
}
// Méthode pour pré-sélectionner les paris (rendue publique)
public void preSelectAvailableBets(List<Course.TypeParis> preselectedTypes) {
selectedPositions.clear();
for (int i = 0; i < types.size(); i++) {
if (preselectedTypes.contains(types.get(i).getName())) {
selectedPositions.add(i);
}
}
notifyDataSetChanged();
}
public void setTypes(List<TypeOfBet> types) {
this.types = types;
selectedPositions.clear();
notifyDataSetChanged();
}
@NonNull
@Override
public TypeOfBetViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.multi_type_of_bet_item, parent, false);
return new TypeOfBetViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull TypeOfBetViewHolder holder, int position) {
TypeOfBet type = types.get(position);
holder.type_of_bet_name.setText(type.getLabel());
boolean isSelected = selectedPositions.contains(position);
holder.checkBox.setChecked(isSelected);
if (isSelected) {
holder.itemView.setBackgroundResource(R.drawable.item_gradient_bg_selected);
} else {
holder.itemView.setBackgroundResource(R.drawable.item_gradient_bg);
}
holder.itemView.setOnClickListener(v -> {
toggleSelection(position, type);
});
holder.checkBox.setOnClickListener(v -> {
toggleSelection(position, type);
});
}
private void toggleSelection(int position, TypeOfBet type) {
boolean isCurrentlySelected = selectedPositions.contains(position);
if (isCurrentlySelected) {
selectedPositions.remove(position);
} else {
selectedPositions.add(position);
}
if (listener != null) {
listener.onItemClick(type, !isCurrentlySelected);
}
notifyItemChanged(position);
}
@Override
public int getItemCount() {
return types.size();
}
public List<TypeOfBet> getSelectedItems() {
List<TypeOfBet> selectedItems = new ArrayList<>();
for (Integer position : selectedPositions) {
if (position >= 0 && position < types.size()) {
selectedItems.add(types.get(position));
}
}
return selectedItems;
}
public boolean isSelected(int position) {
return selectedPositions.contains(position);
}
public void selectAll() {
selectedPositions.clear();
for (int i = 0; i < types.size(); i++) {
selectedPositions.add(i);
}
notifyDataSetChanged();
}
public void deselectAll() {
selectedPositions.clear();
notifyDataSetChanged();
}
public int getSelectedCount() {
return selectedPositions.size();
}
public class TypeOfBetViewHolder extends RecyclerView.ViewHolder {
TextView type_of_bet_name;
CheckBox checkBox;
public TypeOfBetViewHolder(@NonNull View itemView) {
super(itemView);
type_of_bet_name = itemView.findViewById(R.id.tv_bet_name);
checkBox = itemView.findViewById(R.id.checkbox_bet);
}
}
}

View File

@@ -1,6 +1,5 @@
package com.example.quiz.data.model;
import com.example.quiz.data.model.enums.CourseType;
import java.time.LocalDate;
import java.time.LocalDateTime;

View File

@@ -0,0 +1,49 @@
package com.example.quiz.data.model;
public class MiseInitiale {
private Long id;
private Course.TypeParis typePari;
private Long miseInitialeMin;
private Long miseInitialeMax;
private boolean actif;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Course.TypeParis getTypePari() {
return typePari;
}
public void setTypePari(Course.TypeParis typePari) {
this.typePari = typePari;
}
public Long getMiseInitialeMin() {
return miseInitialeMin;
}
public void setMiseInitialeMin(Long miseInitialeMin) {
this.miseInitialeMin = miseInitialeMin;
}
public Long getMiseInitialeMax() {
return miseInitialeMax;
}
public void setMiseInitialeMax(Long miseInitialeMax) {
this.miseInitialeMax = miseInitialeMax;
}
public boolean isActif() {
return actif;
}
public void setActif(boolean actif) {
this.actif = actif;
}
}

View File

@@ -0,0 +1,17 @@
package com.example.quiz.data.model;
import java.util.List;
public class Restriction {
private List<Course.TypeParis> allowedBetTypes;
public Restriction(){}
public List<Course.TypeParis> getAllowedBetTypes() {
return allowedBetTypes;
}
public void setAllowedBetTypes(List<Course.TypeParis> allowedBetTypes) {
this.allowedBetTypes = allowedBetTypes;
}
}

View File

@@ -0,0 +1,52 @@
package com.example.quiz.data.model.dtos;
public class NotifPayload<T> {
private NotifType type;
private String entity;
private Long entityId;
private T payload;
public NotifPayload() {
}
public NotifType getType() {
return type;
}
public void setType(NotifType type) {
this.type = type;
}
public String getEntity() {
return entity;
}
public void setEntity(String entity) {
this.entity = entity;
}
public Long getEntityId() {
return entityId;
}
public void setEntityId(Long entityId) {
this.entityId = entityId;
}
public T getPayload() {
return payload;
}
public void setPayload(T payload) {
this.payload = payload;
}
public enum NotifType {
COURSE_CREATED,
COURSE_UPDATED,
COURSE_CANCELLED,
COURSE_REPORTED,
COURSE_CLOSED_FOR_BETTING,
RUNNER_DECLARED_NON_PARTANT
}
}

View File

@@ -8,6 +8,8 @@ public class Agent {
private String phone;
private String zone;
private String fonction;
private boolean subAgent;
public Agent(){}
public Long getId() {
@@ -65,4 +67,12 @@ public class Agent {
public void setFonction(String fonction) {
this.fonction = fonction;
}
public boolean isSubAgent() {
return subAgent;
}
public void setSubAgent(boolean subAgent) {
this.subAgent = subAgent;
}
}

View File

@@ -0,0 +1,25 @@
package com.example.quiz.data.model.dtos.auth;
public class ChangePin {
private String oldPin;
private String newPin;
public ChangePin() {
}
public String getOldPin() {
return oldPin;
}
public void setOldPin(String oldPin) {
this.oldPin = oldPin;
}
public String getNewPin() {
return newPin;
}
public void setNewPin(String newPin) {
this.newPin = newPin;
}
}

View File

@@ -0,0 +1,17 @@
package com.example.quiz.data.model.dtos.paris;
public class CancelParisPaylaod {
private String statutPari;
public CancelParisPaylaod(String statutPari) {
this.statutPari = statutPari;
}
public String getStatutPari() {
return statutPari;
}
public void setStatutPari(String statutPari) {
this.statutPari = statutPari;
}
}

View File

@@ -0,0 +1,51 @@
package com.example.quiz.data.model.dtos.paris;
public class SoldeResponse {
private int montantParis;
private int nombrePaiements;
private int montantPaiements;
private int nombreAnnulations;
private int montantAnnulations;
// Getters et setters
public int getMontantParis() {
return montantParis;
}
public void setMontantParis(int montantParis) {
this.montantParis = montantParis;
}
public int getNombrePaiements() {
return nombrePaiements;
}
public void setNombrePaiements(int nombrePaiements) {
this.nombrePaiements = nombrePaiements;
}
public int getMontantPaiements() {
return montantPaiements;
}
public void setMontantPaiements(int montantPaiements) {
this.montantPaiements = montantPaiements;
}
public int getNombreAnnulations() {
return nombreAnnulations;
}
public void setNombreAnnulations(int nombreAnnulations) {
this.nombreAnnulations = nombreAnnulations;
}
public int getMontantAnnulations() {
return montantAnnulations;
}
public void setMontantAnnulations(int montantAnnulations) {
this.montantAnnulations = montantAnnulations;
}
}

View File

@@ -1,7 +0,0 @@
package com.example.quiz.data.model.enums;
public enum CourseType {
TIERCE,
QUARTE,
QUINTE
}

View File

@@ -1,21 +1,29 @@
package com.example.quiz.data.remote;
import com.example.quiz.data.model.Course;
import com.example.quiz.data.model.MiseInitiale;
import com.example.quiz.data.model.PagedModel;
import com.example.quiz.data.model.Pari;
import com.example.quiz.data.model.PariMise;
import com.example.quiz.data.model.ParisResponse;
import com.example.quiz.data.model.PointDeVente;
import com.example.quiz.data.model.Restriction;
import com.example.quiz.data.model.Reunion;
import com.example.quiz.data.model.Tpe;
import com.example.quiz.data.model.dtos.auth.ChangePin;
import com.example.quiz.data.model.dtos.auth.LoginPayload;
import com.example.quiz.data.model.dtos.auth.LoginResponse;
import com.example.quiz.data.model.TpeResponse;
import com.example.quiz.data.model.dtos.auth.User;
import com.example.quiz.data.model.dtos.paris.CancelParisPaylaod;
import com.example.quiz.data.model.dtos.paris.SoldeResponse;
import java.util.List;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.PATCH;
import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Path;
@@ -26,7 +34,7 @@ public interface ApiService {
Call<List<Reunion>> getReunions(@Path("date") String date);
@GET("courses")
Call<PagedModel<Course>> getCourses(@Query("reunionDate") String reunionDate);
Call<PagedModel<Course>> getCourses(@Query("reunionDate") String reunionDate, @Query("statut") String statut);
@POST("paris")
Call<ParisResponse> createPari(@Body Pari pari);
@@ -37,18 +45,39 @@ public interface ApiService {
@GET("paris/agent/{agentId}/today")
Call<List<ParisResponse>> derniersParis(@Path("agentId") String agentId);
@PUT("pari/annuler/{numeroTicket}")
Call<ParisResponse> annulerPari(@Path("numeroTicket") String numeroTicket);
@PATCH("paris/numero/{numeroTicket}/statut")
Call<ParisResponse> annulerPari(@Path("numeroTicket") String numeroTicket,
@Body() CancelParisPaylaod cancelParisPaylaod);
@GET("paris/agent/{agentId}/solde/course/{courseId}")
Call<Double> getSoldeByCourse(@Path("agentId") String createdBy, @Path("courseId") String courseId);
Call<SoldeResponse> getSoldeByCourse(@Path("agentId") String createdBy, @Path("courseId") String courseId);
@GET("paris/agent/{agentId}/solde")
Call<Double> getSoldeByDay(@Path("agentId") String agentId, @Query("date") String day);
Call<SoldeResponse> getSoldeByDay(@Path("agentId") String agentId, @Query("date") String day);
@POST("terminaux")
Call<TpeResponse> createTpe(@Body Tpe tpe);
@GET("points-de-vente")
Call<PagedModel<PointDeVente>> getPointsDeVente(@Query("nom") String nom);
@PATCH("agents/me/pin")
Call<User> changePin(@Body ChangePin pin);
@GET("config/mise-initiale")
Call<List<MiseInitiale>> getBetInitMise();
@GET("agents/byMaster/{masterId}")
Call<List<User>> getAgentsByMaster(@Path("masterId") String masterId);
@GET("agents/{agentId}")
Call<User> getAgent(@Path("agentId") String agentId);
@GET("agents/{agentId}/available-bets")
Call<List<Course.TypeParis>> getAvailableBets(@Path("agentId") String agentId);
@PATCH("agents/{masterId}/slaves/{slaveId}/access")
Call<Void> setAccess(@Path("masterId") String masterId, @Path("slaveId") String slaveId, @Query("block") boolean access);
@POST("agents/{masterId}/slaves/{slaveId}/available-bets")
Call<Void> setRestrictions(@Path("masterId") String masterId, @Path("slaveId") String slaveId, @Body Restriction restrictions);
}

View File

@@ -0,0 +1,57 @@
package com.example.quiz.data.remote;
import android.annotation.SuppressLint;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import com.example.quiz.R;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.hilt.android.qualifiers.ApplicationContext;
@Singleton
public class NotificationHelper {
private final Context context;
private static final String CHANNEL_ID = "socket_channel";
@Inject
public NotificationHelper(@ApplicationContext Context context) {
this.context = context;
createChannel();
}
private void createChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
"Socket Notifications",
NotificationManager.IMPORTANCE_HIGH
);
channel.setDescription("Notifications des messages du socket");
NotificationManager manager =
context.getSystemService(NotificationManager.class);
if (manager != null) manager.createNotificationChannel(channel);
}
}
@SuppressLint("MissingPermission")
public void showNotification(String title, String message) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true);
NotificationManagerCompat manager = NotificationManagerCompat.from(context);
manager.notify((int) System.currentTimeMillis(), builder.build());
}
}

View File

@@ -1,85 +0,0 @@
package com.example.quiz.data.remote;
import android.util.Log;
import javax.inject.Inject;
import javax.inject.Singleton;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
@Singleton
public class SocketManager {
private final OkHttpClient client;
private final TokenManager tokenManager;
private WebSocket webSocket;
private boolean isConnected = false;
@Inject
public SocketManager(OkHttpClient client, TokenManager tokenManager) {
this.client = client;
this.tokenManager = tokenManager;
}
public synchronized void connect() {
String token = tokenManager.getToken();
if (token == null) {
Log.d("SOCKET", "Token null → no connection");
return;
}
if (isConnected) return;
Request request = new Request.Builder()
.url("ws://boxer-adapting-bluegill.ngrok-free.app/ws")
.addHeader("Authorization", "Bearer " + token)
.build();
webSocket = client.newWebSocket(request, new WebSocketListener() {
@Override
public void onOpen(WebSocket webSocket, Response response) {
isConnected = true;
Log.d("SOCKET", "Connected");
}
@Override
public void onMessage(WebSocket webSocket, String text) {
Log.d("SOCKET", "Message: " + text);
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
isConnected = false;
Log.e("SOCKET", "Error: " + t.getMessage());
// tentative de reconnexion
reconnect();
}
@Override
public void onClosed(WebSocket webSocket, int code, String reason) {
isConnected = false;
}
});
}
public synchronized void reconnect() {
disconnect();
connect();
}
public synchronized void disconnect() {
if (webSocket != null) {
webSocket.close(1000, "Closing");
webSocket = null;
}
isConnected = false;
}
}

View File

@@ -0,0 +1,285 @@
package com.example.quiz.data.remote;
import android.util.Log;
import com.example.quiz.data.model.Course;
import com.example.quiz.data.model.dtos.NotifPayload;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import ua.naiksoftware.stomp.Stomp;
import ua.naiksoftware.stomp.StompClient;
import ua.naiksoftware.stomp.dto.StompHeader;
@Singleton
public class StompManager {
private final TokenManager tokenManager;
private final NotificationHelper notificationHelper;
private StompClient stompClient;
private boolean isConnected = false;
// Map topic -> liste de listeners
private final Map<String, List<Consumer<String>>> topicListeners = new HashMap<>();
// Gestion des Disposables RxJava
private final CompositeDisposable compositeDisposable = new CompositeDisposable();
// Map pour stocker les subscriptions par topic
private final Map<String, Disposable> topicSubscriptions = new HashMap<>();
@Inject
public StompManager(TokenManager tokenManager,
NotificationHelper notificationHelper) {
this.tokenManager = tokenManager;
this.notificationHelper = notificationHelper;
}
// -------------------- Connexion --------------------
public synchronized void connect() {
if (isConnected) return;
String token = tokenManager.getToken();
if (token == null) {
Log.e("STOMP", "No token available");
return;
}
// URL de connexion WebSocket
String url = "wss://boxer-adapting-bluegill.ngrok-free.app/ws";
try {
// Créer le client STOMP
stompClient = Stomp.over(Stomp.ConnectionProvider.OKHTTP, url);
// Ajouter les headers d'authentification
List<StompHeader> headers = new ArrayList<>();
headers.add(new StompHeader("Authorization", "Bearer " + token));
// Observer la connexion et stocker le Disposable
Disposable lifecycleDisposable = stompClient.lifecycle()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(lifecycleEvent -> {
switch (lifecycleEvent.getType()) {
case OPENED:
isConnected = true;
Log.d("STOMP", "Connected to WebSocket");
resubscribeAll();
break;
case CLOSED:
isConnected = false;
Log.d("STOMP", "Disconnected from WebSocket");
break;
case ERROR:
isConnected = false;
Log.e("STOMP", "Error: " + lifecycleEvent.getException());
reconnect();
break;
}
}, throwable -> {
Log.e("STOMP", "Lifecycle error", throwable);
isConnected = false;
reconnect();
});
compositeDisposable.add(lifecycleDisposable);
// Se connecter
stompClient.connect(headers);
} catch (Exception e) {
Log.e("STOMP", "Connection error", e);
reconnect();
}
}
// -------------------- Souscription aux topics --------------------
public synchronized void subscribe(String topic, Consumer<String> listener) {
// Ajouter le listener
topicListeners.computeIfAbsent(topic, k -> new ArrayList<>()).add(listener);
// Si déjà connecté, souscrire immédiatement
if (isConnected && stompClient != null) {
subscribeToTopic(topic);
} else {
connect();
}
}
private void subscribeToTopic(String topic) {
if (stompClient == null) return;
// Si déjà abonné à ce topic, ne pas souscrire à nouveau
if (topicSubscriptions.containsKey(topic)) {
Log.d("STOMP", "Already subscribed to " + topic);
return;
}
// Formater le topic correctement
String destination = topic.startsWith("/topic/") ? topic : "/topic/" + topic;
Log.d("STOMP", "Subscribing to " + destination);
Disposable subscription = stompClient.topic(destination)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
stompMessage -> {
String payload = stompMessage.getPayload();
Log.d("STOMP", "Message on " + destination + ": " + payload);
// Notifier les listeners
List<Consumer<String>> listeners = topicListeners.get(topic);
if (listeners != null) {
for (Consumer<String> listener : listeners) {
try {
listener.accept(payload);
} catch (Exception e) {
Log.e("STOMP", "Listener error", e);
}
}
}
String notif = "";
Type type = new TypeToken<NotifPayload<Course>>(){}.getType();
NotifPayload<Course> notifCourse = new Gson().fromJson(payload, type);
Course updatedCourse = notifCourse.getPayload();
switch (notifCourse.getType()){
case COURSE_CREATED:
notif = "Nouvelle course "+updatedCourse.getNom()+" créée";
break;
case COURSE_UPDATED:
notif = "Course "+updatedCourse.getNom()+" modifiée";
break;
case COURSE_CANCELLED:
notif = "Course "+updatedCourse.getNom()+" annulée";
break;
case COURSE_REPORTED:
notif = "Course "+updatedCourse.getNom()+" reportée";
break;
case COURSE_CLOSED_FOR_BETTING:
notif = "Course "+updatedCourse.getNom()+" fermée aux paris!";
break;
case RUNNER_DECLARED_NON_PARTANT:
notif = "Non partants déclarés pour la course "+updatedCourse.getNom();
break;
}
// Notification
notificationHelper.showNotification(topic, notif);
},
throwable -> {
Log.e("STOMP", "Error on " + destination, throwable);
// Nettoyer en cas d'erreur
topicSubscriptions.remove(topic);
}
);
topicSubscriptions.put(topic, subscription);
compositeDisposable.add(subscription);
}
private void resubscribeAll() {
if (stompClient == null) return;
// Nettoyer les anciennes subscriptions
for (Disposable disposable : topicSubscriptions.values()) {
if (disposable != null && !disposable.isDisposed()) {
disposable.dispose();
}
}
topicSubscriptions.clear();
// Resouscrire à tous les topics
for (String topic : topicListeners.keySet()) {
subscribeToTopic(topic);
}
}
// -------------------- Envoi de messages --------------------
public void sendMessage(String destination, Object payload) {
if (!isConnected || stompClient == null) {
Log.e("STOMP", "Not connected");
return;
}
String dest = destination.startsWith("/app/") ? destination : "/app/" + destination;
Disposable sendDisposable = stompClient.send(dest, payload.toString())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
() -> Log.d("STOMP", "Message sent to " + dest),
throwable -> Log.e("STOMP", "Error sending to " + dest, throwable)
);
compositeDisposable.add(sendDisposable);
}
public void sendToCourses(Object payload) {
sendMessage("/app/courses", payload);
}
// -------------------- Désabonnement --------------------
public synchronized void unsubscribe(String topic, Consumer<String> listener) {
if (topicListeners.containsKey(topic)) {
topicListeners.get(topic).remove(listener);
if (topicListeners.get(topic).isEmpty()) {
topicListeners.remove(topic);
// Si plus de listeners, annuler la souscription STOMP
Disposable subscription = topicSubscriptions.remove(topic);
if (subscription != null && !subscription.isDisposed()) {
subscription.dispose();
compositeDisposable.remove(subscription);
}
}
}
}
// -------------------- Déconnexion --------------------
public synchronized void disconnect() {
// Nettoyer toutes les subscriptions RxJava
compositeDisposable.clear();
topicSubscriptions.clear();
if (stompClient != null) {
stompClient.disconnect();
stompClient = null;
}
isConnected = false;
topicListeners.clear();
}
private void reconnect() {
disconnect();
// Réessayer après délai
new android.os.Handler().postDelayed(() -> {
Log.d("STOMP", "Attempting to reconnect...");
connect();
}, 5000);
}
// À appeler dans le cycle de vie du fragment/activity
public void onCleanup() {
disconnect();
compositeDisposable.dispose();
}
}

View File

@@ -11,16 +11,13 @@ public class TokenManager {
private static final String PREF_NAME = "auth_pref";
private static final String KEY_TOKEN = "auth_token";
SocketManager socketManager;
private SharedPreferences sharedPreferences;
@Inject
public TokenManager(@ApplicationContext Context context, SocketManager socketManager) {
public TokenManager(@ApplicationContext Context context) {
this.sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
this.socketManager = socketManager;
}
public void saveToken(String token){
sharedPreferences.edit().putString(KEY_TOKEN, token).apply();
socketManager.reconnect();
}
public String getToken(){

View File

@@ -0,0 +1,128 @@
package com.example.quiz.data.repository;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.quiz.data.model.Course;
import com.example.quiz.data.model.Restriction;
import com.example.quiz.data.model.dtos.auth.User;
import com.example.quiz.data.remote.ApiService;
import com.example.quiz.utils.Result;
import java.util.List;
import javax.inject.Inject;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class AgentRepository {
private ApiService apiService;
@Inject
public AgentRepository(ApiService apiService) {
this.apiService = apiService;
}
public LiveData<Result<List<User>>> getAgents(String agentId) {
MutableLiveData<Result<List<User>>> liveAgents = new MutableLiveData<>();
liveAgents.setValue(Result.loading());
apiService.getAgentsByMaster(agentId).enqueue(new Callback<List<User>>() {
@Override
public void onResponse(@NonNull Call<List<User>> call, @NonNull Response<List<User>> response) {
if (response.isSuccessful()) {
liveAgents.postValue(Result.success(response.body()));
} else {
liveAgents.postValue(Result.error(response.message()));
}
}
@Override
public void onFailure(@NonNull Call<List<User>> call, @NonNull Throwable throwable) {
liveAgents.postValue(Result.error(throwable.getMessage()));
}
});
return liveAgents;
}
public LiveData<Result<User>> getAgentById(String agentId) {
MutableLiveData<Result<User>> liveAgent = new MutableLiveData<>();
liveAgent.setValue(Result.loading());
apiService.getAgent(agentId).enqueue(new Callback<User>() {
@Override
public void onResponse(@NonNull Call<User> call, @NonNull Response<User> response) {
if (response.isSuccessful()) {
liveAgent.postValue(Result.success(response.body()));
} else {
liveAgent.postValue(Result.error(response.message()));
}
}
@Override
public void onFailure(@NonNull Call<User> call, @NonNull Throwable throwable) {
liveAgent.postValue(Result.error(throwable.getMessage()));
}
});
return liveAgent;
}
public LiveData<Result<List<Course.TypeParis>>> getAvailableBets(String agentId) {
MutableLiveData<Result<List<Course.TypeParis>>> liveAvailableBets = new MutableLiveData<>();
liveAvailableBets.setValue(Result.loading());
apiService.getAvailableBets(agentId).enqueue(new Callback<List<Course.TypeParis>>() {
@Override
public void onResponse(@NonNull Call<List<Course.TypeParis>> call, @NonNull Response<List<Course.TypeParis>> response) {
if (response.isSuccessful()) {
liveAvailableBets.postValue(Result.success(response.body()));
} else {
liveAvailableBets.postValue(Result.error(response.message()));
}
}
@Override
public void onFailure(@NonNull Call<List<Course.TypeParis>> call, @NonNull Throwable throwable) {
liveAvailableBets.postValue(Result.error(throwable.getMessage()));
}
});
return liveAvailableBets;
}
public LiveData<Result<Void>> setAccess(String masterId, String slaveId, boolean access) {
MutableLiveData<Result<Void>> liveSetAccess = new MutableLiveData<>();
liveSetAccess.setValue(Result.loading());
apiService.setAccess(masterId, slaveId, access).enqueue(new Callback<Void>() {
@Override
public void onResponse(@NonNull Call<Void> call, @NonNull Response<Void> response) {
if (response.isSuccessful()) {
liveSetAccess.postValue(Result.success(null));
} else {
liveSetAccess.postValue(Result.error(response.message()));
}
}
@Override
public void onFailure(@NonNull Call<Void> call, @NonNull Throwable throwable) {
liveSetAccess.postValue(Result.error(throwable.getMessage()));
}
});
return liveSetAccess;
};
public LiveData<Result<Void>> setRestrictions(String masterId, String slaveId, Restriction restrictions) {
MutableLiveData<Result<Void>> liveSetRestrictions = new MutableLiveData<>();
liveSetRestrictions.setValue(Result.loading());
apiService.setRestrictions(masterId, slaveId, restrictions).enqueue(new Callback<Void>() {
@Override
public void onResponse(@NonNull Call<Void> call, @NonNull Response<Void> response) {
if (response.isSuccessful()) {
liveSetRestrictions.postValue(Result.success(null));
} else {
liveSetRestrictions.postValue(Result.error(response.message()));
}
}
@Override
public void onFailure(@NonNull Call<Void> call, @NonNull Throwable throwable) {
liveSetRestrictions.postValue(Result.error(throwable.getMessage()));
}
});
return liveSetRestrictions;
}
}

View File

@@ -10,8 +10,6 @@ import com.example.quiz.data.model.PagedModel;
import com.example.quiz.data.remote.ApiService;
import com.example.quiz.utils.Result;
import java.util.List;
import javax.inject.Inject;
import retrofit2.Call;
@@ -20,6 +18,7 @@ import retrofit2.Response;
public class CourseRepository {
private ApiService apiService;
private final Course.Statut OPENED_STATUT = Course.Statut.OUVERT;
@Inject
public CourseRepository(ApiService apiService) {
@@ -29,11 +28,25 @@ public class CourseRepository {
public LiveData<Result<PagedModel<Course>>> getCourses(String reunionDate) {
MutableLiveData<Result<PagedModel<Course>>> liveCourses = new MutableLiveData<Result<PagedModel<Course>>>();
liveCourses.setValue(Result.loading());
apiService.getCourses(reunionDate).enqueue(new Callback<PagedModel<Course>>() {
apiService.getCourses(reunionDate,String.valueOf(OPENED_STATUT)).enqueue(new Callback<PagedModel<Course>>() {
@Override
public void onResponse(Call<PagedModel<Course>> call, Response<PagedModel<Course>> response) {
if(response.isSuccessful()){
liveCourses.postValue(Result.success(response.body()));
// PagedModel<Course> openedPagesCourses = new PagedModel<>();
// List<Course> listOfCourses = new ArrayList<>();
// for(Course course: response.body().getContent()){
// if(course.getStatut().equals(OPENED_STATUT)){
// listOfCourses.add(course);
// }
// }
//// openedPagesCourses.setTotalPages(response.body().getTotalPages());
//// openedPagesCourses.setTotalElements(response.body().getTotalElements());
//// openedPagesCourses.setPageable(response.body().getPageable());
//// openedPagesCourses.setNumberOfElements(response.body().getNumberOfElements());
//// openedPagesCourses.setSize(response.body().getSize());
//// openedPagesCourses.setContent(listOfCourses);
// liveCourses.postValue(Result.success(response.body()));
}else{
liveCourses.postValue(Result.error(response.message()));
}

View File

@@ -5,8 +5,10 @@ import android.util.Log;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.quiz.data.model.dtos.auth.ChangePin;
import com.example.quiz.data.model.dtos.auth.LoginPayload;
import com.example.quiz.data.model.dtos.auth.LoginResponse;
import com.example.quiz.data.model.dtos.auth.User;
import com.example.quiz.data.remote.ApiService;
import com.example.quiz.data.remote.TokenManager;
import com.example.quiz.utils.Result;
@@ -20,6 +22,7 @@ import retrofit2.Response;
public class LoginRepository {
private ApiService apiService;
private TokenManager tokenManager;
@Inject
public LoginRepository(ApiService apiService, TokenManager tokenManager) {
this.tokenManager = tokenManager;
@@ -37,15 +40,38 @@ public class LoginRepository {
Log.d("TOKEN", response.body().getToken());
tokenManager.saveToken(response.body().getToken());
}else{
liveLogin.postValue(Result.error(response.message()));
liveLogin.postValue(Result.error(response.toString()));
}
}
@Override
public void onFailure(Call<LoginResponse> call, Throwable throwable) {
liveLogin.postValue(Result.error(throwable.getMessage()));
liveLogin.postValue(Result.error(throwable.toString()));
}
});
return liveLogin;
}
public LiveData<Result<User>> changePin(String oldPin, String newPin){
MutableLiveData<Result<User>> liveUser = new MutableLiveData<Result<User>>();
liveUser.setValue(Result.loading());
ChangePin changePin = new ChangePin();
changePin.setOldPin(oldPin);
changePin.setNewPin(newPin);
apiService.changePin(changePin).enqueue(new Callback<User>(){
@Override
public void onResponse(Call<User> call, Response<User> response) {
if(response.isSuccessful()) {
liveUser.postValue(Result.success(response.body()));
}else {
liveUser.postValue(Result.error(response.message()));
}
}
@Override
public void onFailure(Call<User> call, Throwable throwable) {
liveUser.postValue(Result.error(throwable.getMessage()));
}
});
return liveUser;
}
}

View File

@@ -0,0 +1,47 @@
package com.example.quiz.data.repository;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.quiz.data.model.MiseInitiale;
import com.example.quiz.data.model.PariMise;
import com.example.quiz.data.remote.ApiService;
import com.example.quiz.data.remote.TokenManager;
import com.example.quiz.utils.Result;
import java.util.List;
import javax.inject.Inject;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class PariMiseRepository {
private ApiService apiService;
@Inject
public PariMiseRepository(ApiService apiService, TokenManager tokenManager) {
this.apiService = apiService;
}
public LiveData<Result<List<MiseInitiale>>> getBetInitMise() {
MutableLiveData<Result<List<MiseInitiale>>> livePariMise = new MutableLiveData<Result<List<MiseInitiale>>>();
livePariMise.setValue(Result.loading());
apiService.getBetInitMise().enqueue(new Callback<List<MiseInitiale>>() {
@Override
public void onResponse(Call<List<MiseInitiale>> call, Response<List<MiseInitiale>> response) {
if (response.isSuccessful()) {
livePariMise.postValue(Result.success(response.body()));
}else{
livePariMise.postValue(Result.error(response.message()));
}
}
@Override
public void onFailure(Call<List<MiseInitiale>> call, Throwable throwable) {
livePariMise.postValue(Result.error(throwable.getMessage()));
}
});
return livePariMise;
}
}

View File

@@ -8,6 +8,8 @@ import androidx.lifecycle.MutableLiveData;
import com.example.quiz.data.model.Pari;
import com.example.quiz.data.model.ParisResponse;
import com.example.quiz.data.model.dtos.paris.CancelParisPaylaod;
import com.example.quiz.data.model.dtos.paris.SoldeResponse;
import com.example.quiz.data.remote.ApiService;
import com.example.quiz.utils.Result;
@@ -96,7 +98,8 @@ public class PariRepository {
public LiveData<Result<ParisResponse>> annulerPari(String numeroTicket){
MutableLiveData<Result<ParisResponse>> pariResponse = new MutableLiveData<>();
pariResponse.setValue(Result.loading());
apiService.annulerPari(numeroTicket).enqueue(new Callback<ParisResponse>(){
CancelParisPaylaod cancelParisPaylaod = new CancelParisPaylaod("ANNULE");
apiService.annulerPari(numeroTicket, cancelParisPaylaod).enqueue(new Callback<ParisResponse>(){
@Override
public void onResponse(Call<ParisResponse> call, Response<ParisResponse> response) {
if(response.isSuccessful()){
@@ -121,12 +124,12 @@ public class PariRepository {
return pariResponse;
}
public LiveData<Result<Double>> getSoldeByCourse(String agentId, String courseId){
MutableLiveData<Result<Double>> solde = new MutableLiveData<>();
public LiveData<Result<SoldeResponse>> getSoldeByCourse(String agentId, String courseId){
MutableLiveData<Result<SoldeResponse>> solde = new MutableLiveData<>();
solde.setValue(Result.loading());
apiService.getSoldeByCourse(agentId, courseId).enqueue(new Callback<Double>() {
apiService.getSoldeByCourse(agentId, courseId).enqueue(new Callback<SoldeResponse>() {
@Override
public void onResponse(Call<Double> call, Response<Double> response) {
public void onResponse(Call<SoldeResponse> call, Response<SoldeResponse> response) {
if(response.isSuccessful()){
solde.postValue(Result.success(response.body()));
}else{
@@ -139,19 +142,19 @@ public class PariRepository {
}
@Override
public void onFailure(Call<Double> call, Throwable throwable) {
public void onFailure(Call<SoldeResponse> call, Throwable throwable) {
solde.postValue(Result.error(throwable.getMessage()));
}
});
return solde;
}
public LiveData<Result<Double>> getSoldeByDay(String agentId, String day){
MutableLiveData<Result<Double>> solde = new MutableLiveData<>();
public LiveData<Result<SoldeResponse>> getSoldeByDay(String agentId, String day){
MutableLiveData<Result<SoldeResponse>> solde = new MutableLiveData<>();
solde.setValue(Result.loading());
apiService.getSoldeByDay(agentId, day).enqueue(new Callback<Double>() {
apiService.getSoldeByDay(agentId, day).enqueue(new Callback<SoldeResponse>() {
@Override
public void onResponse(Call<Double> call, Response<Double> response) {
public void onResponse(Call<SoldeResponse> call, Response<SoldeResponse> response) {
if(response.isSuccessful()){
solde.postValue(Result.success(response.body()));
}else{
@@ -164,7 +167,7 @@ public class PariRepository {
}
@Override
public void onFailure(Call<Double> call, Throwable throwable) {
public void onFailure(Call<SoldeResponse> call, Throwable throwable) {
solde.postValue(Result.error(throwable.getMessage()));
}
});

View File

@@ -28,21 +28,24 @@ public class AuthNavigator {
private FragmentManager fragmentManager;
private TokenManager tokenManager;
private final SessionManager sessionManager;
LoginViewModel viewModel;
LogsViewModel logsViewModel;
private LoginViewModel viewModel;
private LogsViewModel logsViewModel;
private LifecycleOwner lifecycleOwner;
private Dialog dialog;
SharedPrefsHelper prefsHelper;
public AuthNavigator(Context context,
FragmentManager fragmentManager,
SessionManager sessionManager,
LoginViewModel viewModel,
LogsViewModel logsViewModel) {
LogsViewModel logsViewModel,
LifecycleOwner lifecycleOwner) {
this.viewModel = viewModel;
this.logsViewModel = logsViewModel;
this.prefsHelper = SharedPrefsHelper.getInstance(context);
@@ -50,6 +53,7 @@ public class AuthNavigator {
this.context = context;
dialog = new Dialog(context);
this.sessionManager = sessionManager;
this.lifecycleOwner = lifecycleOwner;
}
public void navigate(Fragment destinationFragment) {
@@ -98,12 +102,15 @@ public class AuthNavigator {
pin.setError("Le pin doit contenir minimum quatre chiffres!");
return;
}
if(prefsHelper.get("terminalId") == null){
MessageDialog.showError(context, "Terminal non trouvé");
}
LoginPayload loginPayload = new LoginPayload(
code.getText().toString(),
pin.getText().toString(),
Long.valueOf(prefsHelper.get("terminalId"))
);
viewModel.login(loginPayload).observe((LifecycleOwner) context, new Observer<Result<LoginResponse>>() {
viewModel.login(loginPayload).observe(lifecycleOwner, new Observer<Result<LoginResponse>>() {
@Override
public void onChanged(Result<LoginResponse> loginResponseResult) {
switch (loginResponseResult.status){
@@ -137,11 +144,18 @@ public class AuthNavigator {
}
private void loginSuccess(LoginResponse loginResponse){
String terminalId = prefsHelper.get("terminalId");
if(terminalId == null){
MessageDialog.showError(context, "Terminal non trouvé");
return;
}
prefsHelper.save("id", String.valueOf(loginResponse.getUser().getId()));
prefsHelper.save("firstName", loginResponse.getUser().getPrenom());
prefsHelper.save("lastName", loginResponse.getUser().getNom());
prefsHelper.save("profile", loginResponse.getUser().getFonction());
prefsHelper.save("code", loginResponse.getUser().getCode());
String isSupAgent = loginResponse.getUser().isSubAgent() ? "true" : "false";
prefsHelper.save("isSupAgent", isSupAgent);
}
}

View File

@@ -0,0 +1,56 @@
package com.example.quiz.viewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.example.quiz.data.model.Course;
import com.example.quiz.data.model.Restriction;
import com.example.quiz.data.model.dtos.auth.User;
import com.example.quiz.data.repository.AgentRepository;
import com.example.quiz.utils.Result;
import java.util.List;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class AgentViewModel extends ViewModel {
private final AgentRepository agentRepository;
private LiveData<Result<List<User>>> agents;
private LiveData<Result<User>> agentDetails;
private LiveData<Result<List<Course.TypeParis>>> availableBets;
@Inject
public AgentViewModel(AgentRepository agentRepository) {
this.agentRepository = agentRepository;
}
public LiveData<Result<List<User>>> getAgents(String agentId) {
agents = agentRepository.getAgents(agentId);
return agents;
}
public LiveData<Result<User>> getAgentById(String agentId) {
agentDetails = agentRepository.getAgentById(agentId);
return agentDetails;
}
public LiveData<Result<List<Course.TypeParis>>> getAvailableBets(String agentId) {
availableBets = agentRepository.getAvailableBets(agentId);
return availableBets;
}
public LiveData<Result<Void>> setAccess(String masterId, String slaveId, boolean access) {
return agentRepository.setAccess(masterId, slaveId, access);
};
public LiveData<Result<Void>> setRestrictions(String masterId, String slaveId, Restriction restrictions) {
return agentRepository.setRestrictions(masterId, slaveId, restrictions);
}
}

View File

@@ -1,10 +1,13 @@
package com.example.quiz.viewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.example.quiz.data.model.dtos.auth.ChangePin;
import com.example.quiz.data.model.dtos.auth.LoginPayload;
import com.example.quiz.data.model.dtos.auth.LoginResponse;
import com.example.quiz.data.model.dtos.auth.User;
import com.example.quiz.data.repository.LoginRepository;
import com.example.quiz.utils.Result;
@@ -16,6 +19,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel;
public class LoginViewModel extends ViewModel {
LoginRepository loginRepository;
LiveData<Result<User>> user = new MutableLiveData<>();
@Inject
public LoginViewModel(LoginRepository loginRepository) {
this.loginRepository = loginRepository;
@@ -24,4 +28,9 @@ public class LoginViewModel extends ViewModel {
public LiveData<Result<LoginResponse>> login(LoginPayload loginPayload){
return loginRepository.login(loginPayload);
}
public LiveData<Result<User>> changePin(String oldPin, String newPin){
user = loginRepository.changePin(oldPin, newPin);
return user;
}
}

View File

@@ -0,0 +1,4 @@
package com.example.quiz.viewModel;
public class NotificationViewModel {
}

View File

@@ -0,0 +1,33 @@
package com.example.quiz.viewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.example.quiz.data.model.MiseInitiale;
import com.example.quiz.data.model.PariMise;
import com.example.quiz.data.repository.PariMiseRepository;
import com.example.quiz.utils.Result;
import java.util.List;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class PariMiseViewModel extends ViewModel {
private final PariMiseRepository pariMiseRepository;
private LiveData<Result<List<MiseInitiale>>> pariMise;
@Inject
public PariMiseViewModel(PariMiseRepository pariMiseRepository) {
this.pariMiseRepository = pariMiseRepository;
}
public LiveData<Result<List<MiseInitiale>>> getBetInitMise() {
this.pariMise = pariMiseRepository.getBetInitMise();
return this.pariMise;
}
}

View File

@@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModel;
import com.example.quiz.data.model.Pari;
import com.example.quiz.data.model.ParisResponse;
import com.example.quiz.data.model.dtos.paris.SoldeResponse;
import com.example.quiz.data.repository.PariRepository;
import com.example.quiz.utils.Result;
@@ -23,9 +24,9 @@ public class PariViewModel extends ViewModel {
private LiveData<Result<ParisResponse>> pariAnnule;
private LiveData<Result<Double>> solde;
private LiveData<Result<SoldeResponse>> solde;
private LiveData<Result<Double>> soldeByDay;
private LiveData<Result<SoldeResponse>> soldeByDay;
@Inject
public PariViewModel(PariRepository repository){
@@ -47,12 +48,12 @@ public class PariViewModel extends ViewModel {
return pariAnnule;
}
public LiveData<Result<Double>> getSoldeByCourse(String agentId, String courseId){
public LiveData<Result<SoldeResponse>> getSoldeByCourse(String agentId, String courseId){
solde = pariRepository.getSoldeByCourse(agentId, courseId);
return solde;
}
public LiveData<Result<Double>> getSoldeByDay(String agentId, String day){
public LiveData<Result<SoldeResponse>> getSoldeByDay(String agentId, String day){
soldeByDay = pariRepository.getSoldeByDay(agentId, day);
return soldeByDay;
}

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/transparent" />
<corners android:radius="12dp" />
<stroke
android:width="1dp"
android:color="#E2E8F0" />
</shape>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#F0F0F0" />
<size
android:width="48dp"
android:height="48dp" />
</shape>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/primary_green" />
<size
android:width="6dp"
android:height="6dp" />
</shape>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM12,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5 5,-2.24 5,-5 -2.24,-5 -5,-5z" />
</vector>

View File

@@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M4,12.611L8.923,17.5L20,6.5"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
</vector>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#9CA3AF"
android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zM12,11.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z" />
</vector>

View File

@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="300dp"
android:height="300dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M14.958,19.003C14.72,20.421 13.486,21.501 12,21.501C10.576,21.501 9.384,20.509 9.077,19.178L9.042,19.003L14.958,19.003ZM12,2.004C16.142,2.004 19.5,5.362 19.5,9.504L19.5,13.502L20.918,16.662C20.973,16.785 21.002,16.917 21.002,17.051C21.002,17.576 20.576,18.001 20.052,18.001L3.952,18.001C3.818,18.001 3.686,17.973 3.564,17.919C3.085,17.704 2.871,17.142 3.085,16.663L4.5,13.503L4.5,9.491L4.505,9.241C4.644,5.21 7.956,2.004 12,2.004ZM10.5,9.5L8.5,9.5L8.398,9.507C8.032,9.557 7.75,9.87 7.75,10.25C7.75,10.63 8.032,10.943 8.398,10.993L8.5,11L9.208,11L7.849,13.378L7.803,13.473C7.622,13.921 7.916,14.427 8.395,14.493L8.5,14.5L10.5,14.5L10.602,14.493C10.968,14.443 11.25,14.13 11.25,13.75C11.25,13.37 10.968,13.057 10.602,13.007L10.5,13L9.792,13L11.151,10.622L11.197,10.527C11.378,10.079 11.084,9.573 10.605,9.507L10.5,9.5ZM15,6.5L12.5,6.5L12.398,6.507C12.032,6.557 11.75,6.87 11.75,7.25C11.75,7.63 12.032,7.943 12.398,7.993L12.5,8L13.725,8L11.844,11.386L11.8,11.481C11.625,11.928 11.919,12.428 12.395,12.493L12.5,12.5L15,12.5L15.102,12.493C15.468,12.443 15.75,12.13 15.75,11.75C15.75,11.37 15.468,11.057 15.102,11.007L15,11L13.775,11L15.656,7.614L15.7,7.519C15.875,7.072 15.581,6.572 15.105,6.507L15,6.5Z"
android:strokeWidth="1"
android:fillColor="#212121"
android:fillType="nonZero"
android:strokeColor="#0b9602"/>
</vector>

View File

@@ -0,0 +1,276 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="150dp"
android:height="150dp"
android:viewportWidth="225"
android:viewportHeight="225">
<path
android:pathData="M162,76C169.1,75.16 173.27,76.11 178.98,80.43C179.73,81.03 180.48,81.63 181.25,82.25C182.78,83.42 184.3,84.59 185.83,85.77C186.9,86.6 186.9,86.6 187.98,87.46C189.77,88.82 191.57,90.09 193.45,91.32C196,93 196,93 197.5,94.56C199.69,96.66 202.27,97.73 205,99C205,99.99 205,100.98 205,102C206.65,102.33 208.3,102.66 210,103C210.08,105.25 210.14,107.5 210.19,109.75C210.24,111.63 210.24,111.63 210.29,113.55C210,117 210,117 209.06,119.36C205.72,122.01 201.97,122.05 197.81,122.5C191.42,123.21 185.31,124.06 180,128C179.67,128.99 179.34,129.98 179,131C181.31,132.98 183.62,134.96 186,137C185.51,137.99 185.51,137.99 185,139C183.35,138.67 181.7,138.34 180,138C179.67,137.01 179.34,136.02 179,135C176.5,133.78 176.5,133.78 174,133C173.55,133.66 173.09,134.32 172.63,135C172.09,135.66 171.55,136.32 171,137C170.34,137 169.68,137 169,137C168.92,137.93 168.84,138.86 168.75,139.81C168,143 168,143 165.94,144.88C165.3,145.25 164.66,145.62 164,146C163.01,145.51 163.01,145.51 162,145C161.01,145.33 160.02,145.66 159,146C156,146 156,146 153.94,144.38C151.74,141.69 150.91,139.33 150,136C148.93,136.19 147.85,136.37 146.75,136.56C143.6,136.93 141.9,137.04 139,136C138.51,133.03 138.51,133.03 138,130C134.45,131.4 134.45,131.4 131,133C129.46,133.07 127.92,133.08 126.38,133.06C125.15,133.05 125.15,133.05 123.9,133.04C123.27,133.02 122.65,133.01 122,133C122,132.01 122,131.02 122,130C120.51,130.49 120.51,130.49 119,131C112.29,131.62 107.1,130.83 101,128C98.47,125.47 98.67,124.58 98.63,121.06C98.71,114.63 99.24,98.76 104,94C105.69,93.93 107.38,93.92 109.06,93.94C109.98,93.95 110.9,93.96 111.85,93.96C112.56,93.98 113.27,93.99 114,94C112.87,100.89 111.69,107.77 110.37,114.63C109.9,117.26 109.9,117.26 110,121C113.3,121 116.6,121 120,121C121.59,117.73 122.29,114.62 122.82,111.06C122.97,110.15 123.11,109.23 123.26,108.29C123.54,106.39 123.81,104.48 124.06,102.57C125.17,95.78 125.17,95.78 127.64,93.46C130.04,92.04 132.17,91 135,91C135,90.34 135,89.68 135,89C139.62,86.78 142.77,85.62 148,86C148.33,85.01 148.66,84.02 149,83C154.71,83.43 158.41,85.84 163,89C162.75,88.16 162.75,88.16 162.5,87.3C161.68,83.55 161.94,79.82 162,76ZM164,106C162.46,108.41 161.9,109.5 162.46,112.36C162.76,113.15 163.06,113.94 163.38,114.75C163.67,115.55 163.97,116.35 164.27,117.17C164.51,117.78 164.75,118.38 165,119C165.33,119 165.66,119 166,119C165.67,114.71 165.34,110.42 165,106C164.67,106 164.34,106 164,106ZM131,122C129.02,124.97 129.02,124.97 127,128C127.99,128.49 127.99,128.49 129,129C130.65,127.68 132.3,126.36 134,125C133.01,124.01 132.02,123.02 131,122Z"
android:fillColor="#F5F01A"/>
<path
android:pathData="M162,76C169.1,75.16 173.27,76.11 178.98,80.43C179.73,81.03 180.48,81.63 181.25,82.25C182.78,83.42 184.3,84.59 185.83,85.77C186.9,86.6 186.9,86.6 187.98,87.46C189.77,88.82 191.57,90.09 193.45,91.32C196,93 196,93 197.5,94.56C199.69,96.66 202.27,97.73 205,99C205,99.99 205,100.98 205,102C206.65,102.33 208.3,102.66 210,103C210.08,105.25 210.14,107.5 210.19,109.75C210.24,111.63 210.24,111.63 210.29,113.55C210,117 210,117 209.06,119.36C205.72,122.01 201.97,122.05 197.81,122.5C191.28,123.22 185.54,124.25 180,128C179.67,128.66 179.34,129.32 179,130C174.13,129.58 172.21,127.55 169,124C168.49,123.61 167.97,123.22 167.44,122.82C165.18,119.96 165,116.71 164.56,113.19C164.39,112.14 164.39,112.14 164.21,111.07C163.71,107.2 163.83,104.7 165.96,101.49C166.64,100.67 167.31,99.85 168,99C172.47,100.42 176.71,102.11 181,104C180.02,101.99 179.01,99.99 178,98C177.55,97.1 177.55,97.1 177.09,96.18C174.94,92.15 172.74,89.6 169,87C168.67,87.99 168.34,88.98 168,90C166.68,90 165.36,90 164,90C163.67,90.66 163.34,91.32 163,92C162.08,86.61 161.91,81.46 162,76Z"
android:fillColor="#F3EE18"/>
<path
android:pathData="M169,87C169.99,87.33 170.98,87.66 172,88C172,88.66 172,89.32 172,90C172.6,90.21 173.2,90.41 173.81,90.63C176.83,92.52 177.56,94.8 179,98C179.66,98.99 180.32,99.98 181,101C181,101.99 181,102.98 181,104C175.72,103.48 172.43,101.75 168,99C167.73,99.7 167.46,100.4 167.19,101.13C166,104 166,104 164.19,106.75C162.68,110.88 163.34,112.79 164.95,116.78C168.38,124.01 172.84,127.62 179.45,131.89C182.35,133.77 184.05,135.07 186,138C183.52,138.49 183.52,138.49 181,139C180.77,138.38 180.55,137.76 180.31,137.13C178.62,134.39 177.01,133.95 174,133C173.55,133.66 173.09,134.32 172.63,135C172.09,135.66 171.55,136.32 171,137C170.34,137 169.68,137 169,137C168.92,137.93 168.84,138.86 168.75,139.81C168,143 168,143 165.94,144.88C165.3,145.25 164.66,145.62 164,146C163.34,145.67 162.68,145.34 162,145C160.51,145.49 160.51,145.49 159,146C156,146 156,146 153.94,144.38C151.74,141.69 150.91,139.33 150,136C148.93,136.19 147.85,136.37 146.75,136.56C143.6,136.93 141.9,137.04 139,136C136.7,122.89 136.7,122.89 138,119C141.96,119.33 145.92,119.66 150,120C149,124 149,124 147,127C145.51,127.49 145.51,127.49 144,128C143.67,128.66 143.34,129.32 143,130C145.31,129.34 147.62,128.68 150,128C150.12,127.24 150.25,126.47 150.38,125.69C151,123 151,123 153,120C153.24,117.43 153.24,117.43 152,115C148.53,113.13 148.53,113.13 145,112C145.49,107.54 145.49,107.54 146,103C146.66,103 147.32,103 148,103C148.33,102.01 148.66,101.02 149,100C151.45,97.46 152.75,97.02 156.31,96.75C159.92,96.99 163.43,97.42 167,98C167,96.68 167,95.36 167,94C166.01,93.34 165.02,92.68 164,92C164,91.34 164,90.68 164,90C164.64,89.88 165.28,89.75 165.94,89.63C166.62,89.42 167.3,89.21 168,89C168.33,88.34 168.66,87.68 169,87Z"
android:fillColor="#AF1505"/>
<path
android:pathData="M64.5,93.9C66.02,93.92 66.02,93.92 67.56,93.94C68.57,93.95 69.59,93.96 70.63,93.96C71.8,93.98 71.8,93.98 73,94C73.2,95.2 73.41,96.41 73.62,97.65C73.89,99.22 74.17,100.8 74.44,102.38C74.57,103.17 74.71,103.96 74.85,104.78C75.51,109.01 75.51,109.01 77,113C77.28,112.42 77.55,111.85 77.84,111.26C85.9,94.83 85.9,94.83 91.18,92.74C94.01,92.81 96.28,93.28 99,94C97.68,102.27 96.15,110.49 94.44,118.69C94.22,119.77 94,120.85 93.77,121.97C93.55,122.99 93.34,124.02 93.12,125.07C92.83,126.46 92.83,126.46 92.54,127.87C92,130 92,130 91,131C88.33,131.14 85.68,131.04 83,131C81.46,127.92 82.63,125.65 83.44,122.38C83.72,121.19 84.01,120 84.31,118.77C84.54,117.86 84.76,116.94 85,116C84.13,117.62 84.13,117.62 83.24,119.28C82.45,120.69 81.67,122.09 80.88,123.5C80.5,124.22 80.12,124.93 79.73,125.67C79.34,126.34 78.95,127.02 78.55,127.72C78.21,128.35 77.86,128.98 77.51,129.62C76,131 76,131 70,131C69.01,125.72 68.02,120.44 67,115C66.05,119.89 66.05,119.89 65.12,124.78C64.11,129.89 64.11,129.89 63,131C61.48,131.07 59.96,131.08 58.44,131.06C57.61,131.05 56.78,131.04 55.93,131.04C55.3,131.02 54.66,131.01 54,131C54.79,126.69 55.58,122.37 56.38,118.06C56.6,116.85 56.82,115.64 57.05,114.39C60.8,94.05 60.8,94.05 64.5,93.9Z"
android:fillColor="#0F390F"/>
<path
android:pathData="M106.29,93.9C107.66,93.92 107.66,93.92 109.06,93.94C109.98,93.95 110.9,93.96 111.85,93.96C112.56,93.98 113.27,93.99 114,94C112.87,100.89 111.69,107.77 110.37,114.63C109.9,117.26 109.9,117.26 110,121C113.3,121 116.6,121 120,121C121.43,117.65 122.41,114.5 123.07,110.92C123.24,109.99 123.42,109.06 123.6,108.1C123.86,106.66 123.86,106.66 124.13,105.19C124.31,104.21 124.49,103.23 124.68,102.22C125.12,99.81 125.56,97.41 126,95C129.3,95 132.6,95 136,95C135.6,101.94 134.63,108.64 133.25,115.44C133.11,116.22 132.96,117 132.81,117.8C131.98,121.68 131.43,123.57 128.56,126.43C121.93,130.5 116.77,131.92 109,131C105.14,130 101.87,128.87 99,126C97.4,115.75 100.49,104.89 103,95C104,94 104,94 106.29,93.9Z"
android:fillColor="#10370D"/>
<path
android:pathData="M28,94C31.21,93.91 34.42,93.86 37.63,93.81C38.53,93.79 39.43,93.76 40.36,93.74C46.61,93.67 51.05,94.35 55.88,98.69C57.54,102.11 57.65,105.31 57,109C54.98,112.75 53.55,114.64 50,117C47.26,117.46 44.57,117.83 41.81,118.13C41.06,118.21 40.31,118.3 39.54,118.39C37.69,118.6 35.85,118.8 34,119C33.01,122.96 32.02,126.92 31,131C27.7,131 24.4,131 21,131C21.69,123.59 23.01,116.41 24.5,109.13C24.84,107.44 24.84,107.44 25.18,105.71C26.86,97.42 26.86,97.42 28,94ZM36,103C35.67,104.98 35.34,106.96 35,109C36.62,109.05 38.25,109.09 39.88,109.13C40.78,109.15 41.68,109.17 42.62,109.2C45.19,109.24 45.19,109.24 47,107C46.67,105.68 46.34,104.36 46,103C42.7,103 39.4,103 36,103Z"
android:fillColor="#0F380E"/>
<path
android:pathData="M185,88C190.11,88.85 193.86,90.77 197.5,94.56C199.69,96.66 202.27,97.73 205,99C205,99.99 205,100.98 205,102C207.48,102.5 207.48,102.5 210,103C210.08,105.25 210.14,107.5 210.19,109.75C210.24,111.63 210.24,111.63 210.29,113.55C210,117 210,117 209.06,119.36C205.72,122.01 201.97,122.05 197.81,122.5C191.28,123.22 185.54,124.25 180,128C179.67,128.66 179.34,129.32 179,130C178.34,130 177.68,130 177,130C177.31,129.4 177.62,128.8 177.94,128.19C179.1,125.91 179.1,125.91 180,123C182.56,121.31 182.56,121.31 185,120C184.87,118.92 184.87,118.92 184.75,117.82C183.76,107.8 184.31,98.03 185,88Z"
android:fillColor="#EDD617"/>
<path
android:pathData="M155,119C159.23,119.6 162.35,122.05 165.75,124.44C166.35,124.85 166.95,125.25 167.57,125.67C169.09,126.73 170.55,127.86 172,129C172,129.66 172,130.32 172,131C172.66,131 173.32,131 174,131C173.19,133.44 173.19,133.44 172,136C171.01,136.33 170.02,136.66 169,137C168.92,137.93 168.84,138.86 168.75,139.81C168,143 168,143 165.94,144.88C165.3,145.25 164.66,145.62 164,146C163.34,145.67 162.68,145.34 162,145C161.01,145.33 160.02,145.66 159,146C158.67,145.01 158.34,144.02 158,143C156.68,143 155.36,143 154,143C150.62,134.35 152.4,127.57 155,119Z"
android:fillColor="#ADAD1E"/>
<path
android:pathData="M169,87C169.99,87.33 170.98,87.66 172,88C172,88.66 172,89.32 172,90C172.6,90.21 173.2,90.41 173.81,90.63C176.83,92.52 177.56,94.8 179,98C179.99,99.49 179.99,99.49 181,101C181,101.99 181,102.98 181,104C175.72,103.48 172.43,101.75 168,99C167.71,99.78 167.42,100.57 167.13,101.38C166,104 166,104 164,106C162.68,106 161.36,106 160,106C160.13,106.59 160.27,107.18 160.4,107.79C161.31,111.85 162.21,115.91 163,120C162.01,120 161.02,120 160,120C159.51,118.51 159.51,118.51 159,117C156.98,116.27 156.98,116.27 155,116C155,115.34 155,114.68 155,114C154.34,114 153.68,114 153,114C153,113.34 153,112.68 153,112C152.34,112 151.68,112 151,112C151,110.35 151,108.7 151,107C150.01,106.67 149.02,106.34 148,106C150.08,98.82 150.08,98.82 153,97C157.77,96.63 162.29,97.23 167,98C167,96.68 167,95.36 167,94C166.01,93.34 165.02,92.68 164,92C164,91.34 164,90.68 164,90C164.64,89.88 165.28,89.75 165.94,89.63C166.62,89.42 167.3,89.21 168,89C168.33,88.34 168.66,87.68 169,87Z"
android:fillColor="#CA0503"/>
<path
android:pathData="M195,94C198.3,95.65 201.6,97.3 205,99C205,99.99 205,100.98 205,102C207.48,102.5 207.48,102.5 210,103C210.08,105.25 210.14,107.5 210.19,109.75C210.24,111.63 210.24,111.63 210.29,113.55C210,117 210,117 209.06,119.36C205.72,122.01 201.97,122.05 197.81,122.5C191.28,123.22 185.54,124.25 180,128C179.67,128.66 179.34,129.32 179,130C178.34,130 177.68,130 177,130C178.74,125.81 180.76,124.62 184.76,122.75C188.29,121.57 191.3,121.74 195,122C196.25,117.16 196.15,112.4 196.13,107.44C196.13,106.59 196.13,105.74 196.14,104.86C196.13,103.63 196.13,103.63 196.13,102.37C196.13,101.63 196.13,100.88 196.13,100.12C196.04,97.66 196.04,97.66 195,94Z"
android:fillColor="#B18518"/>
<path
android:pathData="M176,92C176.99,92.33 177.98,92.66 179,93C179.33,94.65 179.66,96.3 180,98C180.99,98.33 181.98,98.66 183,99C183,100.98 183,102.96 183,105C182.03,105.29 181.06,105.58 180.06,105.88C176.89,106.64 176.89,106.64 176,109C175.93,111.14 175.92,113.29 175.94,115.44C175.95,116.59 175.96,117.74 175.96,118.93C175.98,119.95 175.99,120.96 176,122C176,122.99 176,123.98 176,125C177.32,125.33 178.64,125.66 180,126C179.01,127.32 178.02,128.64 177,130C173.17,128.44 170.82,125.97 168,123C167.34,122.34 166.68,121.68 166,121C165.42,118.39 164.98,115.89 164.63,113.25C164.51,112.55 164.39,111.84 164.26,111.12C163.73,107.2 163.73,104.84 165.96,101.48C166.63,100.66 167.31,99.84 168,99C172.47,100.42 176.71,102.11 181,104C179.46,100.86 177.94,97.91 176,95C176,94.01 176,93.02 176,92Z"
android:fillColor="#E8E81C"/>
<path
android:pathData="M138,119C141.96,119.33 145.92,119.66 150,120C149,124 149,124 147,127C146.01,127.33 145.02,127.66 144,128C143.67,128.66 143.34,129.32 143,130C145.31,129.34 147.62,128.68 150,128C150,129.32 150,130.64 150,132C148.68,132.33 147.36,132.66 146,133C148.97,133.49 148.97,133.49 152,134C154,139.63 154,139.63 154,143C155.32,143.33 156.64,143.66 158,144C157.01,144.66 156.02,145.32 155,146C152.36,142.87 151.16,139.91 150,136C148.93,136.19 147.85,136.37 146.75,136.56C143.6,136.93 141.9,137.04 139,136C136.7,122.89 136.7,122.89 138,119Z"
android:fillColor="#29420C"/>
<path
android:pathData="M169,87C169.99,87.33 170.98,87.66 172,88C172,88.66 172,89.32 172,90C172.6,90.21 173.2,90.41 173.81,90.63C176.83,92.52 177.56,94.8 179,98C179.99,99.49 179.99,99.49 181,101C181,101.99 181,102.98 181,104C175.72,103.48 172.43,101.75 168,99C167.34,100.65 166.68,102.3 166,104C165.67,102.68 165.34,101.36 165,100C160.38,100 155.76,100 151,100C151.66,99.01 152.32,98.02 153,97C157.77,96.63 162.29,97.23 167,98C167,96.68 167,95.36 167,94C166.01,93.34 165.02,92.68 164,92C164,91.34 164,90.68 164,90C164.64,89.88 165.28,89.75 165.94,89.63C166.62,89.42 167.3,89.21 168,89C168.33,88.34 168.66,87.68 169,87Z"
android:fillColor="#B81403"/>
<path
android:pathData="M149,83C155.06,83.3 158.51,86.28 163,90C162.67,90.66 162.34,91.32 162,92C161.34,91.67 160.68,91.34 160,91C160,90.34 160,89.68 160,89C157.52,88.5 157.52,88.5 155,88C155,87.34 155,86.68 155,86C154.01,86 153.02,86 152,86C151.67,86.66 151.34,87.32 151,88C149.35,88 147.7,88 146,88C145.67,88.99 145.34,89.98 145,91C143.68,91 142.36,91 141,91C141,92.65 141,94.3 141,96C140.01,96.33 139.02,96.66 138,97C137.89,97.59 137.78,98.19 137.67,98.8C136.43,105.3 134.81,111.64 133,118C131.99,114.83 132.06,112.69 132.78,109.45C132.96,108.61 133.14,107.76 133.33,106.89C133.53,106.02 133.73,105.15 133.94,104.25C134.13,103.36 134.33,102.47 134.53,101.56C135.01,99.37 135.5,97.19 136,95C133.03,95 130.06,95 127,95C130.09,91.91 130.92,91.55 135,91C135,90.34 135,89.68 135,89C139.62,86.78 142.77,85.62 148,86C148.33,85.01 148.66,84.02 149,83Z"
android:fillColor="#D8DE3A"/>
<path
android:pathData="M193,93C195.38,94.76 195.98,95.91 196.69,98.81C198.35,114.98 198.35,114.98 195,122C192.69,122 190.38,122 188,122C188.33,121.34 188.66,120.68 189,120C189.66,120 190.32,120 191,120C190.67,111.75 190.34,103.5 190,95C190.99,95 191.98,95 193,95C193,94.34 193,93.68 193,93Z"
android:fillColor="#DBB213"/>
<path
android:pathData="M168,99C170.02,99.6 172.02,100.27 174,101C174.33,101.66 174.66,102.32 175,103C173.68,103 172.36,103 171,103C171.33,103.66 171.66,104.32 172,105C172.08,106.38 172.11,107.75 172.1,109.13C172.09,109.94 172.09,110.74 172.09,111.57C172.08,112.42 172.07,113.26 172.06,114.13C172.06,114.97 172.05,115.82 172.05,116.7C172.04,118.8 172.02,120.9 172,123C170.68,123 169.36,123 168,123C165.17,120.17 165.14,117.07 164.63,113.25C164.51,112.55 164.39,111.84 164.26,111.12C163.73,107.2 163.73,104.84 165.96,101.48C166.63,100.66 167.31,99.84 168,99Z"
android:fillColor="#D5CB20"/>
<path
android:pathData="M149,100C150,103 150,103 149,106C149.99,106 150.98,106 152,106C151.67,107.98 151.34,109.96 151,112C151.66,112 152.32,112 153,112C153,112.66 153,113.32 153,114C153.66,114 154.32,114 155,114C155.33,114.66 155.66,115.32 156,116C157.32,116 158.64,116 160,116C160,116.99 160,117.98 160,119C158.35,119 156.7,119 155,119C154.88,119.8 154.75,120.61 154.63,121.44C153.88,125.66 152.95,129.82 152,134C148.37,133.67 144.74,133.34 141,133C141,132.67 141,132.34 141,132C143.97,132 146.94,132 150,132C149.94,130.95 149.88,129.9 149.81,128.81C150.01,124.73 150.95,123.39 153,120C153.33,117.33 153.33,117.33 152,115C148.53,113.13 148.53,113.13 145,112C145.33,109.03 145.66,106.06 146,103C146.66,103 147.32,103 148,103C148.33,102.01 148.66,101.02 149,100Z"
android:fillColor="#BC1703"/>
<path
android:pathData="M199,98C200.65,98 202.3,98 204,98C204.33,99.32 204.66,100.64 205,102C206.65,102.33 208.3,102.66 210,103C210.08,105.25 210.14,107.5 210.19,109.75C210.22,111 210.26,112.26 210.29,113.55C209.98,117.2 209.76,118.61 207,121C204.3,121.61 204.3,121.61 201.31,121.75C200.32,121.81 199.32,121.86 198.3,121.92C197.54,121.95 196.78,121.97 196,122C197,120 197,120 200.06,118.88C201.52,118.44 201.52,118.44 203,118C204.15,113.06 204.54,108.04 205,103C204.34,103 203.68,103 203,103C202.9,102.36 202.79,101.72 202.69,101.06C202.35,100.04 202.35,100.04 202,99C201.01,98.67 200.02,98.34 199,98Z"
android:fillColor="#AE5E0F"/>
<path
android:pathData="M187,130C187.56,130.19 188.11,130.37 188.69,130.56C191.45,131.08 193.25,130.56 196,130C198.67,129.91 201.32,129.95 204,130C203.67,131.65 203.34,133.3 203,135C203.99,135 204.98,135 206,135C206.33,133.35 206.66,131.7 207,130C207.66,130 208.32,130 209,130C208.67,132.31 208.34,134.62 208,137C200.74,137 193.48,137 186,137C186.33,134.69 186.66,132.38 187,130ZM194,132C195,134 195,134 195,134Z"
android:fillColor="#0A1607"/>
<path
android:pathData="M145,112C151.75,113.75 151.75,113.75 154,116C153.63,120.33 151.9,124.13 150,128C147,130 147,130 143,130C143.33,129.01 143.66,128.02 144,127C144.99,126.67 145.98,126.34 147,126C148.71,123 148.71,123 150,120C148.45,120.06 148.45,120.06 146.88,120.13C143.13,120 140.46,119.35 137,118C137.33,117.34 137.66,116.68 138,116C139.65,116 141.3,116 143,116C143,115.34 143,114.68 143,114C143.66,114 144.32,114 145,114C145,113.34 145,112.68 145,112Z"
android:fillColor="#F4D542"/>
<path
android:pathData="M80,108C80.66,108.33 81.32,108.66 82,109C77.89,117.51 77.89,117.51 74.19,119.56C73.47,119.71 72.74,119.85 72,120C72,121.32 72,122.64 72,124C72,125.67 72,127.33 72,129C76.13,127.51 77.3,125.19 79.25,121.38C79.77,120.37 80.29,119.37 80.83,118.34C81.21,117.57 81.6,116.79 82,116C82.66,116 83.32,116 84,116C81.96,121.35 79.63,126.53 76,131C72.63,131.63 72.63,131.63 70,131C69.66,129.44 69.33,127.88 69,126.31C68.81,125.44 68.63,124.57 68.44,123.68C68.01,121.06 67.92,118.64 68,116C68.99,116 69.98,116 71,116C73.69,113.56 73.69,113.56 76,111C76.33,111.66 76.66,112.32 77,113C77.99,111.35 78.98,109.7 80,108Z"
android:fillColor="#0C320A"/>
<path
android:pathData="M131,118C132.32,119.65 133.64,121.3 135,123C135.33,121.68 135.66,120.36 136,119C136.66,119 137.32,119 138,119C138,122.3 138,125.6 138,129C132.46,132.77 128.66,133.54 122,133C122.76,129.16 122.76,129.16 124.94,127.69C125.96,127.35 125.96,127.35 127,127C127.66,127.33 128.32,127.66 129,128C130.32,127.01 131.64,126.02 133,125C132.34,124.01 131.68,123.02 131,122C131,120.68 131,119.36 131,118Z"
android:fillColor="#E1E438"/>
<path
android:pathData="M185,88C189.51,88.53 192.35,90.37 196,93C193,95 193,95 191,96C191,103.92 191,111.84 191,120C190.01,119.67 189.02,119.34 188,119C188,110 188,101 188,92C187.02,90.98 186.02,89.98 185,89C185,88.67 185,88.34 185,88Z"
android:fillColor="#E8C512"/>
<path
android:pathData="M28,94C31.21,93.91 34.42,93.86 37.63,93.81C38.53,93.79 39.43,93.76 40.36,93.74C45.66,93.68 49.46,93.93 54,97C54.99,98.49 54.99,98.49 56,100C55.24,99.69 54.47,99.38 53.69,99.06C50.2,97.68 47.5,96.88 43.77,96.9C42.57,96.91 42.57,96.91 41.34,96.91C40.5,96.92 39.67,96.93 38.81,96.94C37.55,96.94 37.55,96.94 36.25,96.95C34.17,96.96 32.08,96.98 30,97C29.81,98.46 29.62,99.92 29.44,101.38C29.33,102.19 29.23,103 29.12,103.84C28.82,106.05 28.82,106.05 30,108C29.34,108 28.68,108 28,108C28,109.32 28,110.64 28,112C27.01,112 26.02,112 25,112C25.12,110.5 25.24,109 25.38,107.5C25.44,106.66 25.51,105.83 25.59,104.97C26.11,101.22 27.04,97.66 28,94Z"
android:fillColor="#0A2509"/>
<path
android:pathData="M200,99C200.66,99 201.32,99 202,99C202.33,100.32 202.66,101.64 203,103C203.66,103 204.32,103 205,103C205.03,104.73 205.05,106.46 205.06,108.19C205.07,109.15 205.09,110.11 205.1,111.11C205,113.86 204.62,116.32 204,119C202.35,119 200.7,119 199,119C198.97,116.04 198.95,113.08 198.94,110.13C198.93,109.28 198.92,108.44 198.91,107.57C198.91,106.77 198.91,105.96 198.9,105.13C198.9,104.39 198.89,103.65 198.89,102.88C199,101 199,101 200,99Z"
android:fillColor="#BE720B"/>
<path
android:pathData="M106.29,93.9C107.66,93.92 107.66,93.92 109.06,93.94C109.98,93.95 110.9,93.96 111.85,93.96C112.56,93.98 113.27,93.99 114,94C112.07,106.67 112.07,106.67 110,112C108.8,108.47 109.2,105.87 109.94,102.25C110.13,101.27 110.33,100.28 110.53,99.27C110.68,98.52 110.84,97.77 111,97C108.03,96.5 108.03,96.5 105,96C104.71,97.58 104.42,99.17 104.13,100.75C103.94,101.74 103.76,102.74 103.57,103.76C103.18,105.96 102.83,108.16 102.49,110.37C102.25,111.98 102.25,111.98 102,113.63C101.78,115.13 101.78,115.13 101.56,116.66C101.38,117.43 101.19,118.21 101,119C100.34,119.33 99.68,119.66 99,120C100.76,94.14 100.76,94.14 106.29,93.9Z"
android:fillColor="#092107"/>
<path
android:pathData="M162,76C164.97,76 167.94,76 171,76C170.01,76.33 169.02,76.66 168,77C168,79.64 168,82.28 168,85C168.99,85.33 169.98,85.66 171,86C170.01,86.5 170.01,86.5 169,87C168.67,87.99 168.34,88.98 168,90C166.68,90 165.36,90 164,90C163.67,90.66 163.34,91.32 163,92C162.08,86.59 161.92,81.49 162,76Z"
android:fillColor="#CAB737"/>
<path
android:pathData="M88,139C89.98,139 91.96,139 94,139C94.33,139.66 94.66,140.32 95,141C96.65,141 98.3,141 100,141C100.33,140.34 100.66,139.68 101,139C101.33,139.66 101.66,140.32 102,141C102.99,141 103.98,141 105,141C105,140.34 105,139.68 105,139C105.66,139 106.32,139 107,139C107,139.66 107,140.32 107,141C108.32,141.33 109.64,141.66 111,142C110.01,143.49 110.01,143.49 109,145C106.05,145.22 103.32,145.28 100.38,145.19C99.57,145.17 98.77,145.16 97.95,145.15C95.96,145.11 93.98,145.06 92,145C92,144.34 92,143.68 92,143C91.34,143 90.68,143 90,143C90,143.66 90,144.32 90,145C89.01,144.67 88.02,144.34 87,144C87.33,142.35 87.66,140.7 88,139ZM96,142C97,144 97,144 97,144Z"
android:fillColor="#010101"/>
<path
android:pathData="M163,126C167.71,125.83 167.71,125.83 170.31,127.38C170.87,127.91 171.43,128.45 172,129C172,129.66 172,130.32 172,131C172.66,131 173.32,131 174,131C173,134 173,134 170,136C169.01,136 168.02,136 167,136C167,135.34 167,134.68 167,134C166.34,134 165.68,134 165,134C165.33,135.98 165.66,137.96 166,140C165.01,140.49 165.01,140.49 164,141C162.68,136.04 162.91,131.09 163,126Z"
android:fillColor="#B9BC16"/>
<path
android:pathData="M80,114C80.66,114.33 81.32,114.66 82,115C81.43,118.9 80.31,121.54 78.13,124.81C77.61,125.6 77.1,126.39 76.57,127.21C76.05,127.8 75.53,128.39 75,129C74.01,129 73.02,129 72,129C71.38,126.69 71.38,126.69 71,124C71.66,123.01 72.32,122.02 73,121C72.34,120.67 71.68,120.34 71,120C73.74,117.26 76.55,115.79 80,114Z"
android:fillColor="#103C10"/>
<path
android:pathData="M172,76C172,77.65 172,79.3 172,81C173.32,81 174.64,81 176,81C176,81.66 176,82.32 176,83C176.99,83.33 177.98,83.66 179,84C178.34,85.32 177.68,86.64 177,88C176.34,88 175.68,88 175,88C175.33,89.32 175.66,90.64 176,92C172.52,90.61 171.52,89.37 170,86C169.34,85.67 168.68,85.34 168,85C168,82.36 168,79.72 168,77C171,76 171,76 172,76Z"
android:fillColor="#F2DE29"/>
<path
android:pathData="M85,110C86.32,110 87.64,110 89,110C87.68,116.27 86.36,122.54 85,129C86.65,128.67 88.3,128.34 90,128C90.33,124.37 90.66,120.74 91,117C91.99,117 92.98,117 94,117C93.59,121.97 93.2,126.45 91,131C88.36,131 85.72,131 83,131C81.15,127.31 83.41,122.91 84.41,119.07C85.26,115.98 85.26,115.98 84,113C84.33,112.01 84.66,111.02 85,110Z"
android:fillColor="#051F05"/>
<path
android:pathData="M172,77C177.38,79.91 182.18,83.24 187,87C183.38,90 183.38,90 180,90C178.31,91.44 178.31,91.44 177,93C176.34,91.35 175.68,89.7 175,88C175.66,88 176.32,88 177,88C176.67,87.01 176.34,86.02 176,85C176.66,84.67 177.32,84.34 178,84C177.34,83.67 176.68,83.34 176,83C176,82.34 176,81.68 176,81C174.68,81 173.36,81 172,81C172,79.68 172,78.36 172,77Z"
android:fillColor="#F4F019"/>
<path
android:pathData="M50,116C50,116.33 50,116.66 50,117C44.72,117.66 39.44,118.32 34,119C33.01,122.96 32.02,126.92 31,131C27.7,131 24.4,131 21,131C21,129.68 21,128.36 21,127C21.66,127 22.32,127 23,127C23,127.66 23,128.32 23,129C25.31,129 27.62,129 30,129C30.05,128.4 30.1,127.79 30.15,127.17C30.22,126.37 30.3,125.57 30.38,124.75C30.44,123.96 30.51,123.17 30.59,122.36C31.04,119.76 31.4,118.12 33,116C35.64,115.36 35.64,115.36 38.81,115.19C39.85,115.12 40.89,115.05 41.96,114.98C44.8,115 47.25,115.33 50,116Z"
android:fillColor="#051E05"/>
<path
android:pathData="M90.85,93.35C93.63,93.37 96.26,93.56 99,94C97.81,101.37 96.52,108.69 95,116C94.34,116 93.68,116 93,116C93.48,113.04 93.96,110.08 94.44,107.13C94.57,106.28 94.71,105.44 94.85,104.57C94.98,103.77 95.11,102.96 95.25,102.13C95.37,101.39 95.49,100.65 95.61,99.88C96,98 96,98 97,96C92.85,95.85 92.85,95.85 89,97C86.49,100.09 84.72,103.42 83,107C82,104 82,104 82.83,101.52C86.61,94.32 86.61,94.32 90.85,93.35Z"
android:fillColor="#061F05"/>
<path
android:pathData="M155,131C159.92,135.18 159.92,135.18 160.31,139.31C160.16,140.64 160.16,140.64 160,142C160.99,142 161.98,142 163,142C162.67,142.99 162.34,143.98 162,145C161.01,145.33 160.02,145.66 159,146C158.67,145.01 158.34,144.02 158,143C156.68,143 155.36,143 154,143C153.58,141.57 153.19,140.13 152.81,138.69C152.59,137.89 152.37,137.09 152.14,136.26C152,134 152,134 153.45,132.21C153.96,131.81 154.47,131.41 155,131Z"
android:fillColor="#868815"/>
<path
android:pathData="M148,128C148.66,128 149.32,128 150,128C150,129.32 150,130.64 150,132C148.68,132.33 147.36,132.66 146,133C148.97,133.49 148.97,133.49 152,134C154,139.63 154,139.63 154,143C155.32,143.33 156.64,143.66 158,144C157.01,144.66 156.02,145.32 155,146C152.36,142.87 151.16,139.91 150,136C148.93,136.19 147.85,136.37 146.75,136.56C143.6,136.93 141.9,137.04 139,136C138.44,134.06 138.44,134.06 138,132C139.58,130.42 141.38,130.65 143.56,130.44C144.8,130.31 144.8,130.31 146.07,130.18C147.02,130.09 147.02,130.09 148,130C148,129.34 148,128.68 148,128Z"
android:fillColor="#71290E"/>
<path
android:pathData="M193,93C196.36,96.05 196.77,98.59 197,103C197.51,117 197.51,117 195,121C194.34,120.67 193.68,120.34 193,120C193.08,118.81 193.17,117.61 193.25,116.38C194.04,104.67 194.04,104.67 193,93Z"
android:fillColor="#D3A012"/>
<path
android:pathData="M169,125C174.97,128.69 180.75,132.32 186,137C185.67,137.66 185.34,138.32 185,139C183.35,138.67 181.7,138.34 180,138C179.67,137.01 179.34,136.02 179,135C178.4,134.88 177.8,134.75 177.19,134.63C173.99,133.71 172.94,131.63 171,129C170.02,127.98 169.03,126.97 168,126C168.33,125.67 168.66,125.34 169,125Z"
android:fillColor="#992A0C"/>
<path
android:pathData="M21,139C23.31,139 25.62,139 28,139C28,139.66 28,140.32 28,141C30.64,141 33.28,141 36,141C35.67,142.32 35.34,143.64 35,145C33.56,145.05 32.13,145.09 30.69,145.13C29.89,145.15 29.09,145.17 28.26,145.2C26,145 26,145 23,143C23,143.66 23,144.32 23,145C22.01,144.67 21.02,144.34 20,144C20.33,142.35 20.66,140.7 21,139Z"
android:fillColor="#010101"/>
<path
android:pathData="M149,83C155.06,83.3 158.51,86.28 163,90C162.67,90.66 162.34,91.32 162,92C161.34,91.67 160.68,91.34 160,91C160,90.34 160,89.68 160,89C157.52,88.5 157.52,88.5 155,88C155,87.34 155,86.68 155,86C154.01,86 153.02,86 152,86C151.67,86.66 151.34,87.32 151,88C146.71,89.3 142.43,89.08 138,89C140.55,86.65 141.71,86.02 145.25,85.81C146.61,85.91 146.61,85.91 148,86C148.33,85.01 148.66,84.02 149,83Z"
android:fillColor="#F3F265"/>
<path
android:pathData="M155,119C158.87,119.57 161.08,121.49 164,124C161.24,123.48 158.67,122.89 156,122C156.16,122.72 156.33,123.44 156.5,124.19C156.98,126.86 157.08,129.29 157,132C155.68,132 154.36,132 153,132C152.67,132.66 152.34,133.32 152,134C152.45,128.73 153.08,123.98 155,119Z"
android:fillColor="#957B13"/>
<path
android:pathData="M143,114C147.46,114.99 147.46,114.99 152,116C151.67,117.65 151.34,119.3 151,121C149.04,120.72 147.08,120.42 145.13,120.13C143.49,119.88 143.49,119.88 141.82,119.63C139,119 139,119 137,117C138,116 138,116 140.56,115.94C141.77,115.97 141.77,115.97 143,116C143,115.34 143,114.68 143,114Z"
android:fillColor="#D3D629"/>
<path
android:pathData="M185,88C189.51,88.53 192.35,90.37 196,93C193,95 193,95 191,96C191,98.64 191,101.28 191,104C189.5,102.94 189.5,102.94 188,101C187.81,97.44 187.81,97.44 188,94C188.23,91.88 188.23,91.88 186.5,90.44C186.01,89.96 185.51,89.49 185,89C185,88.67 185,88.34 185,88Z"
android:fillColor="#E6C313"/>
<path
android:pathData="M82,116C82.66,116 83.32,116 84,116C81.96,121.35 79.63,126.53 76,131C72.63,131.63 72.63,131.63 70,131C69.67,128.03 69.34,125.06 69,122C69.99,122.5 69.99,122.5 71,123C71.63,126.06 71.63,126.06 72,129C76.13,127.51 77.3,125.19 79.25,121.38C79.77,120.37 80.29,119.37 80.83,118.34C81.21,117.57 81.6,116.79 82,116Z"
android:fillColor="#041E04"/>
<path
android:pathData="M154,119C155.88,124.22 153.52,128.89 152,134C148.37,133.67 144.74,133.34 141,133C141,132.67 141,132.34 141,132C143.97,132 146.94,132 150,132C149.92,130.97 149.84,129.94 149.75,128.88C150.04,124.41 151.29,122.48 154,119Z"
android:fillColor="#A11C05"/>
<path
android:pathData="M207,102C207.99,102.33 208.98,102.66 210,103C210.08,105.25 210.14,107.5 210.19,109.75C210.22,111 210.26,112.26 210.29,113.55C209.98,117.2 209.76,118.61 207,121C204.3,121.61 204.3,121.61 201.31,121.75C200.32,121.81 199.32,121.86 198.3,121.92C197.54,121.95 196.78,121.97 196,122C196.33,121.34 196.66,120.68 197,120C199.32,119.61 201.62,119.49 203.97,119.34C204.64,119.23 205.31,119.12 206,119C207.53,115.93 207.1,112.73 207.06,109.38C207.06,108.67 207.05,107.96 207.05,107.23C207.04,105.49 207.02,103.74 207,102Z"
android:fillColor="#7A3D0F"/>
<path
android:pathData="M184,107C184.99,107 185.98,107 187,107C187.33,111.95 187.66,116.9 188,122C185.69,123.32 183.38,124.64 181,126C180.67,125.01 180.34,124.02 180,123C181.65,122.01 183.3,121.02 185,120C184.67,115.71 184.34,111.42 184,107Z"
android:fillColor="#EDE023"/>
<path
android:pathData="M176,92C176.99,92.33 177.98,92.66 179,93C179.33,94.65 179.66,96.3 180,98C180.99,98.33 181.98,98.66 183,99C183,100.98 183,102.96 183,105C180.69,105.33 178.38,105.66 176,106C176,105.34 176,104.68 176,104C174.68,103.67 173.36,103.34 172,103C178.75,102.88 178.75,102.88 181,104C179.46,100.86 177.94,97.91 176,95C176,94.01 176,93.02 176,92Z"
android:fillColor="#F6D52F"/>
<path
android:pathData="M64.5,93.9C66.02,93.92 66.02,93.92 67.56,93.94C68.57,93.95 69.59,93.96 70.63,93.96C71.8,93.98 71.8,93.98 73,94C73.33,96.97 73.66,99.94 74,103C73.01,103.5 73.01,103.5 72,104C71.67,101.36 71.34,98.72 71,96C68.36,96.33 65.72,96.66 63,97C62.67,98.98 62.34,100.96 62,103C61.34,102.67 60.68,102.34 60,102C61.01,94.04 61.01,94.04 64.5,93.9Z"
android:fillColor="#061F05"/>
<path
android:pathData="M39,139C40.33,139.33 41.67,139.67 43,140C44.08,140.19 44.08,140.19 45.19,140.38C46.08,140.68 46.08,140.68 47,141C47.33,141.99 47.66,142.98 48,144C44.22,145.84 42.05,146.16 38,145C37.67,144.01 37.34,143.02 37,142C37.66,141.01 38.32,140.02 39,139Z"
android:fillColor="#010101"/>
<path
android:pathData="M61,139C61.99,139 62.98,139 64,139C64,139.66 64,140.32 64,141C66.31,141 68.62,141 71,141C71,141.66 71,142.32 71,143C71.99,143.33 72.98,143.66 74,144C73.67,144.66 73.34,145.32 73,146C72.31,145.93 71.63,145.86 70.92,145.78C70.02,145.69 69.12,145.6 68.19,145.5C67.29,145.41 66.4,145.31 65.48,145.22C62.92,144.96 62.92,144.96 60,145C60,144.34 60,143.68 60,143C60.66,143 61.32,143 62,143C61.67,141.68 61.34,140.36 61,139Z"
android:fillColor="#010001"/>
<path
android:pathData="M174,120C174.66,120 175.32,120 176,120C176.14,120.64 176.29,121.28 176.44,121.94C176.81,123.98 176.81,123.98 178,125C178.66,125.33 179.32,125.66 180,126C179.01,127.32 178.02,128.64 177,130C174.51,128.8 172.32,127.55 170,126C171.15,123.53 172.05,121.95 174,120Z"
android:fillColor="#EEDE2B"/>
<path
android:pathData="M168,99C170.02,99.6 172.02,100.27 174,101C174.33,101.66 174.66,102.32 175,103C172.69,103 170.38,103 168,103C166.65,107.06 166.44,111.04 167.63,115.19C168.14,117.67 167.18,118.83 166,121C165.55,118.46 165.14,115.92 164.75,113.38C164.62,112.66 164.49,111.95 164.36,111.21C163.78,107.26 163.69,104.87 165.95,101.48C166.63,100.66 167.3,99.84 168,99Z"
android:fillColor="#C59431"/>
<path
android:pathData="M164,92C165.32,92.66 166.64,93.32 168,94C167.67,95.65 167.34,97.3 167,99C165.57,98.84 165.57,98.84 164.11,98.68C162.86,98.56 161.6,98.44 160.31,98.31C159.07,98.18 157.83,98.06 156.55,97.93C152.7,98.01 151.05,98.73 148,101C148.33,100.01 148.66,99.02 149,98C149.66,98 150.32,98 151,98C151,97.01 151,96.02 151,95C158.43,95.99 158.43,95.99 166,97C166,96.01 166,95.02 166,94C165.34,94 164.68,94 164,94C164,93.34 164,92.68 164,92Z"
android:fillColor="#F7CA36"/>
<path
android:pathData="M145,121C146.65,121 148.3,121 150,121C148.51,125.7 148.51,125.7 145.94,127.19C145.3,127.46 144.66,127.72 144,128C143.67,128.66 143.34,129.32 143,130C144.98,129.67 146.96,129.34 149,129C148.67,129.66 148.34,130.32 148,131C145.69,131 143.38,131 141,131C141,129.68 141,128.36 141,127C141.66,127 142.32,127 143,127C143.66,125.02 144.32,123.04 145,121Z"
android:fillColor="#4C5D09"/>
<path
android:pathData="M171,106C171.33,106 171.66,106 172,106C172,111.61 172,117.22 172,123C171.34,123.33 170.68,123.66 170,124C168.46,120.92 169.13,117.79 169.38,114.44C169.42,113.74 169.46,113.04 169.51,112.32C169.87,107.13 169.87,107.13 171,106Z"
android:fillColor="#D9DC14"/>
<path
android:pathData="M76,139C77.65,139 79.3,139 81,139C81,139.66 81,140.32 81,141C82.32,141 83.64,141 85,141C85.33,141.99 85.66,142.98 86,144C82.59,144.78 79.49,145.1 76,145C76,143.02 76,141.04 76,139ZM78,140C79,144 79,144 79,144Z"
android:fillColor="#020102"/>
<path
android:pathData="M64,121C64.33,121 64.66,121 65,121C64.86,122.48 64.71,123.96 64.56,125.44C64.48,126.26 64.4,127.08 64.32,127.93C64,130 64,130 63,131C61.48,131.07 59.96,131.08 58.44,131.06C57.61,131.05 56.78,131.04 55.93,131.04C55.3,131.02 54.66,131.01 54,131C54.33,128.69 54.66,126.38 55,124C55.66,124 56.32,124 57,124C56.67,125.65 56.34,127.3 56,129C57.65,129 59.3,129 61,129C61.99,126.36 62.98,123.72 64,121Z"
android:fillColor="#031C03"/>
<path
android:pathData="M169,87C169.99,87.33 170.98,87.66 172,88C172,88.66 172,89.32 172,90C172.64,90.23 173.28,90.45 173.94,90.69C176,92 176,92 176.75,94.63C176.87,95.8 176.87,95.8 177,97C176.09,96.2 175.18,95.39 174.25,94.56C171.9,92.59 170.13,91.36 167,91C167,91.66 167,92.32 167,93C166.01,92.67 165.02,92.34 164,92C164,91.34 164,90.68 164,90C164.64,89.88 165.28,89.75 165.94,89.63C166.96,89.32 166.96,89.32 168,89C168.33,88.34 168.66,87.68 169,87Z"
android:fillColor="#AD2102"/>
<path
android:pathData="M165,134C165.66,134 166.32,134 167,134C167,134.66 167,135.32 167,136C168.65,135.34 170.3,134.68 172,134C171.67,134.99 171.34,135.98 171,137C170.34,137 169.68,137 169,137C168.92,137.93 168.84,138.86 168.75,139.81C168,143 168,143 165.94,144.88C164.98,145.43 164.98,145.43 164,146C163.67,145.34 163.34,144.68 163,144C163.66,144 164.32,144 165,144C165,140.7 165,137.4 165,134Z"
android:fillColor="#979926"/>
<path
android:pathData="M131,118C132.32,119.65 133.64,121.3 135,123C135.33,121.68 135.66,120.36 136,119C136.66,119 137.32,119 138,119C138,122.3 138,125.6 138,129C137.34,129 136.68,129 136,129C135.73,128.4 135.46,127.8 135.19,127.19C134.13,124.96 134.13,124.96 132.38,123.5C131,122 131,122 131,118Z"
android:fillColor="#C9D23D"/>
<path
android:pathData="M134,111C134.33,111 134.66,111 135,111C135,112.65 135,114.3 135,116C135.76,115.63 136.53,115.26 137.31,114.88C138.2,114.59 139.09,114.3 140,114C140.99,114.66 141.98,115.32 143,116C141.58,116.34 141.58,116.34 140.13,116.69C136.81,117.69 136.81,117.69 135.63,120.63C135.42,121.41 135.21,122.19 135,123C133,122 133,122 132.25,120.38C131.89,116.96 132.95,114.23 134,111Z"
android:fillColor="#F3F22E"/>
<path
android:pathData="M167,91C169.63,91 169.63,91 173,92C175.31,94.47 177.17,97.16 179,100C178.67,100.66 178.34,101.32 178,102C176.68,101.34 175.36,100.68 174,100C174,98.35 174,96.7 174,95C171.69,94.34 169.38,93.68 167,93C167,92.34 167,91.68 167,91Z"
android:fillColor="#C90407"/>
<path
android:pathData="M153,97C156.75,96.71 160.1,97.01 163.81,97.56C166.89,98.29 166.89,98.29 169,97C167.93,99.92 167.22,101.78 165,104C165,102.68 165,101.36 165,100C160.38,100 155.76,100 151,100C151.66,99.01 152.32,98.02 153,97Z"
android:fillColor="#AC2303"/>
<path
android:pathData="M48,139C49.08,139.49 49.08,139.49 50.19,140C53.02,141.01 55.03,141.17 58,141C58,142.32 58,143.64 58,145C56.72,145.06 55.44,145.12 54.13,145.19C53.41,145.22 52.69,145.26 51.95,145.29C51.3,145.2 50.66,145.1 50,145C48,142 48,142 48,139Z"
android:fillColor="#010001"/>
<path
android:pathData="M156,122C156.99,122 157.98,122 159,122C159,122.99 159,123.98 159,125C159.66,125.33 160.32,125.66 161,126C160.01,127.49 160.01,127.49 159,129C159.32,132.64 159.32,132.64 160,136C159.01,135.34 158.02,134.68 157,134C156.49,131.18 156.49,131.18 156.31,127.88C156.25,126.78 156.18,125.68 156.11,124.55C156.06,123.29 156.06,123.29 156,122Z"
android:fillColor="#999E18"/>
<path
android:pathData="M199,103C201.35,106.52 201.38,108.51 201.63,112.69C201.7,113.87 201.77,115.05 201.85,116.26C201.9,117.17 201.95,118.07 202,119C201.01,119 200.02,119 199,119C199,113.72 199,108.44 199,103Z"
android:fillColor="#C6800F"/>
<path
android:pathData="M47,104C47.66,104 48.32,104 49,104C49.33,104.99 49.66,105.98 50,107C49.34,107 48.68,107 48,107C47.67,107.99 47.34,108.98 47,110C44.12,110.96 42.3,111.11 39.31,111.06C38.1,111.05 38.1,111.05 36.86,111.04C35.94,111.02 35.94,111.02 35,111C35,110.34 35,109.68 35,109C35.7,108.97 36.4,108.95 37.12,108.92C38.03,108.87 38.94,108.81 39.88,108.75C40.78,108.7 41.68,108.66 42.62,108.61C45.17,108.24 45.17,108.24 46.32,105.95C46.66,104.99 46.66,104.99 47,104Z"
android:fillColor="#072707"/>
<path
android:pathData="M172,76C172,78.97 172,81.94 172,85C170.68,85 169.36,85 168,85C168,82.36 168,79.72 168,77C171,76 171,76 172,76Z"
android:fillColor="#D8D817"/>
<path
android:pathData="M166,126C168.44,126.81 168.44,126.81 171,128C171.33,128.99 171.66,129.98 172,131C172.66,131 173.32,131 174,131C173.67,131.99 173.34,132.98 173,134C170.44,135.19 170.44,135.19 168,136C168.33,134.02 168.66,132.04 169,130C168.01,129.34 167.02,128.68 166,128C166,127.34 166,126.68 166,126Z"
android:fillColor="#DBC032"/>
<path
android:pathData="M178,132C181.91,133.27 183.79,134.51 186,138C183.63,138.63 183.63,138.63 181,139C178.64,136.64 178.51,135.22 178,132Z"
android:fillColor="#8B180E"/>
<path
android:pathData="M160,106C161.32,106 162.64,106 164,106C163.81,106.52 163.63,107.03 163.44,107.56C162.85,110.86 163.43,113.72 164,117C161,115 161,115 160.49,112.84C160.43,112.02 160.37,111.21 160.31,110.38C160.25,109.56 160.18,108.74 160.11,107.9C160.08,107.27 160.04,106.65 160,106Z"
android:fillColor="#AA1913"/>
<path
android:pathData="M80,108C80.66,108.33 81.32,108.66 82,109C79.53,113.46 79.53,113.46 77,118C76.01,117.67 75.02,117.34 74,117C74,115.35 74,113.7 74,112C74.66,111.67 75.32,111.34 76,111C76.33,111.66 76.66,112.32 77,113C77.99,111.35 78.98,109.7 80,108Z"
android:fillColor="#072706"/>
<path
android:pathData="M172,77C176.47,79.5 180.77,82.11 185,85C182,86 182,86 178.81,84.56C177.88,84.05 176.96,83.53 176,83C176,82.34 176,81.68 176,81C174.68,81 173.36,81 172,81C172,79.68 172,78.36 172,77Z"
android:fillColor="#EFEE61"/>
<path
android:pathData="M165,128C166.94,128.31 166.94,128.31 169,129C169.33,129.99 169.66,130.98 170,132C168.56,134.19 168.56,134.19 167,136C167,135.34 167,134.68 167,134C166.34,134 165.68,134 165,134C165,132.02 165,130.04 165,128Z"
android:fillColor="#CBCD1A"/>
<path
android:pathData="M185,88C189.51,88.53 192.35,90.37 196,93C194,94 194,94 191.06,93.25C188.25,92.1 186.84,91.35 185,89C185,88.67 185,88.34 185,88Z"
android:fillColor="#DAC742"/>
<path
android:pathData="M180,84C183.46,85.49 183.46,85.49 187,87C185.68,87.99 184.36,88.98 183,90C181.68,89.34 180.36,88.68 179,88C179.33,86.68 179.66,85.36 180,84Z"
android:fillColor="#F4F02D"/>
<path
android:pathData="M163,77C163.99,77 164.98,77 166,77C165.67,80.96 165.34,84.92 165,89C164.67,89 164.34,89 164,89C163.02,84.95 162.92,81.16 163,77Z"
android:fillColor="#B6B91B"/>
<path
android:pathData="M151,135C153.62,138.06 154,138.73 154,143C155.32,143.33 156.64,143.66 158,144C157.01,144.66 156.02,145.32 155,146C151.85,142.58 151.44,139.56 151,135Z"
android:fillColor="#46430F"/>
<path
android:pathData="M68,108C70,111.75 70,111.75 70,114C68.35,114.33 66.7,114.66 65,115C65,113.35 65,111.7 65,110C65.99,110 66.98,110 68,110C68,109.34 68,108.68 68,108Z"
android:fillColor="#052503"/>
<path
android:pathData="M153,85C157.4,85.5 159.71,87.09 163,90C162.67,90.66 162.34,91.32 162,92C161.34,91.67 160.68,91.34 160,91C160,90.34 160,89.68 160,89C158.35,88.67 156.7,88.34 155,88C155,87.34 155,86.68 155,86C154.34,85.67 153.68,85.34 153,85Z"
android:fillColor="#EBE749"/>
<path
android:pathData="M172,81C173.32,81 174.64,81 176,81C175.67,82.98 175.34,84.96 175,87C174.01,87 173.02,87 172,87C172,85.02 172,83.04 172,81Z"
android:fillColor="#E7E81D"/>
<path
android:pathData="M155,119C158.87,119.57 161.08,121.49 164,124C159.54,123.01 159.54,123.01 155,122C155,121.01 155,120.02 155,119Z"
android:fillColor="#C47B29"/>
<path
android:pathData="M155,131C155.66,131.33 156.32,131.66 157,132C157,132.66 157,133.32 157,134C156.34,134 155.68,134 155,134C155,134.99 155,135.98 155,137C154.01,137 153.02,137 152,137C152.38,134.56 152.38,134.56 153,132C153.66,131.67 154.32,131.34 155,131Z"
android:fillColor="#7A8815"/>
<path
android:pathData="M148,124C148.63,125.88 148.63,125.88 149,128C148.34,128.66 147.68,129.32 147,130C145.68,130 144.36,130 143,130C143.33,129.01 143.66,128.02 144,127C144.99,126.67 145.98,126.34 147,126C147.33,125.34 147.66,124.68 148,124Z"
android:fillColor="#D8DF49"/>
<path
android:pathData="M141,119C143.97,119.33 146.94,119.66 150,120C150,120.33 150,120.66 150,121C148.35,121 146.7,121 145,121C145,121.99 145,122.98 145,124C144.34,124 143.68,124 143,124C142.67,123.34 142.34,122.68 142,122C141.34,122 140.68,122 140,122C140.33,121.01 140.66,120.02 141,119Z"
android:fillColor="#394F07"/>
<path
android:pathData="M135,89C136.32,89.33 137.64,89.66 139,90C139,90.66 139,91.32 139,92C136.03,92.66 133.06,93.32 130,94C130,93.34 130,92.68 130,92C131.65,91.67 133.3,91.34 135,91C135,90.34 135,89.68 135,89Z"
android:fillColor="#F3F576"/>
<path
android:pathData="M166,77C166.66,77 167.32,77 168,77C168,79.64 168,82.28 168,85C167.01,84.34 166.02,83.68 165,83C165.33,81.02 165.66,79.04 166,77Z"
android:fillColor="#CBC920"/>
<path
android:pathData="M111,143C111.33,143.66 111.66,144.32 112,145C111.34,144.67 110.68,144.34 110,144C110.33,143.67 110.66,143.34 111,143Z"
android:fillColor="#040000"/>
</vector>

View File

@@ -0,0 +1,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/colorRed"/> <!-- couleur verte -->
<corners android:radius="5dp"/> <!-- coins arrondis -->
</shape>

View File

@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/agent_item_border_rounded"
android:orientation="vertical"
android:layout_margin="2sp"
android:paddingStart="16dp"
android:paddingTop="12dp"
android:paddingEnd="16dp"
android:paddingBottom="12dp">
<!-- Code -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_weight="1">
<LinearLayout
android:layout_weight="0.8"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<View
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="8sp"
android:background="@drawable/decorative_dot" />
<TextView
android:id="@+id/txtCode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:text="0001-l2"
android:textColor="#1A1A2E"
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageButton
android:id="@+id/details"
android:layout_width="25sp"
android:layout_height="25sp"
android:background="@drawable/agent_item_border_rounded"
android:layout_gravity="center_vertical"
android:src="@drawable/ic_chevron_right"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:orientation="horizontal">
<!-- Contenu principal : date et adresse en horizontal avec space between -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:weightSum="1"
android:gravity="center_vertical">
<!-- Date avec icône -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.35"
android:orientation="horizontal"
android:gravity="center_vertical">
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:src="@drawable/ic_calendar"
app:tint="@color/primary_green" />
<TextView
android:id="@+id/txtDate"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_weight="1"
android:text="12 mars 2024"
android:textColor="#4A5568"
android:textSize="13sp" />
</LinearLayout>
<!-- Adresse avec icône -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.65"
android:orientation="horizontal"
android:gravity="center_vertical">
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:src="@drawable/ic_location_small"
app:tint="@color/primary_green" />
<TextView
android:id="@+id/txtAdresse"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:layout_weight="1"
android:maxLines="2"
android:ellipsize="end"
android:text="Badalabougou sema, Bamako, Mali"
android:textColor="#4A5568"
android:textSize="13sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,178 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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="@color/background_gray">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<!-- Card pour les infos de l'agent -->
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:cardCornerRadius="12dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_weight="1"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Détail de l'agent"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="@color/primary_green"
android:layout_weight="0.7"/>
<Switch
android:id="@+id/switch_access"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:ignore="UseSwitchCompatOrMaterialXml" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Code: "
android:textSize="14sp"
android:textColor="@color/secondary_yellow" />
<TextView
android:id="@+id/txtCode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text=""
android:textSize="14sp"
android:textColor="@color/secondary_yellow" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Adresse: "
android:textSize="14sp"
android:textColor="@color/secondary_yellow" />
<TextView
android:id="@+id/txtAdresse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textSize="14sp"
android:textColor="@color/secondary_yellow" />
</LinearLayout>
<!-- Bouton d'accès avec icône -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Accès agent :"
android:textSize="16sp"
android:textStyle="bold" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/btn_access_toggle"
android:layout_width="48dp"
android:layout_height="48dp"
android:clickable="true"
android:focusable="true"
android:padding="12dp"
android:background="?attr/selectableItemBackgroundBorderless" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<!-- Card pour les paris disponibles -->
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:cardCornerRadius="12dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingVertical="16dp"
android:paddingHorizontal="3dp"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Paris disponibles"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="@color/primary_green"
android:layout_marginBottom="12dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sélectionnez les paris :"
android:textSize="14sp"
android:textColor="@color/secondary_yellow"
android:layout_marginBottom="8dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/betsRecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<!-- Bouton de validation -->
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_validate"
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="Valider la sélection"
android:textSize="16sp"
app:cornerRadius="28dp"
app:icon="@drawable/ic_check"
app:iconGravity="textStart" />
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".AgentManagement">
<!-- TODO: Update blank fragment layout -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="12dp"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/agentList"
android:layout_marginHorizontal="13sp"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>
</FrameLayout>

View File

@@ -0,0 +1,130 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".UpdatePin">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingHorizontal="10sp"
android:paddingTop="30sp"
android:orientation="vertical">
<ImageView
android:layout_width="100sp"
android:layout_height="100sp"
android:src="@android:drawable/ic_lock_idle_lock"
android:padding="10sp"
android:layout_gravity="center"
app:tint="@color/primary_green">
</ImageView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="40sp"
android:text="Changer pin"
android:layout_marginBottom="20sp"
android:layout_gravity="center"
android:textStyle="bold"
android:textColor="@color/primary_green">
</TextView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginBottom="10sp">
<TextView
android:layout_marginBottom="7sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Ancien pin"
android:textSize="16sp"
tools:ignore="HardcodedText"/>
<EditText
android:id="@+id/old_pin"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="2343234"
android:drawablePadding="5dp"
android:textSize="14sp"
android:drawableStart="@drawable/hashtag"
android:background="@drawable/edittext_outline_white"
android:inputType="numberPassword" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginBottom="10sp">
<TextView
android:layout_marginBottom="7sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nouveau pin"
android:textSize="16sp"
tools:ignore="HardcodedText"/>
<EditText
android:id="@+id/new_pin"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="2343234"
android:drawablePadding="5dp"
android:textSize="14sp"
android:drawableStart="@drawable/hashtag"
android:background="@drawable/edittext_outline_white"
android:inputType="numberPassword"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginBottom="10sp">
<TextView
android:layout_marginBottom="7sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Confirmation pin"
android:textSize="16sp"
tools:ignore="HardcodedText"/>
<EditText
android:id="@+id/pin_confirmation"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="2343234"
android:drawablePadding="5dp"
android:textSize="14sp"
android:drawableStart="@drawable/hashtag"
android:background="@drawable/edittext_outline_white"
android:inputType="numberPassword" />
</LinearLayout>
<LinearLayout
android:layout_marginTop="35sp"
android:showDividers="middle"
android:dividerPadding="10sp"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/cancel"
android:layout_width="150sp"
android:layout_marginEnd="5sp"
android:layout_height="wrap_content"
android:text="@string/cancel"
android:background="@drawable/rounded_button_red">
</Button>
<Button
android:id="@+id/validate"
android:layout_width="150sp"
android:layout_marginStart="5sp"
android:layout_height="wrap_content"
android:text="@string/validate"
android:background="@drawable/rounded_button_green">
</Button>
</LinearLayout>
</LinearLayout>
</RelativeLayout>

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="12dp"
app:cardElevation="2dp"
app:cardBackgroundColor="@android:color/white">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp"
android:gravity="center_vertical"
android:background="?selectableItemBackground">
<!-- Icône avec cercle de fond -->
<RelativeLayout
android:layout_width="48dp"
android:layout_height="48dp"
android:background="@drawable/circle_bg_light">
<ImageView
android:id="@+id/iv_bet_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_centerInParent="true"
android:src="@drawable/bet_item_icon"
app:tint="@color/primary_green" />
</RelativeLayout>
<!-- Texte -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="16dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_bet_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Type de pari"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="#333333" />
<TextView
android:id="@+id/tv_bet_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Description"
android:textSize="12sp"
android:textColor="#999999"
android:visibility="gone" />
</LinearLayout>
<!-- CheckBox -->
<CheckBox
android:id="@+id/checkbox_bet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:buttonTint="@color/primary_green" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</FrameLayout>

View File

@@ -35,7 +35,7 @@
android:layout_width="match_parent"
android:layout_height="45dp"
android:hint="@string/code_message"
android:inputType="number"
android:inputType="text"
android:textSize="18sp"
android:layout_marginVertical="13sp"
android:textFontWeight="600"

View File

@@ -1,6 +1,6 @@
<?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" />
<background android:drawable="@color/white" />
<foreground android:drawable="@drawable/logo_svg" />
<monochrome android:drawable="@drawable/logo_svg" />
</adaptive-icon>

View File

@@ -1,6 +1,6 @@
<?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" />
<background android:drawable="@color/white"/>
<foreground android:drawable="@drawable/logo_svg"/>
<monochrome android:drawable="@drawable/logo_svg" />
</adaptive-icon>

View File

@@ -10,6 +10,7 @@
<color name="text_color">#333333</color>
<color name="green_opacity_30">#501C5A29</color>
<color name="red_opacity_10">#50C31617</color>
<color name="background_gray">#F5F5F5</color>
<color name="text_light_grey">#e8e8e8</color>
<color name="transparent">#00000000</color>
<color name="white">#ffffff</color>

View File

@@ -17,9 +17,12 @@ playServicesMaps = "18.1.0"
recyclerviewV7 = "28.0.0"
retrofit = "2.11.0"
rxandroid = "1.2.1"
rxandroidVersion = "2.1.1"
rxjava = "1.3.8"
kotlin = "1.9.24"
coreKtx = "1.17.0"
rxjavaVersion = "2.2.21"
stompprotocolandroid = "1.6.6"
swiperefreshlayout = "1.1.0"
core = "1.5.0"
fragmentTesting = "1.6.2"
@@ -59,6 +62,9 @@ room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomCommo
rxandroid = { module = "io.reactivex:rxandroid", version.ref = "rxandroid" }
rxjava = { module = "io.reactivex:rxjava", version.ref = "rxjava" }
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
rxjava2-rxandroid = { module = "io.reactivex.rxjava2:rxandroid", version.ref = "rxandroidVersion" }
stompprotocolandroid = { module = "com.github.NaikSoftware:StompProtocolAndroid", version.ref = "stompprotocolandroid" }
rxjava2-rxjava = { module = "io.reactivex.rxjava2:rxjava", version.ref = "rxjavaVersion" }
swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swiperefreshlayout" }
core = { module = "androidx.test:core", version.ref = "core" }
fragment-testing = { module = "androidx.fragment:fragment-testing", version.ref = "fragmentTesting" }

View File

@@ -311,6 +311,11 @@ public class Printama {
});
}
public int checkPaperStatus(){
return _util.checkPaperStatus();
}
public boolean isConnected() {
return _util.isConnected();
}
@@ -385,6 +390,29 @@ public class Printama {
});
}
public void printSold(Bitmap bitmap, String title, StringBuilder text){
_printama.connect(printama -> {
_util.resetPrinter();
_util.setBold();
_util.printImage(bitmap);
_util.printText(lineSeparator());
_util.setAlign(PA.CENTER);
_util.setBold();
_util.printText(title+"\n");
_util.setNormalText();
_util.printText(lineSeparator()+"\n");
_util.setNormalText();
_util.setAlign(PA.LEFT);
_util.printText(text.toString());
_util.printText(lineSeparator());
_util.setAlign(PA.CENTER);
_util.printText("Powered by PMU-MALI"+"\n");
_util.printText(lineSeparator());
printama.feedPaper();
printama.close();
});
}
//----------------------------------------------------------------------------------------------
// PRINTER COMMANDS

View File

@@ -17,6 +17,7 @@ import com.anggastudio.printama.constants.PW;
import com.anggastudio.printama.util.StrUtil;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.UUID;
@@ -54,6 +55,7 @@ class PrinterUtil {
private final BluetoothDevice printer;
private BluetoothSocket btSocket = null;
private OutputStream btOutputStream = null;
private InputStream btInputStream = null;
private boolean is3InchPrinter;
PrinterUtil(BluetoothDevice printer) {
@@ -67,6 +69,7 @@ class PrinterUtil {
btSocket = socket;
try {
btOutputStream = socket.getOutputStream();
btInputStream = socket.getInputStream();
successListener.onConnected();
} catch (IOException e) {
failedListener.onFailed();
@@ -80,8 +83,27 @@ class PrinterUtil {
}).execute(printer);
}
boolean isConnected() {
return btSocket != null && btSocket.isConnected();
boolean isConnected() {
if (btSocket == null) {
return false;
}
try {
// Tester la connexion en vérifiant le flux d'entrée
if (btSocket.isConnected()) {
// Vérifier si le flux est accessible
InputStream inputStream = btSocket.getInputStream();
// Tenter de lire avec un timeout très court
if (inputStream.available() >= 0) {
return true;
}
}
return false;
} catch (IOException e) {
// Exception signifie que la connexion est perdue
Log.e("Bluetooth", "Socket non connecté: " + e.getMessage());
return false;
}
}
void finish() {
@@ -243,6 +265,40 @@ class PrinterUtil {
}
}
public int checkPaperStatus() {
try {
// Certaines imprimantes doivent être en mode "real-time"
// Commande GS a 1 pour activer le mode "real-time"
byte[] realtimeMode = new byte[]{0x1D, 0x61, 0x01};
btOutputStream.write(realtimeMode);
btOutputStream.flush();
Thread.sleep(50);
// Ensuite la commande de statut
byte[] statusCommand = new byte[]{0x1D, 0x72, 0x01};
btOutputStream.write(statusCommand);
btOutputStream.flush();
Thread.sleep(200);
// Lire la réponse
byte[] response = new byte[4];
int bytesRead = btInputStream.read(response);
if (bytesRead > 0) {
// Traiter la réponse...
return 0;
}
return -1;
} catch (Exception e) {
e.printStackTrace();
return -1;
}
}
boolean printImage(Bitmap bitmap, int width) {
return printImage(PA.CENTER, bitmap, width);
}

View File

@@ -17,7 +17,7 @@
android:layout_height="wrap_content"
android:gravity="center"
android:padding="20dp"
android:text="Choose Printer"
android:text="Choisir une imprimante"
android:textSize="16sp"
android:textStyle="bold" />
@@ -36,7 +36,7 @@
android:layout_margin="20dp"
android:backgroundTint="@color/colorGray5"
android:gravity="center"
android:text="Test"
android:text="Tester"
android:textColor="@android:color/white"
android:textSize="14sp" />
@@ -49,7 +49,7 @@
android:layout_marginBottom="20dp"
android:backgroundTint="@color/colorGray5"
android:gravity="center"
android:text="Save"
android:text="Valider"
android:textColor="@android:color/white"
android:textSize="14sp" />

View File

@@ -20,7 +20,7 @@
android:layout_height="wrap_content"
android:gravity="center"
android:padding="20dp"
android:text="Choose Printer"
android:text="Choissez une imprimante"
android:textColor="@color/colorBlack"
android:textSize="16sp"
android:textStyle="bold" />
@@ -36,7 +36,7 @@
android:textColor="@color/colorBlack"
android:textSize="14sp"
android:visibility="gone"
tools:text="No paired Bluetooth printers found.\n\nTap the button below to open Bluetooth settings and pair a printer."
tools:text="Aucune imprimante Bluetooth jumelée trouvée.\n\nAppuyez sur le bouton ci-dessous pour ouvrir les paramètres Bluetooth et jumeler une imprimante."
tools:visibility="visible" />
<androidx.recyclerview.widget.RecyclerView
@@ -61,7 +61,7 @@
android:layout_marginRight="2dp"
android:backgroundTint="@color/colorGray5"
android:gravity="center"
android:text="Test"
android:text="Tester"
android:textColor="@android:color/white"
android:textSize="14sp" />
@@ -74,7 +74,7 @@
android:layout_weight="1"
android:backgroundTint="@color/colorGray5"
android:gravity="center"
android:text="Save"
android:text="Valider"
android:textColor="@android:color/white"
android:textSize="14sp" />

View File

@@ -16,6 +16,12 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven{
url = uri("https://jitpack.io")
content {
includeGroup("com.github.NaikSoftware")
}
}
}
}