From 4ef19bd4e837ca6bd537266a58b747fade39fca5 Mon Sep 17 00:00:00 2001 From: OnlyPapy98 Date: Thu, 30 Apr 2026 13:08:05 +0200 Subject: [PATCH] ticket fomattage --- app/build.gradle.kts | 2 + app/src/main/AndroidManifest.xml | 9 + .../com/example/quiz/AnnulationTicket.java | 80 ++- .../java/com/example/quiz/BetValidation.java | 514 +++++++++++------- .../java/com/example/quiz/DerniersParis.java | 219 +++++++- .../java/com/example/quiz/ListOFBets.java | 2 + .../main/java/com/example/quiz/PageQuiz.java | 90 ++- .../java/com/example/quiz/ServerConfig.java | 95 +++- .../main/java/com/example/quiz/Settings.java | 4 + .../main/java/com/example/quiz/WinTicket.java | 204 +++++++ .../quiz/data/adapter/LastBetsAdapter.java | 71 ++- .../quiz/data/model/ParisResponse.java | 8 +- .../quiz/data/model/ResponseError.java | 36 ++ .../example/quiz/data/remote/ApiClient.java | 2 +- .../example/quiz/data/remote/ApiService.java | 20 +- .../quiz/data/remote/StompManager.java | 2 +- .../quiz/data/repository/AgentRepository.java | 48 +- .../data/repository/CourseRepository.java | 25 +- .../quiz/data/repository/LoginRepository.java | 23 +- .../data/repository/PariMiseRepository.java | 13 +- .../quiz/data/repository/PariRepository.java | 124 ++++- .../repository/PointDeVenteRepository.java | 49 +- .../quiz/data/repository/TpeRepository.java | 40 +- .../com/example/quiz/utils/AuthNavigator.java | 36 +- .../example/quiz/utils/SessionManager.java | 4 + .../quiz/utils/SunmiPrinterManager.java | 275 ++++++++++ .../example/quiz/viewModel/PariViewModel.java | 22 + .../quiz/viewModel/PointDeVenteViewModel.java | 7 + .../example/quiz/viewModel/TpeViewModel.java | 7 + app/src/main/res/layout/dialog_error.xml | 3 +- app/src/main/res/layout/dialog_success.xml | 4 +- .../res/layout/fragment_annulation_ticket.xml | 2 +- app/src/main/res/layout/fragment_caisse.xml | 28 +- app/src/main/res/layout/fragment_login.xml | 2 +- app/src/main/res/layout/fragment_logs.xml | 3 +- .../res/layout/fragment_server_config.xml | 20 +- app/src/main/res/layout/fragment_settings.xml | 28 +- .../main/res/layout/fragment_win_ticket.xml | 2 +- app/src/main/res/layout/item_bet.xml | 13 +- app/src/main/res/layout/last_bets_item.xml | 135 +++-- .../res/layout/pay_confirmation_layout.xml | 283 ++++++++++ app/src/main/res/values/array.xml | 5 + gradle/libs.versions.toml | 2 + .../com/anggastudio/printama/Printama.java | 109 ++-- .../com/anggastudio/printama/PrinterUtil.java | 271 ++++++++- 45 files changed, 2492 insertions(+), 449 deletions(-) create mode 100644 app/src/main/java/com/example/quiz/data/model/ResponseError.java create mode 100644 app/src/main/java/com/example/quiz/utils/SunmiPrinterManager.java create mode 100644 app/src/main/res/layout/pay_confirmation_layout.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f60ca9c..5d85022 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -90,6 +90,8 @@ dependencies { implementation(libs.material) implementation(libs.constraintlayout) + implementation(libs.printerlibrary) + implementation(libs.navigation.fragment) implementation(libs.navigation.ui) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c6c6ad2..55ff5dd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -16,6 +16,11 @@ + + + + + @@ -40,6 +45,10 @@ + + + + { String reference = binding.referenceTicket.getText().toString(); if(reference.isEmpty()){ - Toast.makeText(getContext(),"Entrez la référence du ticket", Toast.LENGTH_SHORT).show(); + MessageDialog.showError(getContext(), "Veuillez donner la reference du ticket"); return; } - viewModel.annulerPari(reference).observe(getViewLifecycleOwner(), new Observer>() { + viewModel.getPariByNumero(reference).observe(getViewLifecycleOwner(), new Observer>() { @Override - public void onChanged(Result pariResult) { - switch (pariResult.status){ - case LOADING:{ - dialog.show("Annulation du ticket"); - break; - } - case ERROR:{ - dialog.dismiss(); - MessageDialog.showError(getContext(), pariResult.message); - break; - } - case SUCCESS:{ - dialog.dismiss(); - MessageDialog.showSuccess(getContext(), "Ticket annulé avec succès"); - logsViewModel.insertLog(prefsHelper.get("id"), "ANNULATION TICKET", "Annulation du ticket "+pariResult.data.getNumeroTicket(), System.currentTimeMillis()); - binding.referenceTicket.setText(""); - } - } + public void onChanged(Result parisResponseResult) { + switch (parisResponseResult.status){ + case LOADING:{ + dialog.show("Recherche du ticket"); + break; + } + case ERROR:{ + MessageDialog.showError(getContext(), parisResponseResult.message); + break; + } + case SUCCESS:{ + if(parisResponseResult.data.getNumeroTicket().equals(reference)){ + dialog.dismiss(); + if(parisResponseResult.data.getStatutPari() == ParisResponse.StatutPari.ANNULE){ + MessageDialog.showError(getContext(), "Le ticket est déjà annulé"); + return; + } + _showPariDialog(reference); + }else{ + MessageDialog.showError(getContext(), "Le ticket n'existe pas"); + } + } + } } }); }); } + void _showPariDialog(String ticketReference){ + new AlertDialog.Builder(getContext()) + .setTitle("Annulation du ticket") + .setMessage("Etes-vous sûr de vouloir annuler le ticket "+ticketReference+" ?") + .setCancelable(true) + .setNegativeButton("Annuler", (dialog, which) -> { + dialog.dismiss(); + }) + .setPositiveButton("Confirmer", (annulationDialog, which) -> { + viewModel.annulerPari(ticketReference).observe(getViewLifecycleOwner(), new Observer>() { + @Override + public void onChanged(Result pariResult) { + switch (pariResult.status){ + case LOADING:{ + dialog.show("Annulation du ticket"); + break; + } + case ERROR:{ + dialog.dismiss(); + MessageDialog.showError(getContext(), pariResult.message); + break; + } + case SUCCESS:{ + dialog.dismiss(); + MessageDialog.showSuccess(getContext(), "Ticket annulé avec succès"); + logsViewModel.insertLog(prefsHelper.get("id"), "ANNULATION TICKET", "Annulation du ticket "+pariResult.data.getNumeroTicket(), System.currentTimeMillis()); + binding.referenceTicket.setText(""); + } + } + } + }); + }).show(); + } + @Override public void onResume() { super.onResume(); diff --git a/app/src/main/java/com/example/quiz/BetValidation.java b/app/src/main/java/com/example/quiz/BetValidation.java index 2c8d5c9..a4caec2 100644 --- a/app/src/main/java/com/example/quiz/BetValidation.java +++ b/app/src/main/java/com/example/quiz/BetValidation.java @@ -1,14 +1,19 @@ package com.example.quiz; -import android.Manifest; + import android.annotation.SuppressLint; -import android.bluetooth.BluetoothAdapter; +import android.app.Dialog; import android.content.Intent; -import android.content.pm.PackageManager; + import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Paint; import android.graphics.drawable.ColorDrawable; +import android.os.Build; import android.os.Bundle; import androidx.activity.result.ActivityResultLauncher; @@ -16,10 +21,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; -import androidx.core.app.ActivityCompat; import androidx.fragment.app.Fragment; -import androidx.lifecycle.LifecycleOwner; -import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; @@ -42,11 +44,9 @@ 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; @@ -54,6 +54,7 @@ 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.utils.SunmiPrinterManager; import com.example.quiz.viewModel.LogsViewModel; import com.example.quiz.viewModel.PariMiseViewModel; import com.example.quiz.viewModel.PariViewModel; @@ -67,15 +68,17 @@ 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; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -import java.util.Map; + import java.util.Objects; +import java.util.Observable; import java.util.stream.Collectors; import javax.inject.Inject; @@ -93,8 +96,10 @@ public class BetValidation extends Fragment { FragmentBetValidationBinding binding; + SharedViewModel shared; + PariMiseViewModel pariMiseViewModel; List misesInitiales; @@ -104,6 +109,8 @@ public class BetValidation extends Fragment { @Inject StompManager stompManager; + SunmiPrinterManager sunmiPrinterManager; + private int nombreX; LogsViewModel logsViewModel; @@ -113,16 +120,20 @@ public class BetValidation extends Fragment { private long mise; + String mobileName; + private TypeOfBet typeOfBet; + MutableLiveData isPrinterReady = new MutableLiveData<>(false); + SharedPrefsHelper prefsHelper; private Course course; - LoaderDialog loader; + LoaderDialog loader; private int coeff; @@ -135,12 +146,11 @@ public class BetValidation extends Fragment { PariViewModel pariViewModel; - public BetValidation() { // Required empty public constructor } - public static BetValidation newInstance() { + public static BetValidation newInstance() { BetValidation fragment = new BetValidation(); Bundle args = new Bundle(); fragment.setArguments(args); @@ -151,8 +161,16 @@ public class BetValidation extends Fragment { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); prefsHelper = SharedPrefsHelper.getInstance(getContext()); + mobileName = Build.MANUFACTURER; + sunmiPrinterManager = SunmiPrinterManager.getInstance(requireContext()); + if(mobileName.toLowerCase().contains("sunmi")){ + sunmiPrinterManager.connectPrinter(status ->{ + isPrinterReady.setValue(status); + }); + } } + @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -161,7 +179,7 @@ public class BetValidation extends Fragment { logsViewModel = new ViewModelProvider(this).get(LogsViewModel.class); binding.coeff.setText(String.valueOf(1)); coeff = Integer.parseInt(binding.coeff.getText().toString()); - binding.coeff.addTextChangedListener(new TextWatcher(){ + binding.coeff.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { @@ -169,11 +187,11 @@ public class BetValidation extends Fragment { @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { - if(charSequence.toString().isEmpty()){ + if (charSequence.toString().isEmpty()) { binding.coeff.setError("Le coefficient est obligatoire"); return; } - if(Integer.parseInt(charSequence.toString())<1){ + if (Integer.parseInt(charSequence.toString()) < 1) { binding.coeff.setError("Le coefficient doit être supérieur ou égal à 1 "); return; } @@ -194,7 +212,7 @@ public class BetValidation extends Fragment { binding.gridNumbers.removeAllViews(); int columns = 8; grid.setColumnCount(columns); - if(shared.selectedCourse.getValue() != null){ + if (shared.selectedCourse.getValue() != null) { for (int i = 1; i <= shared.selectedCourse.getValue().getNombrePartants(); i++) { createNumberItem(String.valueOf(i)); grid.addView(createNumberItem(String.valueOf(i))); @@ -203,7 +221,6 @@ public class BetValidation extends Fragment { createNumberItem("X"); grid.addView(createNumberItem("X")); } - private TextView createNumberItem(String horse) { TextView textView = new TextView(requireContext()); textView.setText(horse); @@ -215,53 +232,53 @@ public class BetValidation extends Fragment { textView.setWidth(80); textView.setHeight(100); textView.setGravity(Gravity.CENTER); - textView.setBackgroundResource(Objects.equals(horse, "X") ?R.drawable.x_background: R.drawable.number_background); - if(!Objects.equals(horse, "X") && shared.selectedCourse.getValue().getNonPartants() != null && shared.selectedCourse.getValue().getNonPartants().contains(Integer.valueOf(horse))){ + textView.setBackgroundResource(Objects.equals(horse, "X") ? R.drawable.x_background : R.drawable.number_background); + if (!Objects.equals(horse, "X") && shared.selectedCourse.getValue().getNonPartants() != null && shared.selectedCourse.getValue().getNonPartants().contains(Integer.valueOf(horse))) { textView.setTextColor(getResources().getColor(R.color.white)); textView.setBackgroundResource(R.drawable.number_background_grey); } textView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); textView.setOnClickListener(v -> { - if(!Objects.equals(horse, "X") && shared.selectedCourse.getValue().getNonPartants() != null && shared.selectedCourse.getValue().getNonPartants().contains(Integer.valueOf(horse))){ + if (!Objects.equals(horse, "X") && shared.selectedCourse.getValue().getNonPartants() != null && shared.selectedCourse.getValue().getNonPartants().contains(Integer.valueOf(horse))) { Toast.makeText(getContext(), "Ce cheval n'est pas partant", Toast.LENGTH_SHORT).show(); return; } final List horses = new ArrayList<>(selectedHorses.getValue()); - if(horses.size() >= shared.typeOfBet.getValue().getNumberOfHorse() && horse.equals("X")){ - Toast.makeText(getContext(), "Vous ne pouvez plus sélectionner X", Toast.LENGTH_SHORT).show(); - return; - } - if(_notClickable(horses) && horse.equals("X")){ - Toast.makeText(getContext(), "Vous ne pouvez plus sélectionner X", Toast.LENGTH_SHORT).show(); - return; - } - if (horses.contains(horse) && !horse.equals("X")) { - horses.remove(horse); - selectedHorses.setValue(horses); - v.setSelected(false); - v.setBackgroundResource(R.drawable.number_background); - } else { - horses.add(horse); - selectedHorses.setValue(horses); - v.setSelected(true); - v.setBackgroundResource(R.drawable.number_selected_background); - } - String combinationText = selectedHorses.getValue().stream() - .map(h -> h) - .collect(Collectors.joining("-")); - binding.combination.setText( combinationText); - }); + if (horses.size() >= shared.typeOfBet.getValue().getNumberOfHorse() && horse.equals("X")) { + Toast.makeText(getContext(), "Vous ne pouvez plus sélectionner X", Toast.LENGTH_SHORT).show(); + return; + } + if (_notClickable(horses) && horse.equals("X")) { + Toast.makeText(getContext(), "Vous ne pouvez plus sélectionner X", Toast.LENGTH_SHORT).show(); + return; + } + if (horses.contains(horse) && !horse.equals("X")) { + horses.remove(horse); + selectedHorses.setValue(horses); + v.setSelected(false); + v.setBackgroundResource(R.drawable.number_background); + } else { + horses.add(horse); + selectedHorses.setValue(horses); + v.setSelected(true); + v.setBackgroundResource(R.drawable.number_selected_background); + } + String combinationText = selectedHorses.getValue().stream() + .map(h -> h) + .collect(Collectors.joining("-")); + binding.combination.setText(combinationText); + }); return textView; } - boolean _notClickable(List selectedHorses){ + boolean _notClickable(List selectedHorses) { int numberOfElement = 0; - if(shared.typeOfBet.getValue().getNumberOfHorse() < 2){ + if (shared.typeOfBet.getValue().getNumberOfHorse() < 2) { return true; } - for(String horse: selectedHorses){ - numberOfElement = horse.equals("X")?numberOfElement+1:numberOfElement; + for (String horse : selectedHorses) { + numberOfElement = horse.equals("X") ? numberOfElement + 1 : numberOfElement; } nombreX = numberOfElement; return numberOfElement == shared.typeOfBet.getValue().getNumberOfHorse() - 1; @@ -274,8 +291,8 @@ public class BetValidation extends Fragment { pariMiseViewModel = new ViewModelProvider(this).get(PariMiseViewModel.class); pariMiseViewModel.getBetInitMise().observe( getViewLifecycleOwner(), - result->{ - switch (result.status){ + result -> { + switch (result.status) { case LOADING: { loader.show("Chargement des mise"); break; @@ -294,12 +311,13 @@ public class BetValidation extends Fragment { } } ); - if(shared.selectedCourse.getValue() != null){ - stompManager.subscribe("courses/"+shared.selectedCourse.getValue().getId(), json->{ - Type type = new TypeToken>(){}.getType(); + if (shared.selectedCourse.getValue() != null) { + stompManager.subscribe("courses/" + shared.selectedCourse.getValue().getId(), json -> { + Type type = new TypeToken>() { + }.getType(); NotifPayload notif = new Gson().fromJson(json, type); Course updatedCourse = notif.getPayload(); - if(shared.selectedCourse.getValue().getId() == updatedCourse.getId()){ + if (shared.selectedCourse.getValue().getId() == updatedCourse.getId()) { shared.setSelectedCourse(updatedCourse); setupNumberGrid(binding.gridNumbers); } @@ -310,12 +328,12 @@ public class BetValidation extends Fragment { typeOfBet = shared.typeOfBet.getValue(); course = shared.selectedCourse.getValue(); AppCompatActivity activity = (AppCompatActivity) getActivity(); - if(activity != null){ + if (activity != null) { MaterialToolbar toolbar = activity.findViewById(R.id.toolbar); - toolbar.setBackgroundColor( getResources().getColor(R.color.primary_green)); + toolbar.setBackgroundColor(getResources().getColor(R.color.primary_green)); activity.setSupportActionBar(toolbar); - if(activity.getSupportActionBar() != null){ - activity.getSupportActionBar().setTitle("Pari "+shared.selectedCourse.getValue().getNom()); + if (activity.getSupportActionBar() != null) { + activity.getSupportActionBar().setTitle("Pari " + shared.selectedCourse.getValue().getNom()); } } setupNumberGrid(binding.gridNumbers); @@ -342,28 +360,28 @@ public class BetValidation extends Fragment { @Override public void onChanged(List horses) { calculateMise(horses); - if(shared.typeOfBet.getValue().getNumberOfHorse() > 2 && horses.size() >= shared.typeOfBet.getValue().getNumberOfHorse()){ + if (shared.typeOfBet.getValue().getNumberOfHorse() > 2 && horses.size() >= shared.typeOfBet.getValue().getNumberOfHorse()) { binding.order.setVisibility(View.VISIBLE); - }else{ + } else { binding.order.setVisibility(View.GONE); } - if(horses.contains("X")){ - if(selectedHorses.getValue().size() == shared.typeOfBet.getValue().getNumberOfHorse()){ + if (horses.contains("X")) { + if (selectedHorses.getValue().size() == shared.typeOfBet.getValue().getNumberOfHorse()) { binding.elargie.setVisibility(View.VISIBLE); binding.elargie.setText("Champ total"); return; - }else{ - if(selectedHorses.getValue().size() > shared.typeOfBet.getValue().getNumberOfHorse()){ + } else { + if (selectedHorses.getValue().size() > shared.typeOfBet.getValue().getNumberOfHorse()) { binding.elargie.setVisibility(View.VISIBLE); binding.elargie.setText("Champ partiel"); return; } } } - if(horses.size() > shared.typeOfBet.getValue().getNumberOfHorse()){ + if (horses.size() > shared.typeOfBet.getValue().getNumberOfHorse()) { binding.elargie.setVisibility(View.VISIBLE); binding.elargie.setText("Elargi"); - }else{ + } else { binding.elargie.setVisibility(View.GONE); } } @@ -373,48 +391,24 @@ public class BetValidation extends Fragment { calculateMise(selectedHorses.getValue()); }); - binding.betValidateBtn.setOnClickListener(v->{ -// Log.d("PAPER_STATUS", String.valueOf(ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.BLUETOOTH_CONNECT))+" "+String.valueOf(PackageManager.PERMISSION_GRANTED)); -// 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(); -// 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; -// } -// } - - if(binding.paymentType.getSelectedItem().toString().equals("Orange Money") && binding.phoneNumber.getText().toString().isEmpty()){ + binding.betValidateBtn.setOnClickListener(v -> { + if (binding.paymentType.getSelectedItem().toString().equals("Orange Money") && binding.phoneNumber.getText().toString().isEmpty()) { MessageDialog.showError(getContext(), "Veuillez saisir le numéro de téléphone"); return; } - if(shared.typeOfBet == null || shared.selectedCourse == null){ + if (shared.typeOfBet == null || shared.selectedCourse == null) { return; } int required = typeOfBet.getNumberOfHorse(); - if(selectedHorses.getValue().size() < required){ - Toast.makeText(getContext(), "Veuillez sélectionner au moins"+required+" chevaux", Toast.LENGTH_SHORT).show(); + if (Objects.requireNonNull(selectedHorses.getValue()).size() < required) { + Toast.makeText(getContext(), "Veuillez sélectionner au moins" + required + " chevaux", Toast.LENGTH_SHORT).show(); return; } - if(this.mise == 0){ + if (this.mise == 0) { Toast.makeText(getContext(), "Pari non valide", Toast.LENGTH_SHORT).show(); return; } @@ -432,61 +426,63 @@ public class BetValidation extends Fragment { "XOF", "Pari" ); + if (dialog != null && dialog.isShowing()) { + return; + } _showPariDialog(pari); }); - binding.backBtn.setOnClickListener(v->{ + binding.backBtn.setOnClickListener(v -> { getActivity().onBackPressed(); }); - binding.deleteBtn.setOnClickListener(v->{ + binding.deleteBtn.setOnClickListener(v -> { _initializeToZero(); }); } - - String _getCombinaison(){ - if(selectedHorses.getValue().contains("X") && shared.typeOfBet.getValue().getNumberOfHorse() < selectedHorses.getValue().size()){ + String _getCombinaison() { + if (selectedHorses.getValue().contains("X") && shared.typeOfBet.getValue().getNumberOfHorse() < selectedHorses.getValue().size()) { String first = selectedHorses.getValue().subList(0, shared.typeOfBet.getValue().getNumberOfHorse()).stream() .map(String::valueOf) .collect(Collectors.joining(",")); String last = selectedHorses.getValue().subList(shared.typeOfBet.getValue().getNumberOfHorse(), selectedHorses.getValue().size()).stream() .map(String::valueOf) .collect(Collectors.joining(",")); - return first+",R,"+last; - }else{ + return first + ",R," + last; + } else { return selectedHorses.getValue().stream().map(String::valueOf).collect(Collectors.joining(",")); } } - List _getFormule(){ + List _getFormule() { List combinaison = selectedHorses.getValue(); - if(!combinaison.contains("X")){ - if(!order){ - return List.of("UNITAIRE"); - }else{ + if (!combinaison.contains("X")) { + if (!order) { + return List.of("UNITAIRE"); + } else { return List.of("FORMULE_COMPLETE"); } - }else{ - if(shared.typeOfBet.getValue().getNumberOfHorse() < selectedHorses.getValue().size()){ - if(order){ + } else { + if (shared.typeOfBet.getValue().getNumberOfHorse() < selectedHorses.getValue().size()) { + if (order) { return List.of("CHAMP_X", "FORMULE_COMPLETE"); - }else{ + } else { return List.of("CHAMP_X"); } - }else{ - if(order){ + } else { + if (order) { return List.of("CHAMP_TOTAL", "FORMULE_COMPLETE"); - }else{ + } else { return List.of("CHAMP_TOTAL"); } } } } - private void setSelectedTypeOfBetImage(){ - switch (shared.typeOfBet.getValue().getName()){ + private void setSelectedTypeOfBetImage() { + switch (shared.typeOfBet.getValue().getName()) { case COUPLE_PLACE: binding.icTypeOfBet.setImageResource(R.drawable.ic_couple_place); break; @@ -506,34 +502,41 @@ public class BetValidation extends Fragment { } public void printPari(ParisResponse pari) throws WriterException { - try { - Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pmu_logo); - Bitmap barcode = generateBarcodeBitmap(pari.getNumeroTicket(), 384, 100); - StringBuilder tspl = new StringBuilder(); - Printama printama = Printama.with(getContext()); + Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pmu_logo); + Bitmap barcode = generateBarcodeBitmap(pari.getNumeroTicket(), 384, 100); + StringBuilder tspl = new StringBuilder(); + tspl.append("Bamako").append("\n"); + tspl.append(shared.selectedCourse.getValue().getNom()).append("\n"); + OffsetDateTime dateTime = OffsetDateTime.parse(shared.selectedCourse.getValue().getHeureDepartPrevue()); + 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(shared.selectedCourse.getValue().getId())).append("\n"); + tspl.append(sunmiPrinterManager.separationText()+ "\n"); + boolean isElargie = selectedHorses.getValue().size() > shared.typeOfBet.getValue().getNumberOfHorse(); + String typePari = pari.getTypesParisMises().get(0).getTypePari(); + tspl.append(isElargie ? typePari + "/Elargie" : typePari).append("\n"); + tspl.append(order ? "COMBINAISON COMPLETE" + "\n" : ""); + String combinationText = formatLineWithNumbers(selectedHorses.getValue().stream().map(String::valueOf).toArray(String[]::new), "-") ; + tspl.append(combinationText).append("\n"); + tspl.append("COEF: ").append(String.valueOf(coeff)).append(".0"); + tspl.append("\n").append(sunmiPrinterManager.separationText()).append("\n"); + tspl.append("MONTANT: ").append(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(LocalDateTime.now())).append("\n"); + if(mobileName.toLowerCase().contains("sunmi")){ + String pariText = tspl.toString(); + if(!sunmiPrinterManager.printPari(resizeToPrinterWidth(bitmap, 384), barcode, pari.getNumeroTicket(), pariText)){ + MessageDialog.showError(getContext(), "Erreur d'impression"); + prefsHelper.save("noPrintId", String.valueOf(pari.getId())); + return; + }; + _initializeToZero(); + return; + } - tspl.append("Bamako").append("\n"); - tspl.append(shared.selectedCourse.getValue().getNom()).append("\n"); - OffsetDateTime dateTime = OffsetDateTime.parse(shared.selectedCourse.getValue().getHeureDepartPrevue()); - 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(shared.selectedCourse.getValue().getId())).append("\n"); - tspl.append(printama.lineSeparator()+"\n"); - boolean isElargie = selectedHorses.getValue().size()>shared.typeOfBet.getValue().getNumberOfHorse(); - String typePari = pari.getTypesParisMises().get(0).getTypePari(); - tspl.append(isElargie?typePari+"/Elargie":typePari).append("\n"); - tspl.append(order?"COMBINAISON COMPLETE"+"\n":""); - String combinationText = selectedHorses.getValue().stream() - .map(String::valueOf) - .collect(Collectors.joining("-")); - tspl.append(combinationText).append("\n"); - tspl.append("COEF: ").append(String.valueOf(coeff)).append(".0"); - tspl.append("\n").append(printama.lineSeparator()).append("\n"); - tspl.append("MONTANT: ").append(pari.getTypesParisMises().get(0).getMiseTotale()).append(" XOF"); - tspl.append("\n").append(printama.lineSeparator()).append("\n"); - tspl.append("AGENT: ").append(prefsHelper.get("code")).append("\n"); - tspl.append("DATE: ").append(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss").format(LocalDateTime.now())).append("\n"); + try { if (BluetoothUtils.needsBluetoothPermissions()) { if (!BluetoothUtils.hasBluetoothPermission(requireContext())) { // Demande la permission si non accordée @@ -544,8 +547,28 @@ public class BetValidation extends Fragment { // 2️⃣ Permission OK, on peut afficher la liste - Printama.with(getContext()).printTextBuilder(tspl, bitmap, pari.getNumeroTicket(), barcode); - _initializeToZero(); + Printama.with(getContext()).printTextBuilder(tspl, bitmap, pari.getNumeroTicket(), barcode, new Printama.PrintCallback() { + @Override + public void onResult(boolean success, String errorMessage) { + if (!success) { + new android.app.AlertDialog.Builder(getContext()) + .setTitle("Impréssion pari") + .setMessage("Voulez-vous rééimprimer ce ticket?") + .setPositiveButton("Oui", (dialog, which) -> { + try { + printPari(pari); + } catch (WriterException e) { + throw new RuntimeException(e); + } + }) + .setNegativeButton("Non", (dialog, which) -> { + dialog.dismiss(); + }); + } else { + _initializeToZero(); + } + } + }); } catch (SecurityException e) { Toast.makeText(requireContext(), "Permission Bluetooth non accordée", Toast.LENGTH_SHORT).show(); @@ -553,36 +576,103 @@ public class BetValidation extends Fragment { } + public String formatLineWithNumbers(String[] numbers, String separator) { + StringBuilder currentLine = new StringBuilder(); + StringBuilder finalOutput = new StringBuilder(); + List formatted = new ArrayList<>(); + int requiredHorse = shared.typeOfBet.getValue().getNumberOfHorse(); + if(Arrays.stream(numbers).map(String::valueOf).collect(Collectors.toList()).contains("X")){ + if(numbers.length <= requiredHorse){ + return Arrays.stream(numbers).map(String::valueOf).collect(Collectors.joining("-")).toString(); + } + List firstPart = Arrays.stream(numbers).limit(requiredHorse).map(String::valueOf).collect(Collectors.toList()); + List secondPart = Arrays.stream(numbers).skip(requiredHorse).map(String::valueOf).collect(Collectors.toList()); + formatted.addAll(firstPart); + formatted.add("R"); + formatted.addAll(secondPart); + }else{ + formatted = Arrays.stream(numbers).map(String::valueOf).collect(Collectors.toList()); + } + + for (String number : formatted) { + String element = (currentLine.length() == 0 ? "":separator) + number; + + // Si l'ajout dépasse la largeur max, on termine la ligne et on recommence + if (currentLine.length() + element.length() > sunmiPrinterManager.getMaxChar()) { + finalOutput.append(currentLine.toString()).append("\n"); + currentLine = new StringBuilder(number); + } else { + currentLine.append(element); + } + } + + if (currentLine.length() > 0) { + finalOutput.append(currentLine.toString()); + } + + return finalOutput.toString(); + } + + @SuppressLint({"MissingInflatedId", "SetTextI18n"}) - void _showPariDialog(Pari pari){ + void _showPariDialog(Pari pari) { LayoutInflater inflater = getLayoutInflater(); View view = inflater.inflate(R.layout.pari_confirmation, null); TextView numero_course = (TextView) view.findViewById(R.id.alert_course_numero); - numero_course.setText("Numero Course: "+String.valueOf(shared.selectedCourse.getValue().getId())); + numero_course.setText("Numero Course: " + String.valueOf(shared.selectedCourse.getValue().getId())); TextView alert_pari_type = (TextView) view.findViewById(R.id.alert_pari_type); alert_pari_type.setText(String.valueOf(shared.typeOfBet.getValue().getName())); TextView is_elargie = (TextView) view.findViewById(R.id.alert_is_elargie); - is_elargie.setText(selectedHorses.getValue().size() > shared.typeOfBet.getValue().getNumberOfHorse()?"CE":"SI"); + is_elargie.setText(selectedHorses.getValue().size() > shared.typeOfBet.getValue().getNumberOfHorse() ? "CE" : "SI"); TextView alert_combinaison = (TextView) view.findViewById(R.id.alert_combinaison); alert_combinaison.setText(selectedHorses.getValue().stream() .map(String::valueOf) .collect(Collectors.joining("-"))); TextView aler_coeff = (TextView) view.findViewById(R.id.alert_coeff); - aler_coeff.setText("Coef:"+String.valueOf(coeff)); + aler_coeff.setText("Coef:" + String.valueOf(coeff)); TextView alert_montant = (TextView) view.findViewById(R.id.alert_montant); - alert_montant.setText("Montant: "+String.valueOf(pari.getTypesParisMises().get(0).getMiseTotale())+" CFA"); + alert_montant.setText("Montant: " + String.valueOf(pari.getTypesParisMises().get(0).getMiseTotale()) + " CFA"); AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); builder.setView(view); builder.setCancelable(false); - dialog = builder.create(); dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); - view.findViewById(R.id.alert_validate).setOnClickListener(v->{ - pariViewModel.createPari(pari).observe(getViewLifecycleOwner(),new Observer>() { + view.findViewById(R.id.alert_validate).setOnClickListener(v -> { + if(mobileName.toLowerCase().contains("sunmi")){ + if(isPrinterReady.getValue() != null && !isPrinterReady.getValue()){ + sunmiPrinterManager.connectPrinter(status ->{ + isPrinterReady.setValue(status); + }); + } + switch (sunmiPrinterManager.printerStatus()){ + case 2:{ + MessageDialog.showError(getContext(), "L'imprimante n'est pas connectée!"); + return; + } + case 3:{ + MessageDialog.showError(getContext(), "L'imprimante n'est pas disponible!"); + return; + } + case 4:{ + MessageDialog.showError(getContext(), "Veuillez insérer du papier dans l'imprimante, SVP!"); + return; + } + case 5: { + MessageDialog.showError(getContext(), "Suchauffe iminante de l'imprimante, Veuillez laisser reposer!"); + return; + } + case 6: { + MessageDialog.showError(getContext(), "Le capot est ouvert, veuillez fermer SVP!"); + return; + } + + } + } + pariViewModel.createPari(pari).observe(getViewLifecycleOwner(), new Observer>() { @Override public void onChanged(Result pariResult) { - switch (pariResult.status){ + switch (pariResult.status) { case ERROR: loader.dismiss(); dialog.dismiss(); @@ -594,7 +684,7 @@ public class BetValidation extends Fragment { 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()); + 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()); printPari(pariResult.data); dialog.dismiss(); MessageDialog.showSuccess(getContext(), "Pari créé avec succès"); @@ -614,7 +704,7 @@ public class BetValidation extends Fragment { dialog.show(); } - void _initializeToZero(){ + void _initializeToZero() { selectedHorses.setValue(List.of()); binding.combination.setText(""); binding.order.setChecked(false); @@ -625,29 +715,29 @@ public class BetValidation extends Fragment { setupNumberGrid(binding.gridNumbers); } - public void calculateMise(List selectedHorses){ + public void calculateMise(List selectedHorses) { _notClickable(selectedHorses); int nombreChevauxSelectionnes = selectedHorses.size(); long mise = 0; int nonPartants = 0; - if(shared.typeOfBet.getValue() == null || shared.selectedCourse.getValue() == null){ + if (shared.typeOfBet.getValue() == null || shared.selectedCourse.getValue() == null) { return; } - if(shared.selectedCourse.getValue().getNonPartants() != null){ + if (shared.selectedCourse.getValue().getNonPartants() != null) { nonPartants = shared.selectedCourse.getValue().getNonPartants().size(); } 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"); + if (shared.typeOfBet.getValue().getNumberOfHorse() > nombreChevauxSelectionnes) { + binding.mise.setText(mise + " CFA"); return; } MiseInitiale miseModel = misesInitiales.stream() - .filter(miseInitiale-> miseInitiale.getTypePari().equals(courseName)) + .filter(miseInitiale -> miseInitiale.getTypePari().equals(courseName)) .findFirst() .orElse(null); - if(miseModel == null){ + if (miseModel == null) { MessageDialog.showError(getContext(), "Erreur lors de l'initialisation de la mise"); _initializeToZero(); return; @@ -662,7 +752,7 @@ public class BetValidation extends Fragment { // 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()); + MessageDialog.showError(getContext(), "Le coefficient doit être au plus 200 pour " + typePari.toString()); _initializeToZero(); return; } @@ -670,58 +760,103 @@ public class BetValidation extends Fragment { // 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()); + 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){ - mise = mise * _calculateArrangement((partants - (typeOfBetHorses-nombreX) - nonPartants), nombreX); - }else{ - if(nombreChevauxSelectionnes - typeOfBetHorses < nombreX || nombreChevauxSelectionnes - typeOfBetHorses==1){ + if (nombreX > 0) { + if (nombreChevauxSelectionnes == typeOfBetHorses) { + mise = mise * _calculateArrangement((partants - (typeOfBetHorses - nombreX) - nonPartants), nombreX); + } else { + if (nombreChevauxSelectionnes - typeOfBetHorses < nombreX || nombreChevauxSelectionnes - typeOfBetHorses == 1) { mise = 0; this.mise = mise; - binding.mise.setText(mise+" CFA"); + binding.mise.setText(mise + " CFA"); return; } - mise = mise * _calculateArrangement(nombreChevauxSelectionnes - typeOfBetHorses , nombreX); + mise = mise * _calculateArrangement(nombreChevauxSelectionnes - typeOfBetHorses, nombreX); } - if(order){ - mise = mise * _calculateArrangement(typeOfBetHorses, (typeOfBetHorses-nombreX)); + if (order) { + mise = mise * _calculateArrangement(typeOfBetHorses, (typeOfBetHorses - nombreX)); } this.mise = mise; - binding.mise.setText(mise+" CFA"); + binding.mise.setText(mise + " CFA"); return; } - if(!order){ + if (!order) { mise = mise * _calculateCombinaison(nombreChevauxSelectionnes, typeOfBetHorses); - }else{ + } else { mise = mise * _calculateArrangement(nombreChevauxSelectionnes, typeOfBetHorses); } this.mise = mise; - binding.mise.setText(mise+" CFA"); + binding.mise.setText(mise + " CFA"); } - Long _calculateArrangement(int n, int k){ - return _calculateFactorial(n) / _calculateFactorial(n-k); + Long _calculateArrangement(int n, int k) { + return _calculateFactorial(n) / _calculateFactorial(n - k); } - Long _calculateCombinaison(int n, int k){ + Long _calculateCombinaison(int n, int k) { return _calculateFactorial(n) / (_calculateFactorial(k) * _calculateFactorial(n - k)); } - Long _calculateFactorial(int n){ + Long _calculateFactorial(int n) { long f = 1; - if(n == 0){ - return f; + if (n == 0) { + return f; } - for(int i = 1; i <=n; i++){ + for (int i = 1; i <= n; i++) { f *= i; } - return f; + return f; + } + + + private Bitmap resizeToPrinterWidth(Bitmap originalBitmap, int printerWidthPx) { + int originalWidth = originalBitmap.getWidth(); + int originalHeight = originalBitmap.getHeight(); + int newHeight = (originalHeight * printerWidthPx) / originalWidth; + + // 1. Redimensionner sans filtre (conserve les contours nets) + Bitmap scaledBitmap = Bitmap.createScaledBitmap(originalBitmap, + printerWidthPx, + newHeight, + false); + + // 2. Créer un bitmap ARGB_8888 (meilleure qualité que RGB_565) + Bitmap result = Bitmap.createBitmap(printerWidthPx, newHeight, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(result); + canvas.drawColor(Color.WHITE); + + // 3. Dessiner avec un Paint qui préserve les couleurs + Paint paint = new Paint(); + paint.setAntiAlias(false); // Pas d'anti-aliasing (évite le flou) + canvas.drawBitmap(scaledBitmap, 0, 0, paint); + + // 4. Seuillage intelligent pour garder les détails + for (int x = 0; x < result.getWidth(); x++) { + for (int y = 0; y < result.getHeight(); y++) { + int pixel = result.getPixel(x, y); + int r = Color.red(pixel); + int g = Color.green(pixel); + int b = Color.blue(pixel); + + // Calculer la luminosité + int gray = (r + g + b) / 3; + + // Seuil adaptatif : si c'est sombre, deviens noir + if (gray < 130) { // Seuil à 200 pour garder les gris clairs + result.setPixel(x, y, Color.BLACK); + } else { + result.setPixel(x, y, Color.WHITE); + } + } + } + + return result; } @@ -735,6 +870,7 @@ public class BetValidation extends Fragment { } return bmp; } + public static String generate12Digits() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 8; i++) { diff --git a/app/src/main/java/com/example/quiz/DerniersParis.java b/app/src/main/java/com/example/quiz/DerniersParis.java index fd283d7..38539dc 100644 --- a/app/src/main/java/com/example/quiz/DerniersParis.java +++ b/app/src/main/java/com/example/quiz/DerniersParis.java @@ -1,9 +1,15 @@ package com.example.quiz; +import android.annotation.SuppressLint; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import androidx.lifecycle.Observer; @@ -11,15 +17,20 @@ import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.TextView; import android.widget.Toast; +import com.anggastudio.printama.Printama; import com.example.quiz.data.adapter.LastBetsAdapter; +import com.example.quiz.data.model.Course; import com.example.quiz.data.model.Pari; import com.example.quiz.data.model.ParisResponse; import com.example.quiz.databinding.FragmentDerniersParisBinding; +import com.example.quiz.utils.BluetoothUtils; import com.example.quiz.utils.LoaderDialog; import com.example.quiz.utils.MessageDialog; import com.example.quiz.utils.Result; @@ -27,8 +38,18 @@ import com.example.quiz.utils.SharedPrefsHelper; import com.example.quiz.viewModel.LogsViewModel; import com.example.quiz.viewModel.PariViewModel; import com.google.android.material.appbar.MaterialToolbar; +import com.google.zxing.BarcodeFormat; +import com.google.zxing.MultiFormatWriter; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import dagger.hilt.android.AndroidEntryPoint; @@ -45,13 +66,18 @@ public class DerniersParis extends Fragment { LogsViewModel logsViewModel; + LoaderDialog loader; SharedPrefsHelper prefsHelper; PariViewModel viewModel; + AlertDialog dialog; + LastBetsAdapter adapter; + Map listProduits = Map.of("QUINTE", 5, "QUARTE", 4, "TIERCE", 3, "COUPLE_GAGNANT", 2, "COUPLE_PLACE", 2); + public DerniersParis() { // Required empty public constructor } @@ -70,6 +96,7 @@ public class DerniersParis extends Fragment { prefsHelper = SharedPrefsHelper.getInstance(getContext()); } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -84,29 +111,203 @@ public class DerniersParis extends Fragment { super.onViewCreated(view, savedInstanceState); binding.lastBetsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); viewModel = new ViewModelProvider(this).get(PariViewModel.class); - viewModel.getDerniersParis(prefsHelper.get("id")).observe(getViewLifecycleOwner(), new Observer>>() { + _getLastBets(); + } + + void _getLastBets(){ + viewModel.getDerniersParis(prefsHelper.get("id")).observe(getViewLifecycleOwner(), new Observer>>() { + @Override + public void onChanged(Result> listResult) { + switch (listResult.status){ + case LOADING: + loader.show("Chargement des derniers paris"); + break; + case ERROR: + loader.dismiss(); + MessageDialog.showError(getContext(), listResult.message); + break; + case SUCCESS: + loader.dismiss(); + if(adapter == null){ + adapter = new LastBetsAdapter(listResult.data); + }else{ + adapter.setData(listResult.data); + } + + logsViewModel.insertLog(prefsHelper.get("id"), "LAST BETS", "Affichage derniers paris", System.currentTimeMillis()); + adapter.setPariClickListener(new LastBetsAdapter.onPariClickListener() { + @Override + public void onItemClick(ParisResponse pari) { + if(pari.getStatutPari() != null && pari.getStatutPari() != ParisResponse.StatutPari.ENREGISTRE){ + MessageDialog.showError(getContext(), "Ce pari ne peut pas être réimprimé!"); + return; + } + _showPariDialog(pari); + } + @Override + public void onItemCancel(ParisResponse pari) { + if(pari.getStatutPari() != null && pari.getStatutPari() != ParisResponse.StatutPari.ENREGISTRE){ + MessageDialog.showError(getContext(), "Ce pari ne peut pas être annulé"); + return; + } + _cancelConfirmationDialog(pari); + } + }); + binding.lastBetsRecyclerView.setAdapter(adapter); + break; + } + } + }); + } + + void _cancelConfirmationDialog(ParisResponse pari){ + new AlertDialog.Builder(getContext()) + .setTitle("Annulation du pari") + .setMessage("Êtes-vous sûr de vouloir annuler le pari "+pari.getNumeroTicket()+" ?") + .setPositiveButton("Oui", (cancelDialog, which) -> { + _cancelPari(pari); + cancelDialog.dismiss(); + }) + .setNegativeButton("Non", (cancelDialog, which) -> { + cancelDialog.dismiss(); + }).show(); + } + + void _cancelPari(ParisResponse pari){ + viewModel.annulerPari(pari.getNumeroTicket()).observe(getViewLifecycleOwner(), new Observer>() { @Override - public void onChanged(Result> listResult) { - switch (listResult.status){ + public void onChanged(Result result) { + switch (result.status){ case LOADING: - loader.show("Chargement des derniers paris"); + loader.show("Annulation du pari"); break; case ERROR: loader.dismiss(); - MessageDialog.showError(getContext(), listResult.message); + MessageDialog.showError(getContext(), result.message); break; case SUCCESS: loader.dismiss(); - adapter = new LastBetsAdapter(listResult.data, pari -> { - }); - logsViewModel.insertLog(prefsHelper.get("id"), "LAST BETS", "Affichage derniers paris", System.currentTimeMillis()); - binding.lastBetsRecyclerView.setAdapter(adapter); + MessageDialog.showSuccess(getContext(), "Pari annulé avec succès"); + _getLastBets(); break; } } }); } + + @SuppressLint({"MissingInflatedId", "SetTextI18n"}) + void _showPariDialog(ParisResponse pari){ + LayoutInflater inflater = getLayoutInflater(); + View view = inflater.inflate(R.layout.pari_confirmation, null); + TextView numero_course = (TextView) view.findViewById(R.id.alert_course_numero); + numero_course.setText("Numero Course: "+String.valueOf(pari.getCourseId())); + TextView alert_pari_type = (TextView) view.findViewById(R.id.alert_pari_type); + alert_pari_type.setText(String.valueOf(pari.getTypesParisMises().get(0).getTypePari())); + TextView is_elargie = (TextView) view.findViewById(R.id.alert_is_elargie); + List selectedHorses = Arrays.stream(pari.getCombinaison().split(",")) + .map(String::trim) + .collect(Collectors.toList()); + is_elargie.setText(selectedHorses.size() > listProduits.get(pari.getTypesParisMises().get(0).getTypePari())?"CE":"SI"); + TextView alert_combinaison = (TextView) view.findViewById(R.id.alert_combinaison); + alert_combinaison.setText(selectedHorses.stream() + .map(String::valueOf) + .collect(Collectors.joining("-"))); + TextView aler_coeff = (TextView) view.findViewById(R.id.alert_coeff); + aler_coeff.setText("Coef:"+String.valueOf(pari.getCoefficient())); + TextView alert_montant = (TextView) view.findViewById(R.id.alert_montant); + alert_montant.setText("Montant: "+String.valueOf(pari.getTypesParisMises().get(0).getMiseTotale())+" CFA"); + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setView(view); + builder.setCancelable(false); + dialog = builder.create(); + dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + + view.findViewById(R.id.alert_validate).setOnClickListener(v->{ + try { + _printPari(pari); + dialog.dismiss(); + } catch (WriterException e) { + throw new RuntimeException(e); + } + }); + view.findViewById(R.id.alert_cancel).setOnClickListener(v -> { + dialog.dismiss(); + }); + dialog.show(); + } + + + + void _printPari(ParisResponse pari) throws WriterException { + try { + Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pmu_logo); + Bitmap barcode = generateBarcodeBitmap(pari.getNumeroTicket(), 384, 100); + StringBuilder tspl = new StringBuilder(); + Printama printama = Printama.with(getContext()); + tspl.append("Bamako").append("\n"); + tspl.append(pari.getCourseNom()).append("\n"); + OffsetDateTime dateTime = OffsetDateTime.parse(pari.getHeureDepartPrevue() != null ? pari.getHeureDepartPrevue() : OffsetDateTime.now().toString()); + 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(printama.lineSeparator()+"\n"); + List selectedHorses = Arrays.stream(pari.getCombinaison().split(",")).collect(Collectors.toList()); + boolean isElargie = selectedHorses.size()> listProduits.get(pari.getTypesParisMises().get(0).getTypePari()); + String typePari = pari.getTypesParisMises().get(0).getTypePari(); + tspl.append(isElargie?typePari+"/Elargie":typePari).append("\n"); + tspl.append(pari.getFormules().contains("FORMULE_COMPLETE") ?"COMBINAISON COMPLETE"+"\n":""); + String combinationText = selectedHorses.stream() + .map(String::valueOf) + .collect(Collectors.joining("-")); + tspl.append(combinationText).append("\n"); + tspl.append("COEF: ").append(String.valueOf(pari.getCoefficient())); + tspl.append("\n").append(printama.lineSeparator()).append("\n"); + tspl.append("MONTANT: ").append(pari.getMiseTotale()).append(" XOF"); + tspl.append("\n").append(printama.lineSeparator()).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"); + if (BluetoothUtils.needsBluetoothPermissions()) { + if (!BluetoothUtils.hasBluetoothPermission(requireContext())) { + // 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 + + Printama.with(getContext()).printTextBuilder(tspl, bitmap, pari.getNumeroTicket(), barcode, new Printama.PrintCallback() { + @Override + public void onResult(boolean success, String errorMessage) { + if(!success){ + MessageDialog.showError(getContext(), errorMessage); + }else{ + MessageDialog.showSuccess(getContext(), "Pari imprimé avec succès"); + } + } + }); + } catch (SecurityException e) { + Toast.makeText(requireContext(), + "Permission Bluetooth non accordée", Toast.LENGTH_SHORT).show(); + } + } + + + public Bitmap generateBarcodeBitmap(String contents, int width, int height) throws WriterException { + BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, BarcodeFormat.CODE_128, width, height); + Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + bmp.setPixel(x, y, bitMatrix.get(x, y) ? android.graphics.Color.BLACK : android.graphics.Color.WHITE); + } + } + return bmp; + } + + + @Override public void onResume() { super.onResume(); diff --git a/app/src/main/java/com/example/quiz/ListOFBets.java b/app/src/main/java/com/example/quiz/ListOFBets.java index 0eca0ab..f692036 100644 --- a/app/src/main/java/com/example/quiz/ListOFBets.java +++ b/app/src/main/java/com/example/quiz/ListOFBets.java @@ -16,6 +16,7 @@ import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.GridLayoutManager; +import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -156,6 +157,7 @@ public class ListOFBets extends Fragment { stompManager.subscribe("courses", json->{ requireActivity().runOnUiThread(this::observe); + Log.d("STOMP", json); }); MenuHost menuHost = requireActivity(); diff --git a/app/src/main/java/com/example/quiz/PageQuiz.java b/app/src/main/java/com/example/quiz/PageQuiz.java index b8ad550..2b4fa12 100644 --- a/app/src/main/java/com/example/quiz/PageQuiz.java +++ b/app/src/main/java/com/example/quiz/PageQuiz.java @@ -1,5 +1,6 @@ package com.example.quiz; import android.graphics.Color; +import android.os.Build; import android.os.Bundle; import com.anggastudio.printama.Pref; @@ -8,12 +9,18 @@ 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.SunmiPrinterManager; import com.example.quiz.viewModel.LoginViewModel; import com.example.quiz.viewModel.LogsViewModel; import androidx.appcompat.app.AppCompatActivity; import android.os.Handler; import android.os.Looper; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; import androidx.lifecycle.ViewModelProvider; import androidx.navigation.NavController; import androidx.navigation.Navigation; @@ -22,6 +29,9 @@ import androidx.navigation.ui.NavigationUI; import com.example.quiz.databinding.ActivityPageQuizBinding; +import java.util.HashSet; +import java.util.Set; + import javax.inject.Inject; import dagger.hilt.android.AndroidEntryPoint; @@ -36,6 +46,11 @@ public class PageQuiz extends AppCompatActivity { AuthNavigator authNavigator; private SessionManager sessionManager; + String mobileName; + + + private Set> exemptedFragment = new HashSet<>(); + private Handler handler = new Handler(); private Runnable checkRunnable; @@ -45,7 +60,10 @@ public class PageQuiz extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + exemptedFragment.add(ServerConfig.class); + exemptedFragment.add(Logs.class); sessionManager = SessionManager.newInstance(this); + mobileName = Build.MANUFACTURER; binding = ActivityPageQuizBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); logsViewModel = new ViewModelProvider(this).get(LogsViewModel.class); @@ -121,6 +139,72 @@ public class PageQuiz extends AppCompatActivity { // } + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + View touchedView = findTouchedView(ev); + Fragment currentFragment = getCurrentFragment(); + if(currentFragment != null && exemptedFragment.contains(currentFragment.getClass())){ + return super.dispatchTouchEvent(ev); + } + if(touchedView!=null && "tag_pin_exempt".equals(touchedView.getTag())){ + return super.dispatchTouchEvent(ev); + } + if (authNavigator.isSessionExpired()) { + authNavigator.showPinDialog(() -> { + authNavigator.updatedLastExpiredDate(); + }); + return false; // Consomme l'événement pour ne pas le propager + } + authNavigator.updatedLastExpiredDate(); + return super.dispatchTouchEvent(ev); + } + + private Fragment getCurrentFragment() { + FragmentManager fm = getSupportFragmentManager(); + // Méthode 1: Par conteneur + Fragment fragment = fm.findFragmentById(R.id.nav_host_fragment_content_main); + + // Méthode 2: Par tag si vous utilisez des tags + if (fragment == null) { + fragment = fm.findFragmentByTag("current_fragment"); + } + + return fragment; + } + + private View findTouchedView(MotionEvent ev) { + float x = ev.getRawX(); + float y = ev.getRawY(); + + View rootView = getWindow().getDecorView().getRootView(); + return findViewAtPosition(rootView, (int) x, (int) y); + } + + private View findViewAtPosition(View view, int x, int y) { + if (view == null) return null; + + int[] location = new int[2]; + view.getLocationOnScreen(location); + + int left = location[0]; + int top = location[1]; + int right = left + view.getWidth(); + int bottom = top + view.getHeight(); + + if (x >= left && x <= right && y >= top && y <= bottom) { + if (view instanceof ViewGroup) { + ViewGroup group = (ViewGroup) view; + for (int i = group.getChildCount() - 1; i >= 0; i--) { + View child = group.getChildAt(i); + View found = findViewAtPosition(child, x, y); + if (found != null) return found; + } + } + return view; + } + return null; + } + private void checkPermission(){ Pref.init(this); if (BluetoothUtils.needsBluetoothPermissions()) { @@ -144,7 +228,9 @@ public class PageQuiz extends AppCompatActivity { @Override protected void onResume() { - checkPermission(); + if(!mobileName.toLowerCase().contains("sunmi")){ + checkPermission(); + } super.onResume(); handler = new Handler(Looper.getMainLooper()); checkRunnable = new Runnable() { @@ -153,7 +239,7 @@ public class PageQuiz extends AppCompatActivity { if (sessionManager.isExpired()) { if (!authNavigator.dialogIsShowing()) { authNavigator.showPinDialog(() -> { - sessionManager.updateLastExpiredDate(); + authNavigator.updatedLastExpiredDate(); handler.postDelayed(checkRunnable, 1); }); } diff --git a/app/src/main/java/com/example/quiz/ServerConfig.java b/app/src/main/java/com/example/quiz/ServerConfig.java index b868498..2c400f4 100644 --- a/app/src/main/java/com/example/quiz/ServerConfig.java +++ b/app/src/main/java/com/example/quiz/ServerConfig.java @@ -1,7 +1,9 @@ package com.example.quiz; +import android.annotation.SuppressLint; import android.content.Context; import android.content.pm.PackageManager; +import android.graphics.Rect; import android.os.Build; import android.os.Bundle; @@ -17,7 +19,9 @@ import androidx.recyclerview.widget.RecyclerView; import android.provider.Settings; import android.text.Editable; import android.text.TextWatcher; +import android.util.Log; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; @@ -41,6 +45,7 @@ import java.net.NetworkInterface; import java.util.Collections; import java.util.Enumeration; import java.util.List; +import java.util.Objects; import dagger.hilt.android.AndroidEntryPoint; @@ -61,6 +66,8 @@ public class ServerConfig extends Fragment { PointDeVenteAdapter pointDeVenteAdapter; + TpeResponse existingTpe; + PointDeVente pdv = null; private PointDeVenteViewModel pointDeVenteViewModel; @@ -100,9 +107,66 @@ public class ServerConfig extends Fragment { toolbar.setTitle("Configuration du terminal"); toolbar.setBackgroundColor( getResources().getColor(R.color.primary_green)); } + if(sharedPrefsHelper.get("terminalId") != null){ + viewModel.getTpeById(sharedPrefsHelper.get("terminalId")).observe(getViewLifecycleOwner(), new Observer>() { + @Override + public void onChanged(Result tpeResult) { + switch (tpeResult.status){ + case LOADING: + loader.show("Chargement du TPE"); + break; + case SUCCESS: + loader.dismiss(); + existingTpe = tpeResult.data; + if( existingTpe != null && Objects.equals(sharedPrefsHelper.get("terminalId"), String.valueOf(existingTpe.getId()))){ + _checkPdv(existingTpe); + return; + }else{ + MessageDialog.showError(getContext(), "Ce TPE n'est pas encore configuré"); + _startAutocomplete(); + break; + } + case ERROR: + loader.dismiss(); + MessageDialog.showError(getContext(), "Erreur lors du chargement du TPE"); + break; + } + } + }); + } + _startAutocomplete(); + super.onViewCreated(view, savedInstanceState); + } + + + @SuppressLint("ClickableViewAccessibility") + void _startAutocomplete(){ pointDeVenteAdapter = new PointDeVenteAdapter(); RecyclerView recyclerView = binding.pointRecyclerView; recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + binding.getRoot().setOnTouchListener((v, event) -> { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + // Vérifier si le clic est en dehors de l'autocomplete + if (binding.autocompleteContainer.getVisibility() == View.VISIBLE && !binding.pointDeVente.isEnabled()) { + int[] location = new int[2]; + binding.autocompleteContainer.getLocationOnScreen(location); + Rect rect = new Rect( + location[0], + location[1], + location[0] + binding.autocompleteContainer.getWidth(), + location[1] + binding.autocompleteContainer.getHeight() + ); + + int x = (int) event.getRawX(); + int y = (int) event.getRawY(); + + if (!rect.contains(x, y)) { + binding.autocompleteContainer.setVisibility(View.GONE); + } + } + } + return false; + }); binding.pointDeVente.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { @@ -112,7 +176,7 @@ public class ServerConfig extends Fragment { @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { if(!isUserTap) return; - if(charSequence.toString().isEmpty() || charSequence.toString().length()==0){ + if(charSequence.toString().isEmpty() || !binding.pointDeVente.isEnabled()){ recyclerView.setVisibility(View.GONE); binding.autocompleteContainer.setVisibility(View.GONE); return; @@ -157,7 +221,34 @@ public class ServerConfig extends Fragment { } }); - super.onViewCreated(view, savedInstanceState); + } + + void _checkPdv(Tpe eTpe){ + binding.numeroSerie.setText(eTpe.getNumeroSerie()); + binding.brand.setText(eTpe.getTypeTerminal()); + binding.model.setText(eTpe.getModeleAppareil()); + binding.type.setText(eTpe.getTypeTerminal()); + pointDeVenteViewModel.getPointDeVenteById(String.valueOf(eTpe.getPointDeVenteId())).observe(getViewLifecycleOwner(), new Observer>() { + @Override + public void onChanged(Result pointDeVenteResult) { + switch (pointDeVenteResult.status){ + case LOADING: + loader.show("Chargement du point de vente"); + break; + case SUCCESS: + loader.dismiss(); + binding.pointDeVente.setEnabled(false); + binding.pointDeVente.setText(pointDeVenteResult.data.getNom()); + binding.validate.setClickable(false); + MessageDialog.showSuccess(getContext(), "Ce tpe est déjà configuré"); + break; + case ERROR: + loader.dismiss(); + MessageDialog.showError(getContext(), "Erreur lors du chargement du point de vente"); + break; + } + } + }); } @Override diff --git a/app/src/main/java/com/example/quiz/Settings.java b/app/src/main/java/com/example/quiz/Settings.java index d673051..8e2bbad 100644 --- a/app/src/main/java/com/example/quiz/Settings.java +++ b/app/src/main/java/com/example/quiz/Settings.java @@ -134,6 +134,10 @@ public class Settings extends Fragment { authNavigator.navigate(agentManagement); } }); + + binding.logout.setOnClickListener(v -> { + authNavigator.logOut(); + }); } diff --git a/app/src/main/java/com/example/quiz/WinTicket.java b/app/src/main/java/com/example/quiz/WinTicket.java index 7a3aacc..aeb865c 100644 --- a/app/src/main/java/com/example/quiz/WinTicket.java +++ b/app/src/main/java/com/example/quiz/WinTicket.java @@ -1,26 +1,59 @@ package com.example.quiz; +import android.annotation.SuppressLint; +import android.graphics.Color; import android.os.Bundle; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; 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 android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import com.example.quiz.data.model.ParisResponse; +import com.example.quiz.data.model.enums.PariStatut; import com.example.quiz.databinding.FragmentWinTicketBinding; +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.LogsViewModel; +import com.example.quiz.viewModel.PariViewModel; import com.google.android.material.appbar.MaterialToolbar; +import org.w3c.dom.Text; + +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; + +import dagger.hilt.android.AndroidEntryPoint; + /** * A simple {@link Fragment} subclass. * Use the {@link WinTicket#newInstance} factory method to * create an instance of this fragment. */ +@AndroidEntryPoint public class WinTicket extends Fragment { FragmentWinTicketBinding binding; + PariViewModel pariViewModel; + + LogsViewModel logsViewModel; + + SharedPrefsHelper sharedPrefsHelper; + + LoaderDialog loader; + public WinTicket() { // Required empty public constructor @@ -48,6 +81,10 @@ public class WinTicket extends Fragment { @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); + pariViewModel = new ViewModelProvider(this).get(PariViewModel.class); + logsViewModel = new ViewModelProvider(this).get(LogsViewModel.class); + sharedPrefsHelper = SharedPrefsHelper.getInstance(getContext()); + loader = new LoaderDialog(getContext()); binding.winTicketBtnBack.setOnClickListener(v -> { getActivity().onBackPressed(); }); @@ -58,6 +95,173 @@ public class WinTicket extends Fragment { }); scannerDialog.show(getParentFragmentManager(), "scanner"); }); + binding.winTicketVerificationBtn.setOnClickListener(v -> { + String reference = binding.referenceTicket.getText().toString(); + if(reference.isEmpty()){ + MessageDialog.showError(getContext(), "Veuillez donner la reference du ticket"); + return; + } + pariViewModel.getPariByNumero(reference).observe(getViewLifecycleOwner(), new Observer>() { + @Override + public void onChanged(Result parisResponseResult) { + switch (parisResponseResult.status){ + case LOADING:{ + loader.show("Recherche du ticket"); + break; + } + case ERROR:{ + MessageDialog.showError(getContext(), parisResponseResult.message); + break; + } + case SUCCESS:{ + if(parisResponseResult.data.getNumeroTicket().equals(reference)){ + loader.dismiss(); + _showDialog(parisResponseResult.data); + }else{ + MessageDialog.showError(getContext(), "Le ticket n'existe pas"); + } + break; + } + } + } + }); + }); + } + + @SuppressLint("SetTextI18n") + void _showDialog(ParisResponse pari){ + LayoutInflater inflater = getLayoutInflater(); + View view = inflater.inflate(R.layout.pay_confirmation_layout, null); + TextView statut = (TextView) view.findViewById(R.id.tv_statut); + statut.setText("Statut: "+String.valueOf(pari.getStatutPari())); + TextView dateHeure = (TextView) view.findViewById(R.id.tv_date_heure); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"); + dateHeure.setText(formatter.format(LocalDateTime.now())); + TextView numeroCourse = (TextView) view.findViewById(R.id.tv_numero_course); + numeroCourse.setText(String.valueOf(pari.getCourseNumero())); + TextView nomCourse = (TextView) view.findViewById(R.id.tv_nom_course); + nomCourse.setText(pari.getCourseNom()); + TextView numeroTicket = (TextView) view.findViewById(R.id.tv_numero_ticket); + numeroTicket.setText(pari.getNumeroTicket()); + TextView datePrise = (TextView) view.findViewById(R.id.tv_date_prise); + datePrise.setText(formatter.format(OffsetDateTime.parse(pari.getDateHeurePrise()))); + TextView combinaison = (TextView) view.findViewById(R.id.tv_combinaison); + combinaison.setText(pari.getCombinaison()); + TextView montant = (TextView) view.findViewById(R.id.tv_montant); + LinearLayout montantLayout = (LinearLayout) view.findViewById(R.id.tv_montant_layout); + Button confirmer = (Button) view.findViewById(R.id.btn_confirmer); + LinearLayout notesLayout = (LinearLayout) view.findViewById(R.id.notes_layout); + TextView notes = (TextView) view.findViewById(R.id.notes); + + TextView messageStatut = (TextView) view.findViewById(R.id.tv_message_statut); + switch (pari.getStatutPari()){ + case GAGNANT:{ + messageStatut.setText("Veuillez confirmer le paiement de ce pari"); + montant.setText(pari.getGainCalcule()+" CFA"); + break; + } + case A_REMBOURSER:{ + messageStatut.setText("Veuillez confirmer le remboursement de ce pari"); + montant.setText(pari.getMiseTotale()+" CFA"); + break; + } + case PAYE:{ + messageStatut.setText("Ce pari a déjà été payé"); + montant.setText(pari.getGainCalcule()+" CFA"); + notesLayout.setVisibility(View.VISIBLE); + notes.setText(pari.getNotes()); + confirmer.setVisibility(View.GONE); + break; + } + default:{ + montantLayout.setVisibility(View.GONE); + statut.setTextColor(Color.parseColor("#cf1c08")); + messageStatut.setVisibility(View.GONE); + confirmer.setVisibility(View.GONE); + break; + } + } + + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setView(view); + AlertDialog dialog = builder.create(); + Button annuler = (Button) view.findViewById(R.id.btn_annuler); + annuler.setOnClickListener(v -> { + dialog.dismiss(); + }); + confirmer.setOnClickListener(v -> { + switch (pari.getStatutPari()){ + case A_REMBOURSER:{ + pariViewModel.rembourserPari(pari.getNumeroTicket()).observe(getViewLifecycleOwner(), new Observer>() { + @Override + public void onChanged(Result pariResponseResult) { + switch (pariResponseResult.status){ + case LOADING: { + loader.show("Remboursement en cours"); + break; + } + case ERROR: { + loader.dismiss(); + MessageDialog.showError(getContext(), pariResponseResult.message); + break; + } + case SUCCESS: { + loader.dismiss(); + if(sharedPrefsHelper.get("id") == null){ + MessageDialog.showError(getContext(), "Erreur de connexion"); + return; + } + logsViewModel.insertLog(sharedPrefsHelper.get("id"), "REMBOURSEMENT TICKET", "Remboursement du ticket numéro "+ pari.getNumeroTicket()+" montant "+pari.getMiseTotale(), System.currentTimeMillis()); + MessageDialog.showSuccess(getContext(), "Remboursement effectué avec succès"); + binding.referenceTicket.setText(""); + dialog.dismiss(); + break; + } + } + } + }); + break; + } + case GAGNANT:{ + pariViewModel.payerPari(pari.getNumeroTicket()).observe(getViewLifecycleOwner(), new Observer>() { + @Override + public void onChanged(Result pariResponseResult) { + switch (pariResponseResult.status){ + case LOADING: { + loader.show("Paiement en cours"); + break; + } + case ERROR: { + loader.dismiss(); + MessageDialog.showError(getContext(), pariResponseResult.message); + break; + } + case SUCCESS: { + loader.dismiss(); + if(sharedPrefsHelper.get("id") == null){ + MessageDialog.showError(getContext(), "Erreur de connexion"); + return; + } + logsViewModel.insertLog(sharedPrefsHelper.get("id"), "PAIEMENT TICKET", "Paiement du ticket numéro "+ pari.getNumeroTicket()+" montant "+pari.getGainCalcule(), System.currentTimeMillis()); + binding.referenceTicket.setText(""); + MessageDialog.showSuccess(getContext(), "Paiement effectué avec succès"); + dialog.dismiss(); + break; + } + } + } + }); + break; + } + default:{ + dialog.dismiss(); + MessageDialog.showError(getContext(), "Action non comprise pour ce ticket"); + binding.referenceTicket.setText(""); + break; + } + } + }); + dialog.show(); } @Override diff --git a/app/src/main/java/com/example/quiz/data/adapter/LastBetsAdapter.java b/app/src/main/java/com/example/quiz/data/adapter/LastBetsAdapter.java index ff78ef5..1590207 100644 --- a/app/src/main/java/com/example/quiz/data/adapter/LastBetsAdapter.java +++ b/app/src/main/java/com/example/quiz/data/adapter/LastBetsAdapter.java @@ -1,9 +1,16 @@ package com.example.quiz.data.adapter; +import static java.security.AccessController.getContext; + +import android.graphics.Color; import android.text.TextUtils; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.Button; +import android.widget.Spinner; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; @@ -17,21 +24,23 @@ import java.util.List; public class LastBetsAdapter extends RecyclerView.Adapter { private List listeParis = new ArrayList<>(); // évite les NPE - private OnBetClick onBetClick; // peut être null si tu veux - public interface OnBetClick { + private onPariClickListener listener; + + public interface onPariClickListener { void onItemClick(ParisResponse pari); + + void onItemCancel(ParisResponse pari); } - public LastBetsAdapter(List listeParis, OnBetClick onBetClick) { + public void setPariClickListener(onPariClickListener listener) { + this.listener = listener; + } + + public LastBetsAdapter(List listeParis) { if (listeParis != null) this.listeParis = listeParis; - this.onBetClick = onBetClick; } - // Constructeur vide utile si tu veux créer puis setData ensuite - public LastBetsAdapter(OnBetClick onBetClick) { - this.onBetClick = onBetClick; - } public void setData(List newList) { if (newList == null) { @@ -77,10 +86,44 @@ public class LastBetsAdapter extends RecyclerView.Adapter { - if (onBetClick != null) onBetClick.onItemClick(pari); + holder.btnAnnuler.setOnClickListener(v -> { + if (listener != null) { + listener.onItemCancel(pari); + } + }); + + holder.btnImprimer.setOnClickListener(v -> { + if (listener != null) { + listener.onItemClick(pari); + } }); } @@ -90,7 +133,8 @@ public class LastBetsAdapter extends RecyclerView.Adapter> derniersParis(@Path("agentId") String agentId); - @PATCH("paris/numero/{numeroTicket}/statut") - Call annulerPari(@Path("numeroTicket") String numeroTicket, - @Body() CancelParisPaylaod cancelParisPaylaod); + @PATCH("paris/numero/{numeroTicket}/cancel") + Call annulerPari(@Path("numeroTicket") String numeroTicket); + @PATCH("paris/numero/{numeroTicket}/rembourser") + Call rembourserPari(@Path("numeroTicket") String numeroTicket); + + @PATCH("paris/numero/{numeroTicket}/pay") + Call payerPari(@Path("numeroTicket") String numeroTicket); + + + @GET("paris/numero/{numeroTicket}") + Call getPariByNumero(@Path("numeroTicket") String numeroTicket); @GET("paris/agent/{agentId}/solde/course/{courseId}") Call getSoldeByCourse(@Path("agentId") String createdBy, @Path("courseId") String courseId); @@ -58,9 +66,15 @@ public interface ApiService { @POST("terminaux") Call createTpe(@Body Tpe tpe); + @GET("terminaux/{id}") + Call getTpeById(@Path("id") String id); + @GET("points-de-vente") Call> getPointsDeVente(@Query("nom") String nom); + @GET("points-de-vente/{id}") + Call getPointDeVenteById(@Path("id") String id); + @PATCH("agents/me/pin") Call changePin(@Body ChangePin pin); diff --git a/app/src/main/java/com/example/quiz/data/remote/StompManager.java b/app/src/main/java/com/example/quiz/data/remote/StompManager.java index 7238498..e60095b 100644 --- a/app/src/main/java/com/example/quiz/data/remote/StompManager.java +++ b/app/src/main/java/com/example/quiz/data/remote/StompManager.java @@ -61,7 +61,7 @@ public class StompManager { } // URL de connexion WebSocket - String url = "wss://boxer-adapting-bluegill.ngrok-free.app/ws"; + String url = "wss://alr.pmu.ml/ws"; try { // Créer le client STOMP diff --git a/app/src/main/java/com/example/quiz/data/repository/AgentRepository.java b/app/src/main/java/com/example/quiz/data/repository/AgentRepository.java index f8126e6..aa32e60 100644 --- a/app/src/main/java/com/example/quiz/data/repository/AgentRepository.java +++ b/app/src/main/java/com/example/quiz/data/repository/AgentRepository.java @@ -5,11 +5,14 @@ import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import com.example.quiz.data.model.Course; +import com.example.quiz.data.model.ResponseError; 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 com.google.gson.Gson; +import java.io.IOException; import java.util.List; import javax.inject.Inject; @@ -34,7 +37,14 @@ public class AgentRepository { if (response.isSuccessful()) { liveAgents.postValue(Result.success(response.body())); } else { - liveAgents.postValue(Result.error(response.message())); + try { + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + liveAgents.postValue(Result.error(errorResponse.getMessage())); + } catch (IOException e) { + throw new RuntimeException(e); + } } } @Override @@ -54,7 +64,14 @@ public class AgentRepository { if (response.isSuccessful()) { liveAgent.postValue(Result.success(response.body())); } else { - liveAgent.postValue(Result.error(response.message())); + try { + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + liveAgent.postValue(Result.error(errorResponse.getMessage())); + } catch (IOException e) { + throw new RuntimeException(e); + } } } @Override @@ -74,7 +91,14 @@ public class AgentRepository { if (response.isSuccessful()) { liveAvailableBets.postValue(Result.success(response.body())); } else { - liveAvailableBets.postValue(Result.error(response.message())); + try{ + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + liveAvailableBets.postValue(Result.error(errorResponse.getMessage())); + }catch (Exception e){ + liveAvailableBets.postValue(Result.error(e.getMessage())); + } } } @Override @@ -94,7 +118,14 @@ public class AgentRepository { if (response.isSuccessful()) { liveSetAccess.postValue(Result.success(null)); } else { - liveSetAccess.postValue(Result.error(response.message())); + try { + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + liveSetAccess.postValue(Result.error(errorResponse.getMessage())); + } catch (IOException e) { + throw new RuntimeException(e); + } } } @Override @@ -114,7 +145,14 @@ public class AgentRepository { if (response.isSuccessful()) { liveSetRestrictions.postValue(Result.success(null)); } else { - liveSetRestrictions.postValue(Result.error(response.message())); + try { + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + liveSetRestrictions.postValue(Result.error(errorResponse.getMessage())); + } catch (IOException e) { + throw new RuntimeException(e); + } } } @Override diff --git a/app/src/main/java/com/example/quiz/data/repository/CourseRepository.java b/app/src/main/java/com/example/quiz/data/repository/CourseRepository.java index 33ec1c2..e61b997 100644 --- a/app/src/main/java/com/example/quiz/data/repository/CourseRepository.java +++ b/app/src/main/java/com/example/quiz/data/repository/CourseRepository.java @@ -7,8 +7,10 @@ import androidx.lifecycle.MutableLiveData; import com.example.quiz.data.model.Course; import com.example.quiz.data.model.PagedModel; +import com.example.quiz.data.model.ResponseError; import com.example.quiz.data.remote.ApiService; import com.example.quiz.utils.Result; +import com.google.gson.Gson; import javax.inject.Inject; @@ -33,22 +35,15 @@ public class CourseRepository { public void onResponse(Call> call, Response> response) { if(response.isSuccessful()){ liveCourses.postValue(Result.success(response.body())); -// PagedModel openedPagesCourses = new PagedModel<>(); -// List 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())); + try{ + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + liveCourses.postValue(Result.error(errorResponse.getMessage())); + } catch (Exception e) { + throw new RuntimeException(e); + } } } diff --git a/app/src/main/java/com/example/quiz/data/repository/LoginRepository.java b/app/src/main/java/com/example/quiz/data/repository/LoginRepository.java index 93331bc..2750031 100644 --- a/app/src/main/java/com/example/quiz/data/repository/LoginRepository.java +++ b/app/src/main/java/com/example/quiz/data/repository/LoginRepository.java @@ -5,6 +5,7 @@ import android.util.Log; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; +import com.example.quiz.data.model.ResponseError; 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; @@ -12,6 +13,9 @@ 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; +import com.google.gson.Gson; + +import java.io.IOException; import javax.inject.Inject; @@ -37,10 +41,16 @@ public class LoginRepository { public void onResponse(Call call, Response response) { if(response.isSuccessful()){ liveLogin.postValue(Result.success(response.body())); - Log.d("TOKEN", response.body().getToken()); tokenManager.saveToken(response.body().getToken()); }else{ - liveLogin.postValue(Result.error(response.toString())); + try { + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + liveLogin.postValue(Result.error(errorResponse.getMessage())); + } catch (IOException e) { + throw new RuntimeException(e); + } } } @@ -64,7 +74,14 @@ public class LoginRepository { if(response.isSuccessful()) { liveUser.postValue(Result.success(response.body())); }else { - liveUser.postValue(Result.error(response.message())); + try { + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + liveUser.postValue(Result.error(errorResponse.getMessage())); + } catch (IOException e) { + throw new RuntimeException(e); + } } } @Override diff --git a/app/src/main/java/com/example/quiz/data/repository/PariMiseRepository.java b/app/src/main/java/com/example/quiz/data/repository/PariMiseRepository.java index c7506b8..6d17815 100644 --- a/app/src/main/java/com/example/quiz/data/repository/PariMiseRepository.java +++ b/app/src/main/java/com/example/quiz/data/repository/PariMiseRepository.java @@ -1,13 +1,17 @@ package com.example.quiz.data.repository; +import android.util.Log; + 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.model.ResponseError; import com.example.quiz.data.remote.ApiService; import com.example.quiz.data.remote.TokenManager; import com.example.quiz.utils.Result; +import com.google.gson.Gson; import java.util.List; @@ -34,7 +38,14 @@ public class PariMiseRepository { if (response.isSuccessful()) { livePariMise.postValue(Result.success(response.body())); }else{ - livePariMise.postValue(Result.error(response.message())); + try{ + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + livePariMise.postValue(Result.error(errorResponse.getMessage())); + } catch (Exception e) { + throw new RuntimeException(e); + } } } @Override diff --git a/app/src/main/java/com/example/quiz/data/repository/PariRepository.java b/app/src/main/java/com/example/quiz/data/repository/PariRepository.java index 047ac96..ddc31cf 100644 --- a/app/src/main/java/com/example/quiz/data/repository/PariRepository.java +++ b/app/src/main/java/com/example/quiz/data/repository/PariRepository.java @@ -8,10 +8,12 @@ import androidx.lifecycle.MutableLiveData; import com.example.quiz.data.model.Pari; import com.example.quiz.data.model.ParisResponse; +import com.example.quiz.data.model.ResponseError; 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; +import com.google.gson.Gson; import java.util.Collections; import java.util.List; @@ -44,13 +46,10 @@ public class PariRepository { } else { // On récupère l'erreur exacte envoyée par le backend try { - Map errorBody = Collections.emptyMap(); - if(response.errorBody() != null){ - errorBody = (Map) response.errorBody(); - } - Log.d("PariRepository", response.errorBody().toString()); - pariResponse.postValue(Result.error(errorBody.get("message"))); - + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + pariResponse.postValue(Result.error(errorResponse.getMessage())); } catch (Exception e) { pariResponse.postValue(Result.error("Erreur serveur")); } @@ -76,10 +75,10 @@ public class PariRepository { derniersParis.postValue(Result.success(response.body())); }else{ try{ - String errorBody = response.errorBody() != null ? - response.errorBody().string() : "Erreur inconnue"; - - derniersParis.postValue(Result.error(errorBody)); + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + derniersParis.postValue(Result.error(errorResponse.getMessage())); }catch (Exception e){ derniersParis.postValue(Result.error(e.getMessage())); } @@ -95,21 +94,74 @@ public class PariRepository { return derniersParis; } - public LiveData> annulerPari(String numeroTicket){ + public LiveData> payTicket(String numeroTicket){ MutableLiveData> pariResponse = new MutableLiveData<>(); pariResponse.setValue(Result.loading()); - CancelParisPaylaod cancelParisPaylaod = new CancelParisPaylaod("ANNULE"); - apiService.annulerPari(numeroTicket, cancelParisPaylaod).enqueue(new Callback(){ + apiService.payerPari(numeroTicket).enqueue(new Callback(){ @Override public void onResponse(Call call, Response response) { if(response.isSuccessful()){ pariResponse.postValue(Result.success(response.body())); }else{ try{ - String errorBody = response.errorBody() != null ? - response.errorBody().string() : "Erreur inconnue"; + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + pariResponse.postValue(Result.error(errorResponse.getMessage())); + }catch (Exception e){ + pariResponse.postValue(Result.error(e.getMessage())); + } + } + }; + @Override + public void onFailure(Call call, Throwable throwable) { + pariResponse.postValue(Result.error(throwable.getMessage())); + } + }); + return pariResponse; + } - pariResponse.postValue(Result.error(errorBody)); + public LiveData> rembourserTicket(String numeroTicket){ + MutableLiveData> pariResponse = new MutableLiveData<>(); + pariResponse.setValue(Result.loading()); + apiService.rembourserPari(numeroTicket).enqueue(new Callback(){ + @Override + public void onResponse(Call call, Response response) { + if(response.isSuccessful()){ + pariResponse.postValue(Result.success(response.body())); + }else{ + try{ + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + pariResponse.postValue(Result.error(errorResponse.getMessage())); + }catch (Exception e){ + pariResponse.postValue(Result.error(e.getMessage())); + } + } + } + @Override + public void onFailure(Call call, Throwable throwable) { + pariResponse.postValue(Result.error(throwable.getMessage())); + } + }); + return pariResponse; + } + + public LiveData> annulerPari(String numeroTicket){ + MutableLiveData> pariResponse = new MutableLiveData<>(); + pariResponse.setValue(Result.loading()); + apiService.annulerPari(numeroTicket).enqueue(new Callback(){ + @Override + public void onResponse(Call call, Response response) { + if(response.isSuccessful()){ + pariResponse.postValue(Result.success(response.body())); + }else{ + try{ + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + pariResponse.postValue(Result.error(errorResponse.getMessage())); }catch (Exception e){ pariResponse.postValue(Result.error(e.getMessage())); } @@ -134,7 +186,10 @@ public class PariRepository { solde.postValue(Result.success(response.body())); }else{ try{ - solde.postValue(Result.error(response.errorBody().string())); + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + solde.postValue(Result.error(errorResponse.getMessage())); }catch (Exception e){ solde.postValue(Result.error(e.getMessage())); } @@ -159,7 +214,10 @@ public class PariRepository { solde.postValue(Result.success(response.body())); }else{ try { - solde.postValue(Result.error(response.errorBody().string())); + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + solde.postValue(Result.error(errorResponse.getMessage())); }catch (Exception e){ solde.postValue(Result.error(e.getMessage())); } @@ -174,4 +232,32 @@ public class PariRepository { return solde; } + public LiveData> getPariByNumero(String numeroTicket) { + MutableLiveData> pari = new MutableLiveData<>(); + pari.setValue(Result.loading()); + apiService.getPariByNumero(numeroTicket).enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (response.isSuccessful()) { + pari.postValue(Result.success(response.body())); + }else{ + try { + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + pari.postValue(Result.error(errorResponse.getMessage())); + }catch (Exception e){ + pari.postValue(Result.error(e.getMessage())); + } + } + } + + @Override + public void onFailure(Call call, Throwable throwable) { + pari.postValue(Result.error(throwable.getMessage())); + } + }); + return pari; + } + } diff --git a/app/src/main/java/com/example/quiz/data/repository/PointDeVenteRepository.java b/app/src/main/java/com/example/quiz/data/repository/PointDeVenteRepository.java index 28d250c..039163b 100644 --- a/app/src/main/java/com/example/quiz/data/repository/PointDeVenteRepository.java +++ b/app/src/main/java/com/example/quiz/data/repository/PointDeVenteRepository.java @@ -5,10 +5,14 @@ import androidx.lifecycle.MutableLiveData; import com.example.quiz.data.model.PagedModel; import com.example.quiz.data.model.PointDeVente; +import com.example.quiz.data.model.ResponseError; import com.example.quiz.data.remote.ApiService; import com.example.quiz.utils.Result; +import com.google.gson.Gson; +import java.io.IOException; + import javax.inject.Inject; import retrofit2.*; @@ -36,18 +40,14 @@ public class PointDeVenteRepository { if (response.isSuccessful() && response.body() != null) { result.postValue(Result.success(response.body())); } else { - - String errorMessage = "Erreur inconnue"; - try { - if (response.errorBody() != null) { - errorMessage = response.errorBody().string(); - } - } catch (Exception e) { - errorMessage = e.getMessage(); + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + result.postValue(Result.error(errorResponse.getMessage())); + } catch (IOException e) { + throw new RuntimeException(e); } - - result.postValue(Result.error(errorMessage)); } } @@ -62,4 +62,33 @@ public class PointDeVenteRepository { return result; } + + public LiveData> getPointDeVenteById(String id) { + MutableLiveData> result = new MutableLiveData<>(); + result.setValue(Result.loading()); + apiService.getPointDeVenteById(id).enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if(response.isSuccessful()){ + result.postValue(Result.success(response.body())); + }else{ + try { + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + result.postValue(Result.error(errorResponse.getMessage())); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + @Override + public void onFailure(Call call, Throwable throwable) { + result.postValue(Result.error(throwable.getMessage())); + } + }); + return result; + } + } diff --git a/app/src/main/java/com/example/quiz/data/repository/TpeRepository.java b/app/src/main/java/com/example/quiz/data/repository/TpeRepository.java index 6d134b4..6d57c87 100644 --- a/app/src/main/java/com/example/quiz/data/repository/TpeRepository.java +++ b/app/src/main/java/com/example/quiz/data/repository/TpeRepository.java @@ -3,10 +3,12 @@ package com.example.quiz.data.repository; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; +import com.example.quiz.data.model.ResponseError; import com.example.quiz.data.model.Tpe; import com.example.quiz.data.model.TpeResponse; import com.example.quiz.data.remote.ApiService; import com.example.quiz.utils.Result; +import com.google.gson.Gson; import javax.inject.Inject; @@ -33,7 +35,43 @@ public class TpeRepository { if (response.isSuccessful()) { tpeLiveData.postValue(Result.success(response.body())); } else { - tpeLiveData.postValue(Result.error(response.message())); + try{ + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + tpeLiveData.postValue(Result.error(errorResponse.getMessage())); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + @Override + public void onFailure(Call call, Throwable throwable) { + tpeLiveData.postValue(Result.error(throwable.getMessage())); + } + }); + return tpeLiveData; + } + + + public LiveData> getTpeById(String id) { + MutableLiveData> tpeLiveData = new MutableLiveData<>(); + tpeLiveData.setValue(Result.loading()); + apiService.getTpeById(id).enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (response.isSuccessful()) { + tpeLiveData.postValue(Result.success(response.body())); + } else { + try{ + String error = response.errorBody().string(); + Gson gson = new Gson(); + ResponseError errorResponse = gson.fromJson(error, ResponseError.class); + tpeLiveData.postValue(Result.error(errorResponse.getMessage())); + } catch (Exception e) { + throw new RuntimeException(e); + } } } diff --git a/app/src/main/java/com/example/quiz/utils/AuthNavigator.java b/app/src/main/java/com/example/quiz/utils/AuthNavigator.java index f77aac9..3a984b2 100644 --- a/app/src/main/java/com/example/quiz/utils/AuthNavigator.java +++ b/app/src/main/java/com/example/quiz/utils/AuthNavigator.java @@ -61,14 +61,15 @@ public class AuthNavigator { return; } if (sessionManager.isExpired()) { - showPinDialog(() -> { - sessionManager.updateLastExpiredDate(); - fragmentManager.beginTransaction() - .replace(R.id.nav_host_fragment_content_main, destinationFragment) - .addToBackStack(null) - .commit(); - }); - + if(!dialogIsShowing()){ + showPinDialog(() -> { + sessionManager.updateLastExpiredDate(); + fragmentManager.beginTransaction() + .replace(R.id.nav_host_fragment_content_main, destinationFragment) + .addToBackStack(null) + .commit(); + }); + } } else { fragmentManager.beginTransaction() .replace(R.id.nav_host_fragment_content_main, destinationFragment) @@ -143,6 +144,15 @@ public class AuthNavigator { return dialog.isShowing(); } + public boolean isSessionExpired(){ + return sessionManager.isExpired(); + } + + + public void updatedLastExpiredDate(){ + sessionManager.updateLastExpiredDate(); + } + private void loginSuccess(LoginResponse loginResponse){ String terminalId = prefsHelper.get("terminalId"); if(terminalId == null){ @@ -158,4 +168,14 @@ public class AuthNavigator { prefsHelper.save("isSupAgent", isSupAgent); } + public void logOut (){ + prefsHelper.save("id", null); + prefsHelper.save("firstName", null); + prefsHelper.save("lastName", null); + prefsHelper.save("profile", null); + prefsHelper.save("code", null); + prefsHelper.save("isSupAgent", null); + sessionManager.setToExpire(); + } + } diff --git a/app/src/main/java/com/example/quiz/utils/SessionManager.java b/app/src/main/java/com/example/quiz/utils/SessionManager.java index b0104b0..c8724ab 100644 --- a/app/src/main/java/com/example/quiz/utils/SessionManager.java +++ b/app/src/main/java/com/example/quiz/utils/SessionManager.java @@ -31,6 +31,10 @@ public class SessionManager { lastExpiredDate = LocalDateTime.now(); } + public void setToExpire(){ + lastExpiredDate = null; + } + public boolean isExpired() { if (lastExpiredDate == null) { return true; diff --git a/app/src/main/java/com/example/quiz/utils/SunmiPrinterManager.java b/app/src/main/java/com/example/quiz/utils/SunmiPrinterManager.java new file mode 100644 index 0000000..34436ee --- /dev/null +++ b/app/src/main/java/com/example/quiz/utils/SunmiPrinterManager.java @@ -0,0 +1,275 @@ +package com.example.quiz.utils; + +import android.content.Context; +import android.graphics.Bitmap; +import android.os.RemoteException; +import android.util.Log; + +import com.sunmi.peripheral.printer.InnerPrinterCallback; +import com.sunmi.peripheral.printer.InnerPrinterManager; +import com.sunmi.peripheral.printer.InnerResultCallback; +import com.sunmi.peripheral.printer.SunmiPrinterService; + +import java.util.List; +import java.util.function.Consumer; + +public class SunmiPrinterManager { + + private static final String TAG = "SunmiPrinterManager"; + private static SunmiPrinterManager instance; + + private static final int MAX_CHAR_2_INCH = 32; + private static Context context; + + private SunmiPrinterService sunmiPrinter; + + private SunmiPrinterManager() {} + + public static SunmiPrinterManager getInstance(Context ctx) { + if (instance == null) { + instance = new SunmiPrinterManager(); + context = ctx.getApplicationContext(); + } + return instance; + } + + // 🔌 Bind UNE SEULE FOIS + public void connectPrinter(Consumer status) { + Log.d("######", "Point d'entrée printer"); + try { + InnerPrinterManager.getInstance().bindService(context, new InnerPrinterCallback() { + @Override + protected void onConnected(SunmiPrinterService service) { + Log.d("######", "Connecté printer"); + sunmiPrinter = service; + status.accept(true); + } + + @Override + protected void onDisconnected() { + Log.d("######", "Déconnecté printer"); + sunmiPrinter = null; + status.accept(false); + } + }); + } catch (Exception e) { + Log.e(TAG, "#################Erreur bindService"+String.valueOf(e)); + } + } + + // ✅ simple check + + // 🖨️ Impression robuste + public void printText(String text, PrinterListener listener) { + try { + sunmiPrinter.printText(text + "\n", new InnerResultCallback() { + + @Override + public void onRunResult(boolean isSuccess) { + if(!isSuccess){ + try { + throw new Exception("Impression", new Throwable("Erreur lors d'impression!")); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + @Override + public void onReturnString(String result) { + // rarement utile + } + + @Override + public void onRaiseException(int code, String msg) { + listener.onError(msg); + listener.onResult(code); + } + + + @Override + public void onPrintResult(int code, String msg) { + listener.onResult(code); + listener.onError(msg); + } + }); + + } catch (Exception e) { + if (listener != null) { + listener.onError(e.getMessage()); + } + } + } + + public int getMaxChar(){ + return MAX_CHAR_2_INCH; + } + + public boolean printPari(Bitmap logo, Bitmap barcode, String numeroTicket, String text){ + try{ + printSimpleImage(logo); + printTextSimple("\n"+separationText()+"\n"); + printSimpleImage(barcode); + setAlignment(1, new PrinterListener() { + @Override + public void onResult(int code) { + + } + @Override + public void onError(String errorMessage) { + + } + }); + printTextSimple(numeroTicket); + printTextSimple("\n"+separationText()+"\n"); + setAlignment(0, new PrinterListener() { + @Override + public void onResult(int code) {} + @Override + public void onError(String errorMessage) {} + }); + printTextSimple(text); + printTextSimple("\n"+separationText()+"\n"); + setAlignment(1, new PrinterListener() { + @Override + public void onResult(int code) {} + @Override + public void onError(String errorMessage) {} + }); + printTextSimple("Powered by PMU-MALI"); + printTextSimple("\n"+separationText()+"\n"); + printTextSimple(" "); + printTextSimple(" "); + return true; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public String separationText(){ + return "-".repeat(MAX_CHAR_2_INCH); + } + + + public void printSimpleImage(Bitmap bitmap){ + printImage(bitmap, new PrinterListener() { + @Override + public void onResult(int code) { + Log.d(TAG, "Print OK"); + } + @Override + public void onError(String errorMessage) { + Log.e(TAG, errorMessage); + } + }); + } + + // version simple + public void printTextSimple(String text) { + printText(text, new PrinterListener() { + @Override + public void onResult(int code) { + Log.d(TAG, "Print OK"); + } + + @Override + public void onError(String errorMessage) { + Log.e(TAG, errorMessage); + } + }); + } + + // Dans SunmiPrinterManager.java + + public void setAlignment(int alignment, PrinterListener listener) { + try { + // Appel au SDK Sunmi : 0=gauche, 1=centre, 2=droite + sunmiPrinter.setAlignment(alignment, new InnerResultCallback() { + @Override + public void onRunResult(boolean isSuccess) throws RemoteException { + if(!isSuccess){ + try { + throw new Exception("Alignement", new Throwable("L'alignement n'a pas pu être défini")); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + @Override + public void onReturnString(String result) throws RemoteException { + + } + + @Override + public void onRaiseException(int code, String msg) throws RemoteException { + listener.onError(msg); + listener.onResult(code); + } + + @Override + public void onPrintResult(int code, String msg) throws RemoteException { + + } + }); + } catch (Exception e) { + if (listener != null) listener.onError(e.getMessage()); + } + } + + public void printImage(Bitmap bitmap, PrinterListener listener){ + try { + // La plupart des imprimantes Sunmi utilisent cette méthode + // Le paramètre '0' correspond généralement à l'alignement (0 = gauche) + sunmiPrinter.printBitmap(bitmap, new InnerResultCallback() { + @Override + public void onRunResult(boolean isSuccess) { + if(!isSuccess){ + try { + throw new Exception("Impression", new Throwable("Erreur lors d'impression d'une image!")); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + @Override + public void onReturnString(String result) { + // Pas utile + } + + @Override + public void onRaiseException(int code, String msg) { + Log.e(TAG, "onRaiseException: "+String.valueOf(code)); + listener.onResult(code); + listener.onError(msg); + } + + @Override + public void onPrintResult(int code, String msg) { + Log.e(TAG, "onPrintResult: "+String.valueOf(code)); + listener.onResult(code); + listener.onError(msg); + } + // N'oublie pas les autres callbacks vides (onRunResult, etc.) + }); + } catch (Exception e) { + listener.onError(e.getMessage()); + } + } + + + + public int printerStatus(){ + try { + return sunmiPrinter.updatePrinterState(); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + public interface PrinterListener { + void onResult(int code); + void onError(String errorMessage); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/quiz/viewModel/PariViewModel.java b/app/src/main/java/com/example/quiz/viewModel/PariViewModel.java index 8ec6a59..9eb81ac 100644 --- a/app/src/main/java/com/example/quiz/viewModel/PariViewModel.java +++ b/app/src/main/java/com/example/quiz/viewModel/PariViewModel.java @@ -24,10 +24,17 @@ public class PariViewModel extends ViewModel { private LiveData> pariAnnule; + private LiveData> pariPaye; + + + private LiveData> pariByNumero; + private LiveData> solde; private LiveData> soldeByDay; + private LiveData> pariRembourse; + @Inject public PariViewModel(PariRepository repository){ this.pariRepository = repository; @@ -57,4 +64,19 @@ public class PariViewModel extends ViewModel { soldeByDay = pariRepository.getSoldeByDay(agentId, day); return soldeByDay; } + + public LiveData> getPariByNumero(String numeroTicket){ + pariByNumero = pariRepository.getPariByNumero(numeroTicket); + return pariByNumero; + } + + public LiveData> payerPari(String numeroTicket){ + pariPaye = pariRepository.payTicket(numeroTicket); + return pariPaye; + } + + public LiveData> rembourserPari(String numeroTicket){ + pariRembourse = pariRepository.rembourserTicket(numeroTicket); + return pariRembourse; + } } diff --git a/app/src/main/java/com/example/quiz/viewModel/PointDeVenteViewModel.java b/app/src/main/java/com/example/quiz/viewModel/PointDeVenteViewModel.java index 137b140..aa5739a 100644 --- a/app/src/main/java/com/example/quiz/viewModel/PointDeVenteViewModel.java +++ b/app/src/main/java/com/example/quiz/viewModel/PointDeVenteViewModel.java @@ -17,6 +17,8 @@ public class PointDeVenteViewModel extends ViewModel { private final PointDeVenteRepository pointDeVenteRepository; private LiveData>> pointsDeVente; + private LiveData> pointDeVenteById; + @Inject public PointDeVenteViewModel(PointDeVenteRepository pointDeVenteRepository){ this.pointDeVenteRepository = pointDeVenteRepository; @@ -26,4 +28,9 @@ public class PointDeVenteViewModel extends ViewModel { this.pointsDeVente = pointDeVenteRepository.getPointsDeVente(search); return this.pointsDeVente; } + + public LiveData> getPointDeVenteById(String id) { + pointDeVenteById = pointDeVenteRepository.getPointDeVenteById(id); + return pointDeVenteById; + } } diff --git a/app/src/main/java/com/example/quiz/viewModel/TpeViewModel.java b/app/src/main/java/com/example/quiz/viewModel/TpeViewModel.java index 476dc86..9844714 100644 --- a/app/src/main/java/com/example/quiz/viewModel/TpeViewModel.java +++ b/app/src/main/java/com/example/quiz/viewModel/TpeViewModel.java @@ -18,6 +18,8 @@ public class TpeViewModel extends ViewModel { private LiveData> tpeLiveData; + private LiveData> tpeByIdLiveData; + @Inject public TpeViewModel(TpeRepository repository){ this.tpeRepository = repository; @@ -27,4 +29,9 @@ public class TpeViewModel extends ViewModel { tpeLiveData = tpeRepository.createTpe(tpe); return tpeLiveData; } + + public LiveData> getTpeById(String id){ + tpeByIdLiveData = tpeRepository.getTpeById(id); + return tpeByIdLiveData; + } } diff --git a/app/src/main/res/layout/dialog_error.xml b/app/src/main/res/layout/dialog_error.xml index ad54b59..9b59dd6 100644 --- a/app/src/main/res/layout/dialog_error.xml +++ b/app/src/main/res/layout/dialog_error.xml @@ -1,4 +1,5 @@ + app:tint="#F44336" /> + android:outlineSpotShadowColor="#4CAF50" + /> -