package com.example.quiz; import android.annotation.SuppressLint; import android.app.Dialog; import android.content.Intent; 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; 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.MutableLiveData; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.GridLayout; import android.widget.TextView; import android.widget.Toast; import com.anggastudio.printama.Printama; import com.example.quiz.data.model.Course; import com.example.quiz.data.model.MiseInitiale; import com.example.quiz.data.model.Pari; import com.example.quiz.data.model.PariMise; import com.example.quiz.data.model.ParisResponse; import com.example.quiz.data.model.TypeOfBet; import com.example.quiz.data.model.dtos.NotifPayload; import com.example.quiz.data.remote.StompManager; import com.example.quiz.databinding.FragmentBetValidationBinding; import com.example.quiz.utils.BluetoothUtils; import com.example.quiz.utils.LoaderDialog; 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; import com.example.quiz.viewModel.SharedViewModel; import com.google.android.material.appbar.MaterialToolbar; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.google.zxing.BarcodeFormat; import com.google.zxing.MultiFormatWriter; import com.google.zxing.WriterException; import com.google.zxing.common.BitMatrix; import java.lang.reflect.Type; import java.time.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.Objects; import java.util.Observable; import java.util.stream.Collectors; import javax.inject.Inject; import dagger.hilt.android.AndroidEntryPoint; /** * A simple {@link Fragment} subclass. * Use the {@link BetValidation#newInstance} factory method to * create an instance of this fragment. */ @AndroidEntryPoint public class BetValidation extends Fragment { FragmentBetValidationBinding binding; SharedViewModel shared; PariMiseViewModel pariMiseViewModel; List misesInitiales; private AlertDialog dialog; @Inject StompManager stompManager; SunmiPrinterManager sunmiPrinterManager; private int nombreX; LogsViewModel logsViewModel; private boolean order; private long mise; String mobileName; private TypeOfBet typeOfBet; MutableLiveData isPrinterReady = new MutableLiveData<>(false); SharedPrefsHelper prefsHelper; private Course course; LoaderDialog loader; private int coeff; private ActivityResultLauncher enableBluetoothLauncher; private final MutableLiveData> selectedHorses = new MutableLiveData<>(List.of()); PariViewModel pariViewModel; public BetValidation() { // Required empty public constructor } public static BetValidation newInstance() { BetValidation fragment = new BetValidation(); Bundle args = new Bundle(); fragment.setArguments(args); return fragment; } @Override 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) { binding = FragmentBetValidationBinding.inflate(inflater, container, false); loader = new LoaderDialog(getContext()); 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() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { if (charSequence.toString().isEmpty()) { binding.coeff.setError("Le coefficient est obligatoire"); return; } if (Integer.parseInt(charSequence.toString()) < 1) { binding.coeff.setError("Le coefficient doit être supérieur ou égal à 1 "); return; } coeff = Integer.parseInt(charSequence.toString()); calculateMise(selectedHorses.getValue()); } @Override public void afterTextChanged(Editable editable) { } }); return binding.getRoot(); } private void setupNumberGrid(GridLayout grid) { binding.gridNumbers.removeAllViews(); int columns = 8; grid.setColumnCount(columns); 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))); } } createNumberItem("X"); grid.addView(createNumberItem("X")); } private TextView createNumberItem(String horse) { TextView textView = new TextView(requireContext()); textView.setText(horse); textView.setTextColor(getResources().getColor(R.color.white)); GridLayout.LayoutParams params = new GridLayout.LayoutParams(); params.setMargins(1, 1, 1, 1); textView.setLayoutParams(params); textView.setTextSize(10); 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.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))) { 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); }); return textView; } boolean _notClickable(List selectedHorses) { int numberOfElement = 0; if (shared.typeOfBet.getValue().getNumberOfHorse() < 2) { return true; } for (String horse : selectedHorses) { numberOfElement = horse.equals("X") ? numberOfElement + 1 : numberOfElement; } nombreX = numberOfElement; return numberOfElement == shared.typeOfBet.getValue().getNumberOfHorse() - 1; } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); shared = new ViewModelProvider(requireActivity()).get(SharedViewModel.class); pariMiseViewModel = new ViewModelProvider(this).get(PariMiseViewModel.class); pariMiseViewModel.getBetInitMise().observe( getViewLifecycleOwner(), result -> { switch (result.status) { case LOADING: { loader.show("Chargement des mise"); break; } case SUCCESS: { misesInitiales = result.data; loader.dismiss(); break; } case ERROR: { loader.dismiss(); MessageDialog.showError(getContext(), result.message); } default: break; } } ); if (shared.selectedCourse.getValue() != null) { stompManager.subscribe("courses/" + shared.selectedCourse.getValue().getId(), json -> { Type type = new TypeToken>() { }.getType(); NotifPayload notif = new Gson().fromJson(json, type); Course updatedCourse = notif.getPayload(); if (shared.selectedCourse.getValue().getId() == updatedCourse.getId()) { shared.setSelectedCourse(updatedCourse); setupNumberGrid(binding.gridNumbers); } }); } setSelectedTypeOfBetImage(); pariViewModel = new ViewModelProvider(this).get(PariViewModel.class); typeOfBet = shared.typeOfBet.getValue(); course = shared.selectedCourse.getValue(); AppCompatActivity activity = (AppCompatActivity) getActivity(); if (activity != null) { MaterialToolbar toolbar = activity.findViewById(R.id.toolbar); toolbar.setBackgroundColor(getResources().getColor(R.color.primary_green)); activity.setSupportActionBar(toolbar); if (activity.getSupportActionBar() != null) { activity.getSupportActionBar().setTitle("Pari " + shared.selectedCourse.getValue().getNom()); } } setupNumberGrid(binding.gridNumbers); binding.paymentType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { String selected = binding.paymentType.getSelectedItem().toString(); if (selected.equals("Orange Money")) { binding.phoneNumber.setVisibility(View.VISIBLE); } else { binding.phoneNumber.setVisibility(View.GONE); // ou GONE binding.phoneNumber.setText(""); // vider le champ si non utilisé } } @Override public void onNothingSelected(AdapterView parent) { binding.phoneNumber.setVisibility(View.INVISIBLE); } }); selectedHorses.observe(getViewLifecycleOwner(), new Observer>() { @Override public void onChanged(List horses) { calculateMise(horses); if (shared.typeOfBet.getValue().getNumberOfHorse() > 2 && horses.size() >= shared.typeOfBet.getValue().getNumberOfHorse()) { binding.order.setVisibility(View.VISIBLE); } else { binding.order.setVisibility(View.GONE); } 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()) { binding.elargie.setVisibility(View.VISIBLE); binding.elargie.setText("Champ partiel"); return; } } } if (horses.size() > shared.typeOfBet.getValue().getNumberOfHorse()) { binding.elargie.setVisibility(View.VISIBLE); binding.elargie.setText("Elargi"); } else { binding.elargie.setVisibility(View.GONE); } } }); binding.order.setOnCheckedChangeListener((buttonView, isChecked) -> { order = isChecked; calculateMise(selectedHorses.getValue()); }); 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) { return; } int required = typeOfBet.getNumberOfHorse(); 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) { Toast.makeText(getContext(), "Pari non valide", Toast.LENGTH_SHORT).show(); return; } Pari pari = new Pari( OffsetDateTime.now(ZoneOffset.UTC).toString(), Long.valueOf(course.getId()), Long.valueOf(prefsHelper.get("id")), Long.valueOf(prefsHelper.get("terminalId")), Long.valueOf(prefsHelper.get("pointDeVenteId")), List.of(new PariMise(String.valueOf(shared.typeOfBet.getValue().getName()), mise)), _getFormule(), _getCombinaison(), coeff, "XOF", "Pari" ); if (dialog != null && dialog.isShowing()) { return; } _showPariDialog(pari); }); binding.backBtn.setOnClickListener(v -> { getActivity().onBackPressed(); }); binding.deleteBtn.setOnClickListener(v -> { _initializeToZero(); }); } 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 selectedHorses.getValue().stream().map(String::valueOf).collect(Collectors.joining(",")); } } List _getFormule() { List combinaison = selectedHorses.getValue(); 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) { return List.of("CHAMP_X", "FORMULE_COMPLETE"); } else { return List.of("CHAMP_X"); } } else { if (order) { return List.of("CHAMP_TOTAL", "FORMULE_COMPLETE"); } else { return List.of("CHAMP_TOTAL"); } } } } private void setSelectedTypeOfBetImage() { switch (shared.typeOfBet.getValue().getName()) { case COUPLE_PLACE: binding.icTypeOfBet.setImageResource(R.drawable.ic_couple_place); break; case COUPLE_GAGNANT: binding.icTypeOfBet.setImageResource(R.drawable.ic_couple_gagnant); break; case TIERCE: binding.icTypeOfBet.setImageResource(R.drawable.ic_tierce); break; case QUARTE: binding.icTypeOfBet.setImageResource(R.drawable.ic_quarte); break; case QUINTE: binding.icTypeOfBet.setImageResource(R.drawable.ic_quinte_plus); break; } } public void printPari(ParisResponse pari) throws WriterException { 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; } try { 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) { 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(); } } 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) { 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())); 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"); 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)); 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 -> { 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) { case ERROR: loader.dismiss(); dialog.dismiss(); MessageDialog.showError(getContext(), pariResult.message); break; case LOADING: loader.show("Prise de pari"); break; case SUCCESS: try { loader.dismiss(); logsViewModel.insertLog(prefsHelper.get("id"), "BET", "Création du pari " + pariResult.data.getNumeroTicket() + ", type de paris: " + pariResult.data.getTypesParisMises().get(0).getTypePari() + ", combinaison:" + selectedHorses.getValue().stream().map(String::valueOf).collect(Collectors.joining("-")), System.currentTimeMillis()); printPari(pariResult.data); dialog.dismiss(); MessageDialog.showSuccess(getContext(), "Pari créé avec succès"); } catch (WriterException e) { loader.dismiss(); MessageDialog.showError(getContext(), e.getMessage()); throw new RuntimeException(e); } break; } } }); }); view.findViewById(R.id.alert_cancel).setOnClickListener(v -> { dialog.dismiss(); }); dialog.show(); } void _initializeToZero() { selectedHorses.setValue(List.of()); binding.combination.setText(""); binding.order.setChecked(false); order = false; binding.order.setVisibility(View.GONE); binding.coeff.setText("1"); coeff = Integer.parseInt(binding.coeff.getText().toString()); setupNumberGrid(binding.gridNumbers); } 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) { return; } 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"); return; } MiseInitiale miseModel = misesInitiales.stream() .filter(miseInitiale -> miseInitiale.getTypePari().equals(courseName)) .findFirst() .orElse(null); if (miseModel == null) { MessageDialog.showError(getContext(), "Erreur lors de l'initialisation de la mise"); _initializeToZero(); return; } mise = miseModel.getMiseInitialeMin(); Course.TypeParis typePari = shared.typeOfBet.getValue().getName(); boolean estCoupleGagnantOuPlace = typePari.equals(Course.TypeParis.COUPLE_GAGNANT) || typePari.equals(Course.TypeParis.COUPLE_PLACE); Log.d("TYPE_PARI", typePari.toString()); if (estCoupleGagnantOuPlace) { // Pour COUPLE_GAGNANT ou COUPLE_PLACE: coefficient < 200 if (coeff > 200) { // Erreur: coefficient trop élevé MessageDialog.showError(getContext(), "Le coefficient doit être au plus 200 pour " + typePari.toString()); _initializeToZero(); return; } } else { // Pour les autres types: coefficient <= 20 if (coeff > 20) { // Erreur: coefficient trop élevé MessageDialog.showError(getContext(), "Le coefficient doit être inférieur ou égal à 20 pour " + typePari.toString()); _initializeToZero(); return; } } mise = mise * coeff; if (nombreX > 0) { if (nombreChevauxSelectionnes == typeOfBetHorses) { 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"); return; } mise = mise * _calculateArrangement(nombreChevauxSelectionnes - typeOfBetHorses, nombreX); } if (order) { mise = mise * _calculateArrangement(typeOfBetHorses, (typeOfBetHorses - nombreX)); } this.mise = mise; binding.mise.setText(mise + " CFA"); return; } if (!order) { mise = mise * _calculateCombinaison(nombreChevauxSelectionnes, typeOfBetHorses); } else { mise = mise * _calculateArrangement(nombreChevauxSelectionnes, typeOfBetHorses); } this.mise = mise; binding.mise.setText(mise + " CFA"); } Long _calculateArrangement(int n, int k) { return _calculateFactorial(n) / _calculateFactorial(n - k); } Long _calculateCombinaison(int n, int k) { return _calculateFactorial(n) / (_calculateFactorial(k) * _calculateFactorial(n - k)); } Long _calculateFactorial(int n) { long f = 1; if (n == 0) { return f; } for (int i = 1; i <= n; i++) { f *= i; } 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; } 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; } public static String generate12Digits() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 8; i++) { int digit = (int) (Math.random() * 10); // 0 à 9 sb.append(digit); } return sb.toString(); } @Override public void onDestroyView() { super.onDestroyView(); binding = null; } @Override public void onDestroy() { super.onDestroy(); stompManager.disconnect(); } }