push before automatize version incrementation!

This commit is contained in:
OnlyPapy98
2026-06-02 09:03:46 +00:00
parent 8aa4ad3921
commit c652dc4141
37 changed files with 1291 additions and 88 deletions

View File

@@ -22,8 +22,8 @@ android {
applicationId = "com.example.quiz"
minSdk = 29
targetSdk = 34
versionCode = 1
versionName = "1.0"
versionCode = 2
versionName = "1.1"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

View File

@@ -102,6 +102,7 @@ public class AnnulationTicket extends Fragment {
}
case ERROR:{
MessageDialog.showError(getContext(), parisResponseResult.message);
dialog.dismiss();
break;
}
case SUCCESS:{
@@ -131,7 +132,8 @@ public class AnnulationTicket extends Fragment {
dialog.dismiss();
})
.setPositiveButton("Confirmer", (annulationDialog, which) -> {
viewModel.annulerPari(ticketReference).observe(getViewLifecycleOwner(), new Observer<Result<ParisResponse>>() {
String agentId = prefsHelper.get("id");
viewModel.annulerPari(ticketReference, agentId).observe(getViewLifecycleOwner(), new Observer<Result<ParisResponse>>() {
@Override
public void onChanged(Result<ParisResponse> pariResult) {
switch (pariResult.status){

View File

@@ -0,0 +1,93 @@
package com.example.quiz;
import android.content.SharedPreferences;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.quiz.databinding.FragmentApiUrlConfigurationBinding;
import com.example.quiz.utils.MessageDialog;
import com.example.quiz.utils.SharedPrefsHelper;
/**
* A simple {@link Fragment} subclass.
* Use the {@link ApiUrlConfiguration#newInstance} factory method to
* create an instance of this fragment.
*/
public class ApiUrlConfiguration extends Fragment {
private FragmentApiUrlConfigurationBinding binding;
SharedPrefsHelper prefsHelper;
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
public ApiUrlConfiguration() {
// Required empty public constructor
}
// TODO: Rename and change types and number of parameters
public static ApiUrlConfiguration newInstance() {
ApiUrlConfiguration fragment = new ApiUrlConfiguration();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
prefsHelper = SharedPrefsHelper.getInstance(getContext());
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
binding = FragmentApiUrlConfigurationBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
String serverLink = prefsHelper.get("MAIN_API_URL");
String depLink = prefsHelper.get("REPORT_API_URL");
if(serverLink!=null){
binding.serverLink.setText(serverLink);
}
if(depLink!=null){
binding.depLink.setText(depLink);
}
binding.btnValidate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(binding.serverLink.getText().toString().isEmpty()){
binding.serverLink.setError("Entrez l'URL du serveur");
return;
}
if(binding.depLink.getText().toString().isEmpty()){
binding.depLink.setError("Entrez l'URL de secours");
return;
}
saveServerLinks(binding.serverLink.getText().toString(), binding.depLink.getText().toString());
}
});
super.onViewCreated(view, savedInstanceState);
}
private void saveServerLinks(String serverLink, String depLink){
SharedPreferences.Editor editor = prefsHelper.edit();
editor.putString("MAIN_API_URL", serverLink);
editor.putString("REPORT_API_URL", depLink);
editor.apply();
MessageDialog.showSuccess(getContext(), "URL's enregistrée avec succès");
requireActivity().finishAffinity();
}
}

View File

@@ -343,6 +343,7 @@ public class BetValidation extends Fragment {
}
try {
printPari(pariResult.data);
_initializeToZero();
dlg.dismiss();
prefsHelper.save("pariNoPrintId", null);
} catch (WriterException e) {
@@ -523,6 +524,7 @@ public class BetValidation extends Fragment {
}
try {
printPari(pariResult.data);
_initializeToZero();
dlg.dismiss();
prefsHelper.save("pariNoPrintId", null);
} catch (WriterException | FileNotFoundException e) {
@@ -640,10 +642,8 @@ public class BetValidation extends Fragment {
tspl.append(formattedDate).append("\n");
tspl.append("Course ").append(String.valueOf(pari.getCourseId())).append("\n");
tspl.append(sunmiPrinterManager.separationText()+ "\n");
if(selectedHorses.getValue() == null){
selectedHorses.setValue(Arrays.stream(pari.getCombinaison().split(",")).collect(Collectors.toList()));
}
int requireHorseNumber = shared != null && shared.typeOfBet != null? shared.typeOfBet.getValue().getNumberOfHorse():listProduits.get(pari.getTypesParisMises().get(0));
selectedHorses.setValue(Arrays.stream(pari.getCombinaison().split(",")).collect(Collectors.toList()));
int requireHorseNumber = listProduits.get(pari.getTypesParisMises().get(0).getTypePari());
boolean isElargie = selectedHorses.getValue().size() > requireHorseNumber;
String typePari = pari.getTypesParisMises().get(0).getTypePari();
tspl.append(isElargie ? typePari + "/Elargie" : typePari).append("\n");
@@ -652,7 +652,7 @@ public class BetValidation extends Fragment {
tspl.append(combinationText).append("\n");
tspl.append("COEF: ").append(String.valueOf(pari.getCoefficient()));
tspl.append("\n").append(sunmiPrinterManager.separationText()).append("\n");
tspl.append("MONTANT: ").append(pari.getTypesParisMises().get(0).getMiseTotale()).append(" XOF");
tspl.append("MONTANT: ").append( BitMapUtils.formatMontant(pari.getTypesParisMises().get(0).getMiseTotale())).append(" XOF");
tspl.append("\n").append(sunmiPrinterManager.separationText()).append("\n");
tspl.append("AGENT: ").append(prefsHelper.get("code")).append("\n");
tspl.append("DATE: ").append(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss").format(OffsetDateTime.parse(pari.getDateHeurePrise()))).append("\n");
@@ -678,9 +678,7 @@ public class BetValidation extends Fragment {
printPari(pari);
dlg.dismiss();
prefsHelper.save("pariNoPrintId", null);
} catch (WriterException e) {
throw new RuntimeException(e);
} catch (FileNotFoundException e) {
} catch (WriterException | FileNotFoundException e) {
throw new RuntimeException(e);
}
})
@@ -690,18 +688,19 @@ public class BetValidation extends Fragment {
});
}else{
prefsHelper.save("pariNoPrintId", null);
binding.mise.setText("0 CFA");
}
}
});
_initializeToZero();
return;
}
// MobiIotPrinterManager.getInstance().testImagePrinting(bitmap);
MobiIotPrinterManager.getInstance().printPari(BitMapUtils.bitmapToBmp(BitMapUtils.resizeToPrinterWidth(bitmap, 384)), BitMapUtils.bitmapToBmp(BitMapUtils.resizeToPrinterWidth(barcode, 384)), pari.getNumeroTicket(), tspl.toString(), new MobiIotPrinterManager.MobiIotPrinterStatus() {
MobiIotPrinterManager.getInstance().printPari(BitMapUtils.bitmapToBmp(BitMapUtils.resizeToPrinterWidth(bitmap, 384)), BitMapUtils.bitmapToBmp(BitMapUtils.resizeToPrinterWidth(barcode, 384)), pari.getNumeroTicket(), tspl, new MobiIotPrinterManager.MobiIotPrinterStatus() {
@Override
public void printStatusCode(int status) {
Log.e("TAG", "### "+String.valueOf(status));
if(status!=1){
prefsHelper.save("pariNoPrintId", String.valueOf(pari.getNumeroTicket()));
new Handler(Looper.getMainLooper()).post(new Runnable() {
@@ -738,7 +737,7 @@ public class BetValidation extends Fragment {
@Override
public void run() {
prefsHelper.save("pariNoPrintId", null);
_initializeToZero();
binding.mise.setText("0 CFA");
}
});
}
@@ -843,16 +842,16 @@ public class BetValidation extends Fragment {
}
}
}
try {
int printerStatus = MobiIotPrinterManager.getInstance().getPrinterStatus();
Log.e("TAG", "### "+String.valueOf(printerStatus));
if(printerStatus != 1){
MessageDialog.showError(getContext(), "Erreur lors de l'impréssion."+"\n"+" Veuillez vérifier le papier puis rééssayer!");
return;
}else{
try {
int printerStatus = MobiIotPrinterManager.getInstance().getPrinterStatus();
if(printerStatus != 1){
MessageDialog.showError(getContext(), "Erreur lors de l'impréssion."+"\n"+" Veuillez vérifier le papier puis rééssayer!");
return;
}
} catch (RemoteException e) {
throw new RuntimeException(e);
}
} catch (RemoteException e) {
throw new RuntimeException(e);
}
pariViewModel.createPari(pari).observe(getViewLifecycleOwner(), new Observer<Result<ParisResponse>>() {
@Override
@@ -862,14 +861,17 @@ public class BetValidation extends Fragment {
loader.dismiss();
dialog.dismiss();
MessageDialog.showError(getContext(), pariResult.message);
view.findViewById(R.id.alert_validate).setEnabled(false);
break;
case LOADING:
loader.show("Prise de pari");
view.findViewById(R.id.alert_validate).setEnabled(false);
break;
case SUCCESS:
try {
loader.dismiss();
logsViewModel.insertLog(prefsHelper.get("id"), "BET", "Création du pari " + pariResult.data.getNumeroTicket() + ", type de paris: " + pariResult.data.getTypesParisMises().get(0).getTypePari() + ", combinaison:" + selectedHorses.getValue().stream().map(String::valueOf).collect(Collectors.joining("-")), System.currentTimeMillis());
_initializeToZero();
printPari(pariResult.data);
dialog.dismiss();
MessageDialog.showSuccess(getContext(), "Pari créé avec succès");
@@ -892,7 +894,7 @@ public class BetValidation extends Fragment {
}
void _initializeToZero() {
selectedHorses.setValue(List.of());
selectedHorses.postValue(new ArrayList<>());
binding.combination.setText("");
binding.order.setChecked(false);
order = false;

View File

@@ -88,6 +88,9 @@ public class Caisse extends Fragment {
binding.lastBetsBtn.setOnClickListener(v -> {
authNavigator.navigate(DerniersParis.newInstance());
});
binding.raceReport.setOnClickListener(v -> {
authNavigator.navigate(RaceReport.newInstance());
});
}
@Override

View File

@@ -198,7 +198,8 @@ public class DerniersParis extends Fragment {
}
void _cancelPari(ParisResponse pari){
viewModel.annulerPari(pari.getNumeroTicket()).observe(getViewLifecycleOwner(), new Observer<Result<ParisResponse>>() {
String agentId = prefsHelper.get("id");
viewModel.annulerPari(pari.getNumeroTicket(), agentId).observe(getViewLifecycleOwner(), new Observer<Result<ParisResponse>>() {
@Override
public void onChanged(Result<ParisResponse> result) {
switch (result.status){
@@ -313,7 +314,7 @@ public class DerniersParis extends Fragment {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
String formattedDate = dateTime.format(formatter);
tspl.append(formattedDate).append("\n");
tspl.append("Course ").append(String.valueOf(pari.getCourseId())).append("\n");
tspl.append("Course ").append(BitMapUtils.formatMontant(pari.getCourseId())).append("\n");
tspl.append(sunmiPrinterManager.separationText()+"\n");
List<String> selectedHorses = Arrays.stream(pari.getCombinaison().split(",")).collect(Collectors.toList());
boolean isElargie = selectedHorses.size()> listProduits.get(pari.getTypesParisMises().get(0).getTypePari());
@@ -322,9 +323,9 @@ public class DerniersParis extends Fragment {
tspl.append(pari.getFormules().contains("FORMULE_COMPLETE") ?"COMBINAISON COMPLETE"+"\n":"");
String combinationText = BitMapUtils.formatLineWithNumbers(selectedHorses.stream().map(String::valueOf).toArray(String[]::new), "-", listProduits.get(pari.getTypesParisMises().get(0).getTypePari()));
tspl.append(combinationText).append("\n");
tspl.append("COEF: ").append(String.valueOf(pari.getCoefficient()));
tspl.append("COEF: ").append(BitMapUtils.formatMontant(pari.getCoefficient()));
tspl.append("\n").append(sunmiPrinterManager.separationText()).append("\n");
tspl.append("MONTANT: ").append(pari.getMiseTotale()).append(" XOF");
tspl.append("MONTANT: ").append(BitMapUtils.formatMontant(pari.getMiseTotale())).append(" XOF");
tspl.append("\n").append(sunmiPrinterManager.separationText()).append("\n");
tspl.append("AGENT: ").append(pari.getAgentCode()).append("\n");
tspl.append("DATE: ").append(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss").format(OffsetDateTime.parse(pari.getDateHeurePrise()))).append("\n");
@@ -348,7 +349,7 @@ public class DerniersParis extends Fragment {
});
return;
}
MobiIotPrinterManager.getInstance().printPari(BitMapUtils.bitmapToBmp(BitMapUtils.resizeToPrinterWidth(bitmap, 384)), BitMapUtils.bitmapToBmp(barcode), pari.getNumeroTicket(), tspl.toString(), new MobiIotPrinterManager.MobiIotPrinterStatus() {
MobiIotPrinterManager.getInstance().printPari(BitMapUtils.bitmapToBmp(BitMapUtils.resizeToPrinterWidth(bitmap, 384)), BitMapUtils.bitmapToBmp(barcode), pari.getNumeroTicket(), tspl, new MobiIotPrinterManager.MobiIotPrinterStatus() {
@Override
public void printStatusCode(int status) {
if(status != 1){

View File

@@ -11,9 +11,11 @@ import androidx.lifecycle.ViewModelProvider;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.example.quiz.databinding.FragmentLoginBinding;
import com.example.quiz.utils.AuthNavigator;
import com.example.quiz.utils.MessageDialog;
import com.example.quiz.utils.SessionManager;
import com.example.quiz.utils.SharedPrefsHelper;
import com.example.quiz.viewModel.LoginViewModel;
@@ -31,6 +33,10 @@ public class Login extends Fragment {
private AuthNavigator authNavigator;
private int clickCount = 0;
private long lastClickTime = 0;
private static final int CLICKS_REQUIRED = 5;
private static final long TIME_WINDOW = 2000;
private SharedPrefsHelper prefsHelper;
public Login() {
// Required empty public constructor
@@ -62,13 +68,33 @@ public class Login extends Fragment {
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
SessionManager sessionManager = SessionManager.newInstance(getContext());
FragmentManager fragmentManager = getParentFragmentManager();
AppCompatActivity activity = (AppCompatActivity) getActivity();
if(activity != null){
activity.getSupportActionBar().hide();
}
binding.icon.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
long currentTime = System.currentTimeMillis();
if (currentTime - lastClickTime > TIME_WINDOW) {
clickCount = 0;
} else {
clickCount++;
}
lastClickTime = currentTime;
if(clickCount == CLICKS_REQUIRED){
clickCount = 0;
ApiUrlConfiguration apiUrlConfiguration = ApiUrlConfiguration.newInstance();
FragmentManager fragmentManager = getParentFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.nav_host_fragment_content_main, apiUrlConfiguration)
.addToBackStack(null)
.commit();
}
}
});
}
public void onStart(){

View File

@@ -62,6 +62,7 @@ public class PageQuiz extends AppCompatActivity {
super.onCreate(savedInstanceState);
exemptedFragment.add(ServerConfig.class);
exemptedFragment.add(Logs.class);
exemptedFragment.add(ApiUrlConfiguration.class);
sessionManager = SessionManager.newInstance(this);
mobileName = Build.MANUFACTURER;
binding = ActivityPageQuizBinding.inflate(getLayoutInflater());

View File

@@ -0,0 +1,231 @@
package com.example.quiz;
import android.app.AlertDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.quiz.data.model.enums.TypeProduits;
import com.example.quiz.databinding.FragmentRaceReportBinding;
import com.example.quiz.utils.BitMapUtils;
import com.example.quiz.utils.LoaderDialog;
import com.example.quiz.utils.MessageDialog;
import com.example.quiz.utils.MobiIotPrinterManager;
import com.example.quiz.utils.Result;
import com.example.quiz.utils.SunmiPrinterManager;
import com.example.quiz.viewModel.RapportCourseViewModel;
import dagger.hilt.android.AndroidEntryPoint;
/**
* A simple {@link Fragment} subclass.
* Use the {@link RaceReport#newInstance} factory method to
* create an instance of this fragment.
*/
@AndroidEntryPoint
public class RaceReport extends Fragment {
FragmentRaceReportBinding binding;
SunmiPrinterManager sunmiPrinterManager;
String mobileName;
MutableLiveData<Boolean> isPrinterReady = new MutableLiveData<>(false);
LoaderDialog loader;
RapportCourseViewModel rapportCourseViewModel;
public RaceReport() {
// Required empty public constructor
}
// TODO: Rename and change types and number of parameters
public static RaceReport newInstance() {
RaceReport fragment = new RaceReport();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
rapportCourseViewModel = new ViewModelProvider(this).get(RapportCourseViewModel.class);
loader = new LoaderDialog(getContext());
mobileName = Build.MANUFACTURER;
sunmiPrinterManager = SunmiPrinterManager.getInstance(requireContext());
if(mobileName.toLowerCase().contains("sunmi")){
sunmiPrinterManager.connectPrinter(status -> {
isPrinterReady.setValue(status);
sunmiPrinterManager.disableSystemMessages();
});
}else{
MobiIotPrinterManager.getInstance().init(requireContext());
}
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentRaceReportBinding.inflate(inflater, container, false);
// Inflate the layout for this fragment
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.btnCheckBalance.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(binding.etRaceNumber.getText().toString().isEmpty()){
binding.etRaceNumber.setError("Veuillez entrer un numéro de course");
return;
}
rapportCourseViewModel.getRaceReport(binding.etRaceNumber.getText().toString()).observe(getViewLifecycleOwner(), new Observer<Result<com.example.quiz.data.model.RaceReport>>() {
@Override
public void onChanged(Result<com.example.quiz.data.model.RaceReport> raceReportResult) {
switch (raceReportResult.status){
case LOADING:{
loader.show("Chargement du rapport");
break;
}
case ERROR:{
loader.dismiss();
MessageDialog.showError(getContext(), raceReportResult.message);
break;
}
case SUCCESS:{
loader.dismiss();
new AlertDialog.Builder(getContext()).setTitle("Rapport")
.setMessage("Voulez-vous imprimer le rapport ?")
.setPositiveButton("Oui", (dialog, which) -> {
try {
printRapport(raceReportResult.data);
dialog.dismiss();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
})
.setNegativeButton("Non", (dialog, which) -> {
dialog.dismiss();
}).show();
break;
}
}
}
});
}
});
}
void printRapport(com.example.quiz.data.model.RaceReport report) throws RemoteException {
Bitmap logo = BitmapFactory.decodeResource(getResources(), R.drawable.pmu_logo);
String courseCode = report.getCodeCourse()+" "+report.getDateCourse();
String hippodomeName = report.getHippodrome();
String courseName = report.getNomCourse() != null?report.getNomCourse().toUpperCase():"Non défini";
String arrivee = report.getArrivee();
String nonPartants = !report.getNonPartants().equals("néant".toUpperCase())?report.getNonPartants():" ";
StringBuilder text = new StringBuilder();
if(report.getDetailsGagnantsGroupes().get(TypeProduits.QUINTE) != null && report.getDetailsGagnantsGroupes().get(TypeProduits.QUINTE).size() > 0){
text.append("Quinté+").append("\n");
report.getDetailsGagnantsGroupes().get(TypeProduits.QUINTE).forEach((item)->{
text.append(item.getLibelle()).append(" : ").append(BitMapUtils.formatMontant(item.getMassePayee())).append(" XOF").append("\n");
});
text.append("\n");
}
if(report.getDetailsGagnantsGroupes().get(TypeProduits.QUARTE) != null && report.getDetailsGagnantsGroupes().get(TypeProduits.QUARTE).size() > 0){
text.append("Quarté").append("\n");
report.getDetailsGagnantsGroupes().get(TypeProduits.QUARTE).forEach((item)->{
text.append(item.getLibelle()+" : ").append(BitMapUtils.formatMontant(item.getMassePayee())).append(" XOF").append("\n");
});
text.append("\n");
}
if(report.getDetailsGagnantsGroupes().get(TypeProduits.TIERCE) != null && report.getDetailsGagnantsGroupes().get(TypeProduits.TIERCE).size() > 0){
text.append("Tiercé").append("\n");
report.getDetailsGagnantsGroupes().get(TypeProduits.TIERCE).forEach((item)->{
text.append(item.getLibelle()+" : ").append(BitMapUtils.formatMontant(item.getMassePayee())).append(" XOF").append("\n");
});
text.append("\n");
}
if(report.getDetailsGagnantsGroupes().get(TypeProduits.COUPLE_GAGNANT) != null && report.getDetailsGagnantsGroupes().get(TypeProduits.COUPLE_GAGNANT).size() > 0){
text.append("Couplé gagnant").append("\n");
report.getDetailsGagnantsGroupes().get(TypeProduits.COUPLE_GAGNANT).forEach((item)->{
text.append(item.getLibelle()+" : ").append(BitMapUtils.formatMontant(item.getMassePayee())).append(" XOF").append("\n");
});
text.append("\n");
}
if(report.getDetailsGagnantsGroupes().get(TypeProduits.COUPLE_PLACE) != null && report.getDetailsGagnantsGroupes().get(TypeProduits.COUPLE_PLACE).size() > 0){
text.append("Couplé placé").append("\n");
report.getDetailsGagnantsGroupes().get(TypeProduits.COUPLE_PLACE).forEach((item)->{
text.append(item.getLibelle()+" : ").append(BitMapUtils.formatMontant(item.getMassePayee())).append(" XOF").append("\n");
});
text.append("\n");
}
if(mobileName.toLowerCase().contains("sunmi")){
if(isPrinterReady.getValue() == null || !isPrinterReady.getValue()){
sunmiPrinterManager.connectPrinter(status ->{
isPrinterReady.setValue(status);
});
}
if(sunmiPrinterManager.printerStatus() != 1){
MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
return;
}
sunmiPrinterManager.printRapport(BitMapUtils.resizeToPrinterWidth(logo, 384), courseCode, hippodomeName, courseName, arrivee, nonPartants, text, new SunmiPrinterManager.PrintStatusListener() {
@Override
public void onStatusChanged(boolean status) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
if(!status){
MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
return;
}
MessageDialog.showSuccess(getContext(), "Rapport imprimé avec succès");
}
});
}
});
}else{
if(MobiIotPrinterManager.getInstance().getPrinterStatus() != 1){
MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
return;
}
MobiIotPrinterManager.getInstance().printRapport(BitMapUtils.bitmapToBmp(BitMapUtils.resizeToPrinterWidth(logo, 384)), courseCode, hippodomeName, courseName, arrivee, nonPartants, text, new MobiIotPrinterManager.MobiIotPrinterStatus() {
@Override
public void printStatusCode(int status) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
if(status == 1 ){
MessageDialog.showSuccess(getContext(), "Rapport imprimé avec succès");
}else{
MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
}
}
});
}
});
}
}
}

View File

@@ -118,7 +118,8 @@ public class ServerConfig extends Fragment {
case SUCCESS:
loader.dismiss();
existingTpe = tpeResult.data;
if( existingTpe != null && Objects.equals(sharedPrefsHelper.get("terminalId"), String.valueOf(existingTpe.getId()))){
String numeroSerie = Settings.Secure.getString(getContext().getContentResolver(), Settings.Secure.ANDROID_ID);
if(existingTpe != null && existingTpe.getNumeroSerie().equals(numeroSerie)){
_checkPdv(existingTpe);
return;
}else{

View File

@@ -218,13 +218,13 @@ public class Sold extends Fragment {
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("VENTES HIPPIQUES: ").append(BitMapUtils.formatMontant(soldeResponse.getMontantParis())).append(" XOF").append("\n");
text.append("NBR. PAIEMENTS: ").append(BitMapUtils.formatMontant(soldeResponse.getNombrePaiements())).append("\n");
text.append("PAIEMENTS: ").append(BitMapUtils.formatMontant(soldeResponse.getMontantPaiements())).append(" XOF").append("\n");
text.append("NBR. ANNULATIONS: ").append(BitMapUtils.formatMontant(soldeResponse.getNombreAnnulations())).append("\n");
text.append("ANNULATIONS: ").append(BitMapUtils.formatMontant(soldeResponse.getMontantAnnulations())).append(" XOF").append("\n");
text.append(sunmiPrinterManager.separationText()).append("\n");
text.append("SOLDE: ").append(String.valueOf(solde)).append(" XOF").append("\n");
text.append("SOLDE: ").append(BitMapUtils.formatMontant(solde)).append(" XOF").append("\n");
text.append(sunmiPrinterManager.separationText()).append("\n");
text.append("AGENT : ").append(prefsHelper.get("code")).append("\n");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");

View File

@@ -173,13 +173,13 @@ public class SoldByCourse extends Fragment {
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("VENTES HIPPIQUES: ").append(BitMapUtils.formatMontant(soldeResponse.getMontantParis())).append(" XOF").append("\n");
text.append("NBR. PAIEMENTS: ").append(BitMapUtils.formatMontant(soldeResponse.getNombrePaiements())).append("\n");
text.append("PAIEMENTS: ").append(BitMapUtils.formatMontant(soldeResponse.getMontantPaiements())).append(" XOF").append("\n");
text.append("NBR. ANNULATIONS: ").append(BitMapUtils.formatMontant(soldeResponse.getNombreAnnulations())).append("\n");
text.append("ANNULATIONS: ").append(BitMapUtils.formatMontant(soldeResponse.getMontantAnnulations())).append(" XOF").append("\n");
text.append(sunmiPrinterManager.separationText()).append("\n");
text.append("SOLDE: ").append(String.valueOf(solde)).append(" XOF").append("\n");
text.append("SOLDE: ").append(BitMapUtils.formatMontant(solde)).append(" XOF").append("\n");
text.append(sunmiPrinterManager.separationText()).append("\n");
text.append("AGENT : ").append(prefsHelper.get("code")).append("\n");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");

View File

@@ -1,5 +1,7 @@
package com.example.quiz;
import android.content.Context;
import com.example.quiz.data.remote.NotificationHelper;
import com.example.quiz.data.remote.StompManager;
import com.example.quiz.data.remote.TokenManager;
@@ -9,6 +11,7 @@ import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import dagger.hilt.InstallIn;
import dagger.hilt.android.qualifiers.ApplicationContext;
import dagger.hilt.components.SingletonComponent;
@Module
@@ -18,7 +21,7 @@ public class StompModule {
@Provides
@Singleton
public StompManager provideStompManager(TokenManager tokenManager,
NotificationHelper notificationHelper) {
return new StompManager(tokenManager, notificationHelper);
NotificationHelper notificationHelper, @ApplicationContext Context context) {
return new StompManager(tokenManager, notificationHelper, context);
}
}

View File

@@ -0,0 +1,47 @@
package com.example.quiz.data.model;
public class GainRapportProduit {
private String libelle;
private double nombre;
private double rapportUnitaire;
private double massePayee;
public GainRapportProduit(String libelle, double nombre, double rapportUnitaire, double massePayee) {
this.libelle = libelle;
this.nombre = nombre;
this.rapportUnitaire = rapportUnitaire;
this.massePayee = massePayee;
}
public String getLibelle() {
return libelle;
}
public void setLibelle(String libelle) {
this.libelle = libelle;
}
public double getNombre() {
return nombre;
}
public void setNombre(double nombre) {
this.nombre = nombre;
}
public double getRapportUnitaire() {
return rapportUnitaire;
}
public void setRapportUnitaire(double rapportUnitaire) {
this.rapportUnitaire = rapportUnitaire;
}
public double getMassePayee() {
return massePayee;
}
public void setMassePayee(double massePayee) {
this.massePayee = massePayee;
}
}

View File

@@ -0,0 +1,93 @@
package com.example.quiz.data.model;
import com.example.quiz.data.model.enums.TypeProduits;
import java.util.List;
import java.util.Map;
public class RaceReport {
private String dateCourse;
private String hippodrome;
private String codeCourse;
private String nomCourse;
private int nbrePartants;
private String arrivee;
private String nonPartants;
private Map<TypeProduits, List<GainRapportProduit>> detailsGagnantsGroupes;
public RaceReport(String dateCourse, String hippodrome, String codeCourse, String nomCourse, int nbrePartants, String arrivee, String nonPartants, Map<TypeProduits, List<GainRapportProduit>> detailsGagnantsGroupes) {
this.dateCourse = dateCourse;
this.hippodrome = hippodrome;
this.codeCourse = codeCourse;
this.nomCourse = nomCourse;
this.nbrePartants = nbrePartants;
this.arrivee = arrivee;
this.nonPartants = nonPartants;
this.detailsGagnantsGroupes = detailsGagnantsGroupes;
}
public String getNomCourse() {
return nomCourse;
}
public void setNomCourse(String nomCourse) {
this.nomCourse = nomCourse;
}
public String getDateCourse() {
return dateCourse;
}
public void setDateCourse(String dateCourse) {
this.dateCourse = dateCourse;
}
public String getHippodrome() {
return hippodrome;
}
public void setHippodrome(String hippodrome) {
this.hippodrome = hippodrome;
}
public String getCodeCourse() {
return codeCourse;
}
public void setCodeCourse(String codeCourse) {
this.codeCourse = codeCourse;
}
public int getNbrePartants() {
return nbrePartants;
}
public void setNbrePartants(int nbrePartants) {
this.nbrePartants = nbrePartants;
}
public String getArrivee() {
return arrivee;
}
public void setArrivee(String arrivee) {
this.arrivee = arrivee;
}
public String getNonPartants() {
return nonPartants;
}
public void setNonPartants(String nonPartants) {
this.nonPartants = nonPartants;
}
public Map<TypeProduits, List<GainRapportProduit>> getDetailsGagnantsGroupes() {
return detailsGagnantsGroupes;
}
public void setDetailsGagnantsGroupes(Map<TypeProduits, List<GainRapportProduit>> detailsGagnantsGroupes) {
this.detailsGagnantsGroupes = detailsGagnantsGroupes;
}
}

View File

@@ -0,0 +1,10 @@
package com.example.quiz.data.model.enums;
public enum TypeProduits {
QUINTE,
QUARTE,
TIERCE,
COUPLE_GAGNANT,
COUPLE_PLACE
}

View File

@@ -1,9 +1,12 @@
package com.example.quiz.data.remote;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import com.example.quiz.utils.SharedPrefsHelper;
import java.util.concurrent.TimeUnit;
import javax.inject.Singleton;
@@ -11,6 +14,7 @@ import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import dagger.hilt.InstallIn;
import dagger.hilt.android.qualifiers.ApplicationContext;
import dagger.hilt.components.SingletonComponent;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
@@ -20,8 +24,12 @@ import retrofit2.converter.gson.GsonConverterFactory;
@Module
@InstallIn(SingletonComponent.class)
public class ApiClient {
private static final String BASE_URL = "https://alr.pmu.ml/api/";
private static final String DEFAULT_BASE_URL = "https://api-alr.pmu.ml/api/";
private static final String DEFAULT_REPORT_BASE_URL = "https://dep-alr.pmu.ml/api/v1/";
private static final String BASE_URL_KEY = "MAIN_API_URL";
private static final String REPORT_BASE_URL_KEY = "REPORT_API_URL";
@Provides
@Singleton
@@ -55,7 +63,9 @@ public class ApiClient {
@Provides
@Singleton
public Retrofit provideRetrofit(OkHttpClient okHttpClient){
public Retrofit provideRetrofit(OkHttpClient okHttpClient, @ApplicationContext Context context){
SharedPrefsHelper prefsHelper = SharedPrefsHelper.getInstance(context);
String BASE_URL = prefsHelper.getString(BASE_URL_KEY, DEFAULT_BASE_URL);
return new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
@@ -68,4 +78,23 @@ public class ApiClient {
public ApiService provideApiService(Retrofit retrofit){
return retrofit.create(ApiService.class);
}
@Provides
@Singleton
@ReportApiClient
public Retrofit provideReportRetrofit(OkHttpClient okHttpClient, @ApplicationContext Context context){
SharedPrefsHelper prefsHelper = SharedPrefsHelper.getInstance(context);
String REPORT_BASE_URL = prefsHelper.getString(REPORT_BASE_URL_KEY, DEFAULT_REPORT_BASE_URL);
return new Retrofit.Builder()
.baseUrl(REPORT_BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
@Provides
@Singleton
public ReportApiService provideReportApiService(@ReportApiClient Retrofit retrofit){
return retrofit.create(ReportApiService.class);
}
}

View File

@@ -43,7 +43,7 @@ public interface ApiService {
Call<List<ParisResponse>> derniersParis(@Path("agentId") String agentId);
@PATCH("paris/numero/{numeroTicket}/cancel")
Call<ParisResponse> annulerPari(@Path("numeroTicket") String numeroTicket);
Call<ParisResponse> annulerPari(@Path("numeroTicket") String numeroTicket, @Query("agentId") String agentId);
@PATCH("paris/numero/{numeroTicket}/rembourser")
Call<ParisResponse> rembourserPari(@Path("numeroTicket") String numeroTicket);

View File

@@ -0,0 +1,9 @@
package com.example.quiz.data.remote;
import java.lang.annotation.RetentionPolicy;
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@javax.inject.Qualifier
public @interface ReportApiClient{
}

View File

@@ -0,0 +1,12 @@
package com.example.quiz.data.remote;
import com.example.quiz.data.model.RaceReport;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;
public interface ReportApiService {
@GET("rapports/course/{courseId}/fiche-calcul")
Call<RaceReport> getRaceReport(@Path("courseId") String courseId);
}

View File

@@ -1,9 +1,11 @@
package com.example.quiz.data.remote;
import android.content.Context;
import android.util.Log;
import com.example.quiz.data.model.Course;
import com.example.quiz.data.model.dtos.NotifPayload;
import com.example.quiz.utils.SharedPrefsHelper;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
@@ -17,6 +19,7 @@ import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.hilt.android.qualifiers.ApplicationContext;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
@@ -34,6 +37,10 @@ public class StompManager {
private StompClient stompClient;
private boolean isConnected = false;
private String url = "wss://api-alr.pmu.ml/ws";
SharedPrefsHelper prefsHelper;
// Map topic -> liste de listeners
private final Map<String, List<Consumer<String>>> topicListeners = new HashMap<>();
@@ -42,10 +49,40 @@ public class StompManager {
// Map pour stocker les subscriptions par topic
private final Map<String, Disposable> topicSubscriptions = new HashMap<>();
private String genererUrlWebSocket(String httpUrl) {
if (httpUrl == null || httpUrl.isEmpty()) {
return "wss://api-alr.pmu.ml/ws"; // Sécurité au cas où
}
String wsUrl = httpUrl;
// 1. On remplace le protocole au début
if (wsUrl.startsWith("https://")) {
wsUrl = wsUrl.replace("https://", "wss://");
} else if (wsUrl.startsWith("http://")) {
wsUrl = wsUrl.replace("http://", "ws://");
}
// 2. On remplace la route finale "/api/" ou "/api" par "/ws"
if (wsUrl.endsWith("/api/")) {
// Enlève "/api/" (5 caractères) à la fin et ajoute "/ws"
wsUrl = wsUrl.substring(0, wsUrl.length() - 5) + "/ws";
} else if (wsUrl.endsWith("/api")) {
// Enlève "/api" (4 caractères) à la fin et ajoute "/ws"
wsUrl = wsUrl.substring(0, wsUrl.length() - 4) + "/ws";
}
return wsUrl;
}
@Inject
public StompManager(TokenManager tokenManager,
NotificationHelper notificationHelper) {
NotificationHelper notificationHelper, Context context) {
prefsHelper = SharedPrefsHelper.getInstance(context);
String apiUrl = prefsHelper.get("MAIN_API_URL");
if(apiUrl != null){
url = genererUrlWebSocket(apiUrl);
}
this.tokenManager = tokenManager;
this.notificationHelper = notificationHelper;
}
@@ -61,7 +98,7 @@ public class StompManager {
}
// URL de connexion WebSocket
String url = "wss://alr.pmu.ml/ws";
try {
// Créer le client STOMP
@@ -162,12 +199,12 @@ public class StompManager {
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_UPDATED:{
if(updatedCourse.getStatut() == Course.Statut.OUVERT){
notif = "Course "+updatedCourse.getNom()+" a été créée avec succès";
break;
}
}
case COURSE_CANCELLED:
notif = "Course "+updatedCourse.getNom()+" annulée";
break;
@@ -181,7 +218,7 @@ public class StompManager {
notif = "Non partants déclarés pour la course "+updatedCourse.getNom();
break;
}
// Notification
if(notif.isEmpty()) return;
notificationHelper.showNotification(topic, notif);
},
throwable -> {

View File

@@ -45,6 +45,10 @@ public class LoginRepository {
}else{
try {
String error = response.errorBody().string();
if(!error.startsWith("{")){
liveLogin.postValue(Result.error(error));
return;
}
Gson gson = new Gson();
ResponseError errorResponse = gson.fromJson(error, ResponseError.class);
liveLogin.postValue(Result.error(errorResponse.getMessage()));

View File

@@ -148,10 +148,10 @@ public class PariRepository {
return pariResponse;
}
public LiveData<Result<ParisResponse>> annulerPari(String numeroTicket){
public LiveData<Result<ParisResponse>> annulerPari(String numeroTicket, String agentId){
MutableLiveData<Result<ParisResponse>> pariResponse = new MutableLiveData<>();
pariResponse.setValue(Result.loading());
apiService.annulerPari(numeroTicket).enqueue(new Callback<ParisResponse>(){
apiService.annulerPari(numeroTicket, agentId).enqueue(new Callback<ParisResponse>(){
@Override
public void onResponse(Call<ParisResponse> call, Response<ParisResponse> response) {
if(response.isSuccessful()){

View File

@@ -0,0 +1,57 @@
package com.example.quiz.data.repository;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.quiz.data.model.RaceReport;
import com.example.quiz.data.model.ResponseError;
import com.example.quiz.data.remote.ReportApiService;
import com.example.quiz.utils.Result;
import com.google.gson.Gson;
import javax.inject.Inject;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class RapportCourseRepository {
private final ReportApiService reportApiService;
@Inject
public RapportCourseRepository(ReportApiService reportApiService) {
this.reportApiService = reportApiService;
}
public LiveData<Result<RaceReport>> getRaceReport(String courseId) {
MutableLiveData<Result<RaceReport>> raceReport = new MutableLiveData<>();
raceReport.setValue(Result.loading());
reportApiService.getRaceReport(courseId).enqueue(new Callback<RaceReport>() {
@Override
public void onResponse(Call<RaceReport> call, Response<RaceReport> response) {
if(response.isSuccessful() && response.body() != null){
raceReport.postValue(Result.success(response.body()));
}else{
if(response.code() == 404){
raceReport.postValue(Result.error("Aucun rapport disponible pour cette course"));
}else{
try {
String error = response.errorBody().string();
Gson gson = new Gson();
ResponseError errorResponse = gson.fromJson(error, ResponseError.class);
raceReport.postValue(Result.error(errorResponse.getMessage()));
} catch (Exception e) {
raceReport.postValue(Result.error("Erreur serveur"));
}
}
}
}
@Override
public void onFailure(Call<RaceReport> call, Throwable throwable) {
raceReport.postValue(Result.error(throwable.getMessage()));
}
});
return raceReport;
}
}

View File

@@ -103,8 +103,9 @@ public class AuthNavigator {
pin.setError("Le pin doit contenir minimum quatre chiffres!");
return;
}
if(prefsHelper.get("terminalId") == null){
if(prefsHelper== null || prefsHelper.get("terminalId") == null){
MessageDialog.showError(context, "Terminal non trouvé");
return;
}
LoginPayload loginPayload = new LoginPayload(
code.getText().toString(),

View File

@@ -12,9 +12,12 @@ import com.google.zxing.common.BitMatrix;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
public class BitMapUtils {
@@ -154,11 +157,7 @@ public class BitMapUtils {
if(numbers.length <= requiredHorse){
return Arrays.stream(numbers).map(String::valueOf).collect(Collectors.joining("-")).toString();
}
List<String> firstPart = Arrays.stream(numbers).limit(requiredHorse).map(String::valueOf).collect(Collectors.toList());
List<String> secondPart = Arrays.stream(numbers).skip(requiredHorse).map(String::valueOf).collect(Collectors.toList());
formatted.addAll(firstPart);
formatted.add("R");
formatted.addAll(secondPart);
formatted = Arrays.stream(numbers).map(String::valueOf).collect(Collectors.toList());
}else{
formatted = Arrays.stream(numbers).map(String::valueOf).collect(Collectors.toList());
}
@@ -182,4 +181,31 @@ public class BitMapUtils {
return finalOutput.toString();
}
public static String formatMontant(Object nombre) {
if (nombre == null) return "0";
try {
double valeur;
if (nombre instanceof String) {
valeur = Double.parseDouble((String) nombre);
} else {
valeur = ((Number) nombre).doubleValue();
}
// On force les espaces comme séparateurs de milliers pour la lisibilité
DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.FRANCE);
symbols.setGroupingSeparator(' ');
// ###,### : Regroupe par milliers
// .## : Garde max 2 décimales si elles existent (ex: 3178000.50 -> 3 178 000.5)
DecimalFormat df = new DecimalFormat("###,###", symbols);
return df.format(valeur);
} catch (Exception e) {
// Sécurité si la String n'est vraiment pas un nombre
return String.valueOf(nombre);
}
}
}

View File

@@ -14,11 +14,17 @@ import android.util.Log;
import com.sagereal.printer.PrinterInterface;
import java.io.ByteArrayOutputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MobiIotPrinterManager {
private PrinterInterface mPrinter;
private static MobiIotPrinterManager instance;
private boolean isBinding = false;
private final ExecutorService printExecutor = Executors.newSingleThreadExecutor();
String TAG = "MOBITAG";
private static final int MAX_CHAR_2_INCH = 32;
@@ -51,6 +57,8 @@ public class MobiIotPrinterManager {
}
};
private void testPrinterStatus() {
new Thread(() -> {
try {
@@ -122,39 +130,40 @@ public class MobiIotPrinterManager {
}).start();
}
public void printPari(byte[] logo, byte[] barcode, String numeroTicket, String pariText, MobiIotPrinterStatus listener) {
public void printPari(byte[] logo, byte[] barcode, String numeroTicket, StringBuilder pariText, MobiIotPrinterStatus listener) {
if (mPrinter == null) {
Log.e("PrinterManager", "❌ L'imprimante n'est pas connectée");
listener.printStatusCode(0);
return;
}
new Thread(()->{
printExecutor.execute(()->{
try {
int status = mPrinter.getPrinterStatus();
if (status != 1) {
listener.printStatusCode(2);
mPrinter.printBitmap_in(logo);
if(mPrinter.getPrinterStatus() != 1){
listener.printStatusCode(0);
return;
}
mPrinter.printBitmap_in(logo);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 1, false, false);
mPrinter.printBitmap_in(barcode);
mPrinter.printText_FullParm("\n" + numeroTicket, 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm(pariText, 0, 0, 0, 0, false, false);
//String[] lines = pariText.toString().split("\n");
mPrinter.printText_FullParm(pariText.toString(), 0, 0, 0, 0, false, false);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm("Powered by PMU-MALI", 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 0, false, false);
mPrinter.printText_FullParm("\n\n\n\n\n\n", 0, 10, 0, 0, false, false);
Thread.sleep(3000);
if (getPrinterStatus() != 1) {
Thread.sleep(3500);
if(mPrinter.getPrinterStatus() != 1){
mPrinter.printText_FullParm("\u001B@", 0, 0, 0, 0, false, false);
listener.printStatusCode(0);
} else {
listener.printStatusCode(1);
return;
}
listener.printStatusCode(1);
} catch (RemoteException | InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
});
}
public void printSold(byte[] logo, String title, StringBuilder text, MobiIotPrinterStatus listener) throws RemoteException {
@@ -163,7 +172,7 @@ public class MobiIotPrinterManager {
listener.printStatusCode(0);
return;
}
new Thread(()->{
printExecutor.execute(()->{
try {
mPrinter.printBitmap_in(logo);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 1, false, false);
@@ -174,20 +183,51 @@ public class MobiIotPrinterManager {
mPrinter.printText_FullParm("Powered by PMU-MALI", 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 0, false, false);
mPrinter.printText_FullParm("\n\n\n\n\n\n", 0, 10, 0, 0, false, false);
Thread.sleep(3000);
if (getPrinterStatus() != 1) {
Thread.sleep(3500);
if(mPrinter.getPrinterStatus() != 1){
listener.printStatusCode(0);
} else {
listener.printStatusCode(1);
return;
}
listener.printStatusCode(1);
} catch (RemoteException | InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
});
}
public void printRapport(byte[] logo, String courseCode, String hippodromeName, String courseName, String arrivee, String nonPartants, StringBuilder text, MobiIotPrinterStatus listener) throws RemoteException {
printExecutor.execute(()->{
try{
mPrinter.printBitmap_in(logo);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm(courseCode, 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm(hippodromeName, 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm(courseName, 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm("ARRIVEE : "+ arrivee, 0, 0, 0, 0, false, false);
mPrinter.printText_FullParm("NON PARTANTS : "+ nonPartants, 0, 0, 0, 0, false, false);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm(text.toString(), 0, 0, 0, 0, false, false);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm("DATE : "+ DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss").format(LocalDateTime.now()), 0, 0, 0, 0, false, false);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm("Powered by PMU-MALI", 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm("\n\n\n\n\n\n", 0, 10, 0, 0, false, false);
Thread.sleep(3500);
if(mPrinter.getPrinterStatus() != 1){
listener.printStatusCode(0);
return;
}
listener.printStatusCode(1);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
public int getPrinterStatus() throws RemoteException {
return mPrinter.getPrinterStatus();
}

View File

@@ -28,6 +28,14 @@ public class SharedPrefsHelper {
return prefs.getString(key, null);
}
public String getString(String key, String defaultValue) {
return prefs.getString(key, defaultValue);
}
public SharedPreferences.Editor edit(){
return prefs.edit();
}
public void clear() {
prefs.edit().clear().apply();
}

View File

@@ -10,6 +10,8 @@ import com.sunmi.peripheral.printer.InnerPrinterManager;
import com.sunmi.peripheral.printer.InnerResultCallback;
import com.sunmi.peripheral.printer.SunmiPrinterService;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.function.Consumer;
@@ -288,6 +290,165 @@ public class SunmiPrinterManager {
}
}
public void printRapport(Bitmap logo, String courseCode, String hippodromeName, String courseName, String arrivee, String nonPartants, StringBuilder text, PrintStatusListener listener){
try{
sunmiPrinter.enterPrinterBuffer(true);
printSimpleImage(logo);
printTextSimple(separationText());
setAlignment(1, new PrinterListener() {
@Override
public void onSuccess(boolean isSuccess) {
}
@Override
public void onResult(int code) {
}
@Override
public void onError(String errorMessage) {
}
});
printTextSimple(courseCode);
setAlignment(1, new PrinterListener() {
@Override
public void onSuccess(boolean isSuccess) {
}
@Override
public void onResult(int code) {
}
@Override
public void onError(String errorMessage) {
}
});
printTextSimple(hippodromeName);
printTextSimple(courseName);
printTextSimple(separationText());
setAlignment(0, new PrinterListener() {
@Override
public void onSuccess(boolean isSuccess) {
}
@Override
public void onResult(int code) {
}
@Override
public void onError(String errorMessage) {
}
});
printTextSimple("ARRIVEE : "+ arrivee);
setAlignment(0, new PrinterListener() {
@Override
public void onSuccess(boolean isSuccess) {
}
@Override
public void onResult(int code) {
}
@Override
public void onError(String errorMessage) {
}
});
printTextSimple("NON PARTANTS : "+ nonPartants);
printTextSimple(separationText());
setAlignment(0, new PrinterListener() {
@Override
public void onSuccess(boolean isSuccess) {
}
@Override
public void onResult(int code) {
}
@Override
public void onError(String errorMessage) {
}
});
printTextSimple(text.toString());
printTextSimple(separationText());
setAlignment(1, new PrinterListener() {
@Override
public void onSuccess(boolean isSuccess) {
}
@Override
public void onResult(int code) {
}
@Override
public void onError(String errorMessage) {
}
});
printTextSimple("DATE : "+ DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss").format(LocalDateTime.now()));
printTextSimple(separationText());
setAlignment(1, new PrinterListener() {
@Override
public void onSuccess(boolean isSuccess) {
}
@Override
public void onResult(int code) {
}
@Override
public void onError(String errorMessage) {
}
});
printTextSimple("Powered by PMU-MALI");
printTextSimple(separationText());
printTextSimple(" ");
printTextSimple(" ");
printTextSimple(" ");
sunmiPrinter.exitPrinterBufferWithCallback(true, new InnerResultCallback() {
@Override
public void onRunResult(boolean isSuccess) throws RemoteException {
}
@Override
public void onReturnString(String result) throws RemoteException {
}
@Override
public void onRaiseException(int code, String msg) throws RemoteException {
}
@Override
public void onPrintResult(int code, String msg) throws RemoteException {
listener.onStatusChanged(code == 0);
}
});
}catch (Exception e){
throw new RuntimeException(e);
}
}
private boolean readyForPrinting(){
return printerStatus() == 1;
}

View File

@@ -50,8 +50,8 @@ public class PariViewModel extends ViewModel {
return pariRepository.derniersParis(agentId);
}
public LiveData<Result<ParisResponse>> annulerPari(String numeroTicket){
pariAnnule = pariRepository.annulerPari(numeroTicket);
public LiveData<Result<ParisResponse>> annulerPari(String numeroTicket, String agentId){
pariAnnule = pariRepository.annulerPari(numeroTicket, agentId);
return pariAnnule;
}

View File

@@ -0,0 +1,28 @@
package com.example.quiz.viewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.example.quiz.data.model.RaceReport;
import com.example.quiz.data.repository.RapportCourseRepository;
import com.example.quiz.utils.Result;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class RapportCourseViewModel extends ViewModel {
private final RapportCourseRepository rapportCourseRepository;
private LiveData<Result<RaceReport>> raceReport = new MutableLiveData<Result<RaceReport>>();
@Inject
public RapportCourseViewModel(RapportCourseRepository rapportCourseRepository) {
this.rapportCourseRepository = rapportCourseRepository;
}
public LiveData<Result<RaceReport>> getRaceReport(String courseId) {
raceReport = rapportCourseRepository.getRaceReport(courseId);
return raceReport;
}
}

View File

@@ -0,0 +1,173 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:fitsSystemWindows="true"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:background="#F5F7FA"
tools:context=".ApiUrlConfiguration">
<!-- Section Logo -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/icon"
android:layout_width="200dp"
android:layout_height="80dp"
android:clickable="true"
android:contentDescription="@string/logo"
android:scaleType="fitCenter"
android:src="@drawable/pmu_logo"
android:tag="tag_pin_exempt" />
</LinearLayout>
<!-- Section Formulaire -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="24dp"
android:paddingVertical="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Configuration API"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#333333"
android:layout_marginBottom="16dp" />
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="12dp"
app:cardElevation="2dp"
app:cardBackgroundColor="#FFFFFF"
android:layout_marginBottom="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="URL principale"
android:textSize="12sp"
android:textColor="#666666"
android:layout_marginBottom="8dp" />
<EditText
android:id="@+id/server_link"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="Entrez l'URL du serveur"
android:textSize="14sp"
android:inputType="textUri"
android:background="@drawable/edittext_outline_white"
android:paddingStart="12dp"
android:paddingEnd="12dp" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="12dp"
app:cardElevation="2dp"
app:cardBackgroundColor="#FFFFFF"
android:layout_marginTop="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="URL dépouillement"
android:textSize="12sp"
android:textColor="#666666"
android:layout_marginBottom="8dp" />
<EditText
android:id="@+id/dep_link"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="Entrez l'URL de secours"
android:textSize="14sp"
android:inputType="textUri"
android:background="@drawable/edittext_outline_white"
android:paddingStart="12dp"
android:paddingEnd="12dp" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
<!-- Section Boutons -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="24dp"
android:paddingVertical="20dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:baselineAligned="false">
<Button
android:id="@+id/btn_cancel"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1"
android:text="ANNULER"
android:textAllCaps="true"
android:textSize="14sp"
android:background="@drawable/rounded_button_red"
android:textColor="@color/white"
android:layout_marginEnd="8dp"
style="?android:attr/borderlessButtonStyle" />
<Button
android:id="@+id/btn_validate"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1"
android:background="@drawable/rounded_button_green"
android:text="VALIDER"
android:textAllCaps="true"
android:textSize="14sp"
android:textColor="#FFFFFF"
android:layout_marginStart="8dp"
style="?android:attr/borderlessButtonStyle" />
</LinearLayout>
<!-- Espace pour la marge du bas -->
<View
android:layout_width="match_parent"
android:layout_height="20dp" />
</LinearLayout>
</LinearLayout>

View File

@@ -40,6 +40,20 @@
android:text="@string/win_ticket"
android:textAllCaps="false"
android:textColor="#FFFFFF"/>
<Button
android:id="@+id/raceReport"
android:layout_width="match_parent"
android:layout_height="57dp"
android:textSize="21sp"
android:textAlignment="textStart"
android:layout_marginVertical="4sp"
android:background="@drawable/rounded_button_green"
android:fontFamily="sans-serif-medium"
android:backgroundTint="@color/primary_green"
android:drawableLeft="@drawable/tpe"
android:text="@string/race_report"
android:textAllCaps="false"
android:textColor="#FFFFFF"/>
<Button
android:id="@+id/cancel_ticket_btn"
android:layout_width="match_parent"

View File

@@ -17,6 +17,9 @@
android:visibility="visible">
<LinearLayout
android:id="@+id/icon_container"
android:tag="tag_pin_exempt"
android:clickable="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="12dp"
@@ -26,7 +29,10 @@
android:orientation="horizontal">
<ImageView
android:id="@+id/icon"
android:clickable="true"
android:layout_width="300dp"
android:tag="tag_pin_exempt"
android:layout_height="100dp"
android:contentDescription="@string/logo"
android:scaleType="fitCenter"

View File

@@ -0,0 +1,84 @@
<?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"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/bg_fantasy_gradient"
android:padding="24dp"
android:layout_height="match_parent"
tools:context=".RaceReport">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="top|center_horizontal"
android:spacing="24dp">
<!-- Icône décorative -->
<ImageView
android:layout_width="96dp"
android:layout_height="96dp"
android:src="@drawable/horse_face_svgrepo_com"
android:contentDescription="@string/app_name"
android:layout_marginBottom="24dp"
android:alpha="0.9"
app:tint="@android:color/white" />
<!-- Titre -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Récupérer le rapport"
android:textColor="@android:color/white"
android:textSize="22sp"
android:textStyle="bold"
android:letterSpacing="0.05"
android:layout_marginBottom="30dp" />
<!-- Input Fantasy -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@drawable/bg_input_fantasy"
android:orientation="horizontal"
android:paddingHorizontal="16dp"
android:gravity="center_vertical">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/horse_with_creative_hair_raising_feet_right_side_view_svgrepo_com"
android:layout_marginEnd="8dp"
app:tint="#C5CAE9" />
<EditText
android:id="@+id/etRaceNumber"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="Entrez le numéro de course"
android:textColor="@android:color/white"
android:textColorHint="#B0BEC5"
android:inputType="number"
android:background="@null"
android:padding="8dp"
android:textSize="16sp"
android:imeOptions="actionDone" />
</LinearLayout>
<!-- Bouton Vérifier -->
<Button
android:id="@+id/btnCheckBalance"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="🔮 Récupérer le rapport"
android:textAllCaps="false"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="@android:color/white"
android:background="@drawable/bg_button_fantasy"
android:layout_marginTop="20dp"
android:elevation="6dp" />
</LinearLayout>
</FrameLayout>

View File

@@ -49,8 +49,8 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/horse_with_creative_hair_raising_feet_right_side_view_svgrepo_com"
android:tint="#C5CAE9"
android:layout_marginEnd="8dp" />
android:layout_marginEnd="8dp"
app:tint="#C5CAE9" />
<EditText
android:id="@+id/etRaceNumber"

View File

@@ -4,6 +4,7 @@
<string name="sold">Solde</string>
<string name="sold_u">SOLDE</string>
<string name="win_ticket">TICKET GAGNANT</string>
<string name="race_report">RAPPPORT COURSE</string>
<string name="last_bets">Derniers paris</string>
<string name="others">Autres</string>
<string name="by_course">Solde par course</string>