pre prod version for test

This commit is contained in:
OnlyPapy98
2026-05-14 15:04:10 +00:00
parent 4ef19bd4e8
commit 8aa4ad3921
16 changed files with 1360 additions and 251 deletions

View File

@@ -57,6 +57,7 @@ android {
buildFeatures { buildFeatures {
viewBinding = true viewBinding = true
aidl = true
} }
kotlinOptions { kotlinOptions {

View File

@@ -2,6 +2,15 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<queries>
<intent>
<action android:name="com.sagereal.printer.PrinterService" />
</intent>
</queries>
<!-- Permissions système nécessaires -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- ✅ Bluetooth Permissions (Handle Both Legacy and Modern) --> <!-- ✅ Bluetooth Permissions (Handle Both Legacy and Modern) -->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />

View File

@@ -0,0 +1,21 @@
package com.sagereal.printer;
interface PrinterInterface {
void printText(String str);
void printText_isBold(String str, boolean z);
void printText_isUnderline(String str, boolean z);
void printText_size(String str, int i);
void printText_font(String str, int i);
void printText_size_font(String str, int i, int i2);
int printText_FullParm(String str, int i, int i2, int i3, int i4, boolean z, boolean z2);
void printBitmap(String str);
void printEndLine();
void printBitmap_bDate(in byte[] bArr);
void printBitmap_bDate_speed(in byte[] bArr, int i);
int getPrinterStatus();
int getPrinterCacheStatus();
void printBitmap_in(in byte[] bArr);
void printBitmap_inIs(in byte[] bArr, int i);
int getCurrentVoltageStatus();
int getLastError();
}

View File

@@ -26,6 +26,9 @@ import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.util.Log; import android.util.Log;
@@ -49,9 +52,11 @@ import com.example.quiz.data.model.TypeOfBet;
import com.example.quiz.data.model.dtos.NotifPayload; import com.example.quiz.data.model.dtos.NotifPayload;
import com.example.quiz.data.remote.StompManager; import com.example.quiz.data.remote.StompManager;
import com.example.quiz.databinding.FragmentBetValidationBinding; import com.example.quiz.databinding.FragmentBetValidationBinding;
import com.example.quiz.utils.BitMapUtils;
import com.example.quiz.utils.BluetoothUtils; import com.example.quiz.utils.BluetoothUtils;
import com.example.quiz.utils.LoaderDialog; import com.example.quiz.utils.LoaderDialog;
import com.example.quiz.utils.MessageDialog; import com.example.quiz.utils.MessageDialog;
import com.example.quiz.utils.MobiIotPrinterManager;
import com.example.quiz.utils.Result; import com.example.quiz.utils.Result;
import com.example.quiz.utils.SharedPrefsHelper; import com.example.quiz.utils.SharedPrefsHelper;
import com.example.quiz.utils.SunmiPrinterManager; import com.example.quiz.utils.SunmiPrinterManager;
@@ -67,6 +72,11 @@ import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException; import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix; import com.google.zxing.common.BitMatrix;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -77,6 +87,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Observable; import java.util.Observable;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -106,6 +117,9 @@ public class BetValidation extends Fragment {
private AlertDialog dialog; private AlertDialog dialog;
Map<String, Integer> listProduits = Map.of("QUINTE", 5, "QUARTE", 4, "TIERCE", 3, "COUPLE_GAGNANT", 2, "COUPLE_PLACE", 2);
@Inject @Inject
StompManager stompManager; StompManager stompManager;
@@ -122,6 +136,9 @@ public class BetValidation extends Fragment {
String mobileName; String mobileName;
ParisResponse pariNotPrint;
private TypeOfBet typeOfBet; private TypeOfBet typeOfBet;
@@ -166,7 +183,10 @@ public class BetValidation extends Fragment {
if(mobileName.toLowerCase().contains("sunmi")){ if(mobileName.toLowerCase().contains("sunmi")){
sunmiPrinterManager.connectPrinter(status ->{ sunmiPrinterManager.connectPrinter(status ->{
isPrinterReady.setValue(status); isPrinterReady.setValue(status);
sunmiPrinterManager.disableSystemMessages();
}); });
}else{
MobiIotPrinterManager.getInstance().init(requireContext());
} }
} }
@@ -177,6 +197,7 @@ public class BetValidation extends Fragment {
binding = FragmentBetValidationBinding.inflate(inflater, container, false); binding = FragmentBetValidationBinding.inflate(inflater, container, false);
loader = new LoaderDialog(getContext()); loader = new LoaderDialog(getContext());
logsViewModel = new ViewModelProvider(this).get(LogsViewModel.class); logsViewModel = new ViewModelProvider(this).get(LogsViewModel.class);
pariViewModel = new ViewModelProvider(this).get(PariViewModel.class);
binding.coeff.setText(String.valueOf(1)); binding.coeff.setText(String.valueOf(1));
coeff = Integer.parseInt(binding.coeff.getText().toString()); coeff = Integer.parseInt(binding.coeff.getText().toString());
binding.coeff.addTextChangedListener(new TextWatcher() { binding.coeff.addTextChangedListener(new TextWatcher() {
@@ -286,6 +307,60 @@ public class BetValidation extends Fragment {
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
if(prefsHelper!=null && prefsHelper.get("pariNoPrintId") != null && !prefsHelper.get("pariNoPrintId").isEmpty()){
pariViewModel.getPariByNumero(prefsHelper.get("pariNoPrintId")).observe(getViewLifecycleOwner(), pariResult -> {
switch (pariResult.status){
case LOADING: {
loader.show("Recuperation du pari non imprimé");
break;
}
case SUCCESS:{
loader.dismiss();
new android.app.AlertDialog.Builder(getContext())
.setTitle("Réimpréssion du pari "+ pariResult.data.getNumeroTicket())
.setMessage("Veuillez réimprimer le ticket SVP")
.setPositiveButton("OK", (dlg, which) -> {
if(mobileName.toLowerCase().contains("sunmi")){
if(isPrinterReady.getValue() != null && !isPrinterReady.getValue()){
sunmiPrinterManager.connectPrinter(status ->{
isPrinterReady.setValue(status);
});
}
if(sunmiPrinterManager.printerStatus() != 1){
MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
return;
}
}else{
try {
int mobiPrintStatus = MobiIotPrinterManager.getInstance().getPrinterStatus();
if(mobiPrintStatus != 1){
MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
return;
}
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
try {
printPari(pariResult.data);
dlg.dismiss();
prefsHelper.save("pariNoPrintId", null);
} catch (WriterException e) {
throw new RuntimeException(e);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}).setCancelable(false).show();
break;
}
case ERROR:{
MessageDialog.showError(getContext(), "Erreur lors de la récupération!");
break;
}
}
});
}
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
shared = new ViewModelProvider(requireActivity()).get(SharedViewModel.class); shared = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
pariMiseViewModel = new ViewModelProvider(this).get(PariMiseViewModel.class); pariMiseViewModel = new ViewModelProvider(this).get(PariMiseViewModel.class);
@@ -412,6 +487,58 @@ public class BetValidation extends Fragment {
Toast.makeText(getContext(), "Pari non valide", Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(), "Pari non valide", Toast.LENGTH_SHORT).show();
return; return;
} }
if(prefsHelper!=null && prefsHelper.get("pariNoPrintId") != null && !prefsHelper.get("pariNoPrintId").isEmpty()){
pariViewModel.getPariByNumero(prefsHelper.get("pariNoPrintId")).observe(getViewLifecycleOwner(), pariResult -> {
switch (pariResult.status){
case LOADING: {
loader.show("Recuperation du pari non imprimé");
break;
}
case SUCCESS:{
loader.dismiss();
new android.app.AlertDialog.Builder(getContext())
.setTitle("Réimpréssion du pari "+ pariResult.data.getNumeroTicket())
.setMessage("Veuillez réimprimer le ticket SVP")
.setPositiveButton("OK", (dlg, which) -> {
if(mobileName.toLowerCase().contains("sunmi")){
if(isPrinterReady.getValue() != null && !isPrinterReady.getValue()){
sunmiPrinterManager.connectPrinter(status ->{
isPrinterReady.setValue(status);
});
if(sunmiPrinterManager.printerStatus() != 1){
MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
return;
}
}
}else{
try {
int mobiPrinterStatus = MobiIotPrinterManager.getInstance().getPrinterStatus();
if(mobiPrinterStatus != 1){
MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
return;
}
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
try {
printPari(pariResult.data);
dlg.dismiss();
prefsHelper.save("pariNoPrintId", null);
} catch (WriterException | FileNotFoundException e) {
throw new RuntimeException(e);
}
}).setCancelable(false).show();
break;
}
case ERROR:{
MessageDialog.showError(getContext(), "Erreur lors de la récupération du pari!");
break;
}
}
});
return;
}
Pari pari = new Pari( Pari pari = new Pari(
OffsetDateTime.now(ZoneOffset.UTC).toString(), OffsetDateTime.now(ZoneOffset.UTC).toString(),
@@ -501,117 +628,165 @@ public class BetValidation extends Fragment {
} }
} }
public void printPari(ParisResponse pari) throws WriterException { public void printPari(ParisResponse pari) throws WriterException, FileNotFoundException {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pmu_logo); Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pmu_logo);
Bitmap barcode = generateBarcodeBitmap(pari.getNumeroTicket(), 384, 100); Bitmap barcode = BitMapUtils.generateBarcodeBitmap(pari.getNumeroTicket(), 384, 100);
StringBuilder tspl = new StringBuilder(); StringBuilder tspl = new StringBuilder();
tspl.append("Bamako").append("\n"); tspl.append("Bamako").append("\n");
tspl.append(shared.selectedCourse.getValue().getNom()).append("\n"); tspl.append(pari.getCourseNom()).append("\n");
OffsetDateTime dateTime = OffsetDateTime.parse(shared.selectedCourse.getValue().getHeureDepartPrevue()); OffsetDateTime dateTime = OffsetDateTime.parse(pari.getHeureDepartPrevue());
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
String formattedDate = dateTime.format(formatter); String formattedDate = dateTime.format(formatter);
tspl.append(formattedDate).append("\n"); tspl.append(formattedDate).append("\n");
tspl.append("Course ").append(String.valueOf(shared.selectedCourse.getValue().getId())).append("\n"); tspl.append("Course ").append(String.valueOf(pari.getCourseId())).append("\n");
tspl.append(sunmiPrinterManager.separationText()+ "\n"); tspl.append(sunmiPrinterManager.separationText()+ "\n");
boolean isElargie = selectedHorses.getValue().size() > shared.typeOfBet.getValue().getNumberOfHorse(); if(selectedHorses.getValue() == null){
selectedHorses.setValue(Arrays.stream(pari.getCombinaison().split(",")).collect(Collectors.toList()));
}
int requireHorseNumber = shared != null && shared.typeOfBet != null? shared.typeOfBet.getValue().getNumberOfHorse():listProduits.get(pari.getTypesParisMises().get(0));
boolean isElargie = selectedHorses.getValue().size() > requireHorseNumber;
String typePari = pari.getTypesParisMises().get(0).getTypePari(); String typePari = pari.getTypesParisMises().get(0).getTypePari();
tspl.append(isElargie ? typePari + "/Elargie" : typePari).append("\n"); tspl.append(isElargie ? typePari + "/Elargie" : typePari).append("\n");
tspl.append(order ? "COMBINAISON COMPLETE" + "\n" : ""); tspl.append(pari.getFormules().contains("FORMULE_COMPLETE") ?"COMBINAISON COMPLETE"+"\n":"");
String combinationText = formatLineWithNumbers(selectedHorses.getValue().stream().map(String::valueOf).toArray(String[]::new), "-") ; String combinationText = BitMapUtils.formatLineWithNumbers(selectedHorses.getValue().stream().map(String::valueOf).toArray(String[]::new), "-", requireHorseNumber);
tspl.append(combinationText).append("\n"); tspl.append(combinationText).append("\n");
tspl.append("COEF: ").append(String.valueOf(coeff)).append(".0"); tspl.append("COEF: ").append(String.valueOf(pari.getCoefficient()));
tspl.append("\n").append(sunmiPrinterManager.separationText()).append("\n"); tspl.append("\n").append(sunmiPrinterManager.separationText()).append("\n");
tspl.append("MONTANT: ").append(pari.getTypesParisMises().get(0).getMiseTotale()).append(" XOF"); tspl.append("MONTANT: ").append(pari.getTypesParisMises().get(0).getMiseTotale()).append(" XOF");
tspl.append("\n").append(sunmiPrinterManager.separationText()).append("\n"); tspl.append("\n").append(sunmiPrinterManager.separationText()).append("\n");
tspl.append("AGENT: ").append(prefsHelper.get("code")).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"); tspl.append("DATE: ").append(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss").format(OffsetDateTime.parse(pari.getDateHeurePrise()))).append("\n");
if(mobileName.toLowerCase().contains("sunmi")){ if(mobileName.toLowerCase().contains("sunmi")){
String pariText = tspl.toString(); String pariText = tspl.toString();
if(!sunmiPrinterManager.printPari(resizeToPrinterWidth(bitmap, 384), barcode, pari.getNumeroTicket(), pariText)){ sunmiPrinterManager.printPari(BitMapUtils.resizeToPrinterWidth(bitmap, 384), barcode, pari.getNumeroTicket(), pariText, new SunmiPrinterManager.PrintStatusListener() {
MessageDialog.showError(getContext(), "Erreur d'impression"); @Override
prefsHelper.save("noPrintId", String.valueOf(pari.getId())); public void onStatusChanged(boolean status) {
return; if(!status){
}; prefsHelper.save("pariNoPrintId", String.valueOf(pari.getNumeroTicket()));
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
new AlertDialog.Builder(getContext())
.setTitle("Echec de l'impression")
.setMessage("L'impréssion du ticket "+pari.getNumeroTicket()+" n'a pu aboutir, "+"\n"+"Veuillez vérifier le papier puis rééssayez!")
.setPositiveButton("Ok", (dlg, which)->{
if(sunmiPrinterManager.printerStatus() != 1){
MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
return;
}
try {
printPari(pari);
dlg.dismiss();
prefsHelper.save("pariNoPrintId", null);
} catch (WriterException e) {
throw new RuntimeException(e);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
})
.setCancelable(false)
.show();
}
});
}else{
prefsHelper.save("pariNoPrintId", null);
}
}
});
_initializeToZero(); _initializeToZero();
return; return;
} }
try { // MobiIotPrinterManager.getInstance().testImagePrinting(bitmap);
if (BluetoothUtils.needsBluetoothPermissions()) {
if (!BluetoothUtils.hasBluetoothPermission(requireContext())) { MobiIotPrinterManager.getInstance().printPari(BitMapUtils.bitmapToBmp(BitMapUtils.resizeToPrinterWidth(bitmap, 384)), BitMapUtils.bitmapToBmp(BitMapUtils.resizeToPrinterWidth(barcode, 384)), pari.getNumeroTicket(), tspl.toString(), new MobiIotPrinterManager.MobiIotPrinterStatus() {
// Demande la permission si non accordée @Override
BluetoothUtils.requestBluetoothPermission(requireActivity()); public void printStatusCode(int status) {
return; // arrête ici, la popup va apparaître if(status!=1){
prefsHelper.save("pariNoPrintId", String.valueOf(pari.getNumeroTicket()));
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
new AlertDialog.Builder(getContext())
.setTitle("Echec de l'impression")
.setMessage("L'impréssion du ticket "+pari.getNumeroTicket()+" n'a pu aboutir, "+"\n"+"Veuillez vérifier le papier puis rééssayez!")
.setPositiveButton("Ok", (dlg, which)->{
try {
int mobiPrinterStatus = MobiIotPrinterManager.getInstance().getPrinterStatus();
if(mobiPrinterStatus != 1){
MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
return;
}
} catch (RemoteException e) {
throw new RuntimeException(e);
}
try {
printPari(pari);
dlg.dismiss();
prefsHelper.save("pariNoPrintId", null);
} catch (WriterException | FileNotFoundException e) {
throw new RuntimeException(e);
}
})
.setCancelable(false)
.show();
}
});
}else{
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
prefsHelper.save("pariNoPrintId", null);
_initializeToZero();
}
});
} }
} }
});
// 2⃣ Permission OK, on peut afficher la liste // try {
// if (BluetoothUtils.needsBluetoothPermissions()) {
Printama.with(getContext()).printTextBuilder(tspl, bitmap, pari.getNumeroTicket(), barcode, new Printama.PrintCallback() { // if (!BluetoothUtils.hasBluetoothPermission(requireContext())) {
@Override // // Demande la permission si non accordée
public void onResult(boolean success, String errorMessage) { // BluetoothUtils.requestBluetoothPermission(requireActivity());
if (!success) { // return; // arrête ici, la popup va apparaître
new android.app.AlertDialog.Builder(getContext()) // }
.setTitle("Impréssion pari") // }
.setMessage("Voulez-vous rééimprimer ce ticket?") //
.setPositiveButton("Oui", (dialog, which) -> { // // 2⃣ Permission OK, on peut afficher la liste
try { //
printPari(pari); // Printama.with(getContext()).printTextBuilder(tspl, bitmap, pari.getNumeroTicket(), barcode, new Printama.PrintCallback() {
} catch (WriterException e) { // @Override
throw new RuntimeException(e); // public void onResult(boolean success, String errorMessage) {
} // if (!success) {
}) // new android.app.AlertDialog.Builder(getContext())
.setNegativeButton("Non", (dialog, which) -> { // .setTitle("Impréssion pari")
dialog.dismiss(); // .setMessage("Voulez-vous rééimprimer ce ticket?")
}); // .setPositiveButton("Oui", (dialog, which) -> {
} else { // try {
_initializeToZero(); // printPari(pari);
} // } catch (WriterException e) {
} // throw new RuntimeException(e);
}); // }
} catch (SecurityException e) { // })
Toast.makeText(requireContext(), // .setNegativeButton("Non", (dialog, which) -> {
"Permission Bluetooth non accordée", Toast.LENGTH_SHORT).show(); // 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<String> 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<String> firstPart = Arrays.stream(numbers).limit(requiredHorse).map(String::valueOf).collect(Collectors.toList());
List<String> secondPart = Arrays.stream(numbers).skip(requiredHorse).map(String::valueOf).collect(Collectors.toList());
formatted.addAll(firstPart);
formatted.add("R");
formatted.addAll(secondPart);
}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"}) @SuppressLint({"MissingInflatedId", "SetTextI18n"})
@@ -669,6 +844,16 @@ public class BetValidation extends Fragment {
} }
} }
try {
int printerStatus = MobiIotPrinterManager.getInstance().getPrinterStatus();
Log.e("TAG", "### "+String.valueOf(printerStatus));
if(printerStatus != 1){
MessageDialog.showError(getContext(), "Erreur lors de l'impréssion."+"\n"+" Veuillez vérifier le papier puis rééssayer!");
return;
}
} catch (RemoteException e) {
throw new RuntimeException(e);
}
pariViewModel.createPari(pari).observe(getViewLifecycleOwner(), new Observer<Result<ParisResponse>>() { pariViewModel.createPari(pari).observe(getViewLifecycleOwner(), new Observer<Result<ParisResponse>>() {
@Override @Override
public void onChanged(Result<ParisResponse> pariResult) { public void onChanged(Result<ParisResponse> pariResult) {
@@ -692,6 +877,8 @@ public class BetValidation extends Fragment {
loader.dismiss(); loader.dismiss();
MessageDialog.showError(getContext(), e.getMessage()); MessageDialog.showError(getContext(), e.getMessage());
throw new RuntimeException(e); throw new RuntimeException(e);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} }
break; break;
} }
@@ -815,61 +1002,25 @@ public class BetValidation extends Fragment {
} }
private Bitmap resizeToPrinterWidth(Bitmap originalBitmap, int printerWidthPx) { public String getFilePath(Bitmap bitmap, String fileName) {
int originalWidth = originalBitmap.getWidth(); // Utiliser cacheDir au lieu de filesDir (fichiers temporaires)
int originalHeight = originalBitmap.getHeight(); File logo = new File(getContext().getCacheDir(), fileName);
int newHeight = (originalHeight * printerWidthPx) / originalWidth;
// 1. Redimensionner sans filtre (conserve les contours nets) try (FileOutputStream fos = new FileOutputStream(logo)) {
Bitmap scaledBitmap = Bitmap.createScaledBitmap(originalBitmap, // JPEG au lieu de PNG, qualité 85%
printerWidthPx, bitmap.compress(Bitmap.CompressFormat.JPEG, 85, fos);
newHeight, return logo.getAbsolutePath();
false); } catch (IOException e) {
return null;
// 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); // Écrire un mot (16 bits) en little-endian
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
for (int x = 0; x < width; x++) { // Écrire un double mot (32 bits) en little-endian
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() { public static String generate12Digits() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();

View File

@@ -3,8 +3,11 @@ package com.example.quiz;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@@ -12,11 +15,15 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@@ -30,11 +37,14 @@ import com.example.quiz.data.model.Course;
import com.example.quiz.data.model.Pari; import com.example.quiz.data.model.Pari;
import com.example.quiz.data.model.ParisResponse; import com.example.quiz.data.model.ParisResponse;
import com.example.quiz.databinding.FragmentDerniersParisBinding; import com.example.quiz.databinding.FragmentDerniersParisBinding;
import com.example.quiz.utils.BitMapUtils;
import com.example.quiz.utils.BluetoothUtils; import com.example.quiz.utils.BluetoothUtils;
import com.example.quiz.utils.LoaderDialog; import com.example.quiz.utils.LoaderDialog;
import com.example.quiz.utils.MessageDialog; import com.example.quiz.utils.MessageDialog;
import com.example.quiz.utils.MobiIotPrinterManager;
import com.example.quiz.utils.Result; import com.example.quiz.utils.Result;
import com.example.quiz.utils.SharedPrefsHelper; import com.example.quiz.utils.SharedPrefsHelper;
import com.example.quiz.utils.SunmiPrinterManager;
import com.example.quiz.viewModel.LogsViewModel; import com.example.quiz.viewModel.LogsViewModel;
import com.example.quiz.viewModel.PariViewModel; import com.example.quiz.viewModel.PariViewModel;
import com.google.android.material.appbar.MaterialToolbar; import com.google.android.material.appbar.MaterialToolbar;
@@ -75,6 +85,11 @@ public class DerniersParis extends Fragment {
AlertDialog dialog; AlertDialog dialog;
LastBetsAdapter adapter; LastBetsAdapter adapter;
String mobileName;
MutableLiveData<Boolean> isPrinterConnected = new MutableLiveData<>(false);
SunmiPrinterManager sunmiPrinterManager;
Map<String, Integer> listProduits = Map.of("QUINTE", 5, "QUARTE", 4, "TIERCE", 3, "COUPLE_GAGNANT", 2, "COUPLE_PLACE", 2); Map<String, Integer> listProduits = Map.of("QUINTE", 5, "QUARTE", 4, "TIERCE", 3, "COUPLE_GAGNANT", 2, "COUPLE_PLACE", 2);
@@ -94,6 +109,15 @@ public class DerniersParis extends Fragment {
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
prefsHelper = SharedPrefsHelper.getInstance(getContext()); prefsHelper = SharedPrefsHelper.getInstance(getContext());
mobileName = Build.MANUFACTURER;
sunmiPrinterManager = SunmiPrinterManager.getInstance(requireContext());
if(Build.MANUFACTURER.toLowerCase().contains("sunmi")){
sunmiPrinterManager.connectPrinter(status ->{
isPrinterConnected.setValue(status);
});
}else{
MobiIotPrinterManager.getInstance().init(getContext());
}
} }
@@ -224,6 +248,46 @@ public class DerniersParis extends Fragment {
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
view.findViewById(R.id.alert_validate).setOnClickListener(v->{ view.findViewById(R.id.alert_validate).setOnClickListener(v->{
if(mobileName.toLowerCase().contains("sunmi")){
if(isPrinterConnected.getValue() != null && !isPrinterConnected.getValue()){
sunmiPrinterManager.connectPrinter(status ->{
isPrinterConnected.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;
}
}
}else{
try {
int printerStatus = MobiIotPrinterManager.getInstance().getPrinterStatus();
if(printerStatus != 1){
MessageDialog.showError(getContext(), "L'imprimante n'est pas connectée!");
return;
}
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
try { try {
_printPari(pari); _printPari(pari);
dialog.dismiss(); dialog.dismiss();
@@ -240,70 +304,87 @@ public class DerniersParis extends Fragment {
void _printPari(ParisResponse pari) throws WriterException { void _printPari(ParisResponse pari) throws WriterException {
try { Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pmu_logo);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pmu_logo); Bitmap barcode = BitMapUtils.generateBarcodeBitmap(pari.getNumeroTicket(), 384, 100);
Bitmap barcode = generateBarcodeBitmap(pari.getNumeroTicket(), 384, 100); StringBuilder tspl = new StringBuilder();
StringBuilder tspl = new StringBuilder(); tspl.append("Bamako").append("\n");
Printama printama = Printama.with(getContext()); tspl.append(pari.getCourseNom()).append("\n");
tspl.append("Bamako").append("\n"); OffsetDateTime dateTime = OffsetDateTime.parse(pari.getHeureDepartPrevue() != null ? pari.getHeureDepartPrevue() : OffsetDateTime.now().toString());
tspl.append(pari.getCourseNom()).append("\n"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
OffsetDateTime dateTime = OffsetDateTime.parse(pari.getHeureDepartPrevue() != null ? pari.getHeureDepartPrevue() : OffsetDateTime.now().toString()); String formattedDate = dateTime.format(formatter);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"); tspl.append(formattedDate).append("\n");
String formattedDate = dateTime.format(formatter); tspl.append("Course ").append(String.valueOf(pari.getCourseId())).append("\n");
tspl.append(formattedDate).append("\n"); tspl.append(sunmiPrinterManager.separationText()+"\n");
tspl.append("Course ").append(String.valueOf(pari.getCourseId())).append("\n"); List<String> selectedHorses = Arrays.stream(pari.getCombinaison().split(",")).collect(Collectors.toList());
tspl.append(printama.lineSeparator()+"\n"); boolean isElargie = selectedHorses.size()> listProduits.get(pari.getTypesParisMises().get(0).getTypePari());
List<String> selectedHorses = Arrays.stream(pari.getCombinaison().split(",")).collect(Collectors.toList()); String typePari = pari.getTypesParisMises().get(0).getTypePari();
boolean isElargie = selectedHorses.size()> listProduits.get(pari.getTypesParisMises().get(0).getTypePari()); tspl.append(isElargie?typePari+"/Elargie":typePari).append("\n");
String typePari = pari.getTypesParisMises().get(0).getTypePari(); tspl.append(pari.getFormules().contains("FORMULE_COMPLETE") ?"COMBINAISON COMPLETE"+"\n":"");
tspl.append(isElargie?typePari+"/Elargie":typePari).append("\n"); String combinationText = BitMapUtils.formatLineWithNumbers(selectedHorses.stream().map(String::valueOf).toArray(String[]::new), "-", listProduits.get(pari.getTypesParisMises().get(0).getTypePari()));
tspl.append(pari.getFormules().contains("FORMULE_COMPLETE") ?"COMBINAISON COMPLETE"+"\n":""); tspl.append(combinationText).append("\n");
String combinationText = selectedHorses.stream() tspl.append("COEF: ").append(String.valueOf(pari.getCoefficient()));
.map(String::valueOf) tspl.append("\n").append(sunmiPrinterManager.separationText()).append("\n");
.collect(Collectors.joining("-")); tspl.append("MONTANT: ").append(pari.getMiseTotale()).append(" XOF");
tspl.append(combinationText).append("\n"); tspl.append("\n").append(sunmiPrinterManager.separationText()).append("\n");
tspl.append("COEF: ").append(String.valueOf(pari.getCoefficient())); tspl.append("AGENT: ").append(pari.getAgentCode()).append("\n");
tspl.append("\n").append(printama.lineSeparator()).append("\n"); tspl.append("DATE: ").append(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss").format(OffsetDateTime.parse(pari.getDateHeurePrise()))).append("\n");
tspl.append("MONTANT: ").append(pari.getMiseTotale()).append(" XOF"); if(Build.MANUFACTURER.toLowerCase().contains("sunmi")){
tspl.append("\n").append(printama.lineSeparator()).append("\n"); if(Boolean.FALSE.equals(isPrinterConnected.getValue())){
tspl.append("AGENT: ").append(pari.getAgentCode()).append("\n"); MessageDialog.showError(getContext(), "Printer not connected");
tspl.append("DATE: ").append(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss").format(OffsetDateTime.parse(pari.getDateHeurePrise()))).append("\n"); return;
if (BluetoothUtils.needsBluetoothPermissions()) { }
if (!BluetoothUtils.hasBluetoothPermission(requireContext())) { sunmiPrinterManager.printPari(BitMapUtils.resizeToPrinterWidth(bitmap, 384), barcode, pari.getNumeroTicket(), tspl.toString(), new SunmiPrinterManager.PrintStatusListener() {
// Demande la permission si non accordée @Override
BluetoothUtils.requestBluetoothPermission(requireActivity()); public void onStatusChanged(boolean status) {
return; // arrête ici, la popup va apparaître if(!status){
} new Handler(Looper.getMainLooper()).post(new Runnable() {
} @Override
public void run() {
// 2⃣ Permission OK, on peut afficher la liste MessageDialog.showError(getContext(), "Erreur lors de l'impression."+"\n"+"Veuillez rééssayer SVP");
}
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) { return;
Toast.makeText(requireContext(), }
"Permission Bluetooth non accordée", Toast.LENGTH_SHORT).show(); MobiIotPrinterManager.getInstance().printPari(BitMapUtils.bitmapToBmp(BitMapUtils.resizeToPrinterWidth(bitmap, 384)), BitMapUtils.bitmapToBmp(barcode), pari.getNumeroTicket(), tspl.toString(), new MobiIotPrinterManager.MobiIotPrinterStatus() {
} @Override
} public void printStatusCode(int status) {
if(status != 1){
new Handler(Looper.getMainLooper()).post(new Runnable() {
public Bitmap generateBarcodeBitmap(String contents, int width, int height) throws WriterException { @Override
BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, BarcodeFormat.CODE_128, width, height); public void run() {
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); MessageDialog.showError(getContext(), "Erreur lors de l'impression."+"\n"+"Veuillez rééssayer SVP");
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; // 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){
// 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();
// }
} }

View File

@@ -228,9 +228,9 @@ public class PageQuiz extends AppCompatActivity {
@Override @Override
protected void onResume() { protected void onResume() {
if(!mobileName.toLowerCase().contains("sunmi")){ // if(!mobileName.toLowerCase().contains("sunmi")){
checkPermission(); // checkPermission();
} // }
super.onResume(); super.onResume();
handler = new Handler(Looper.getMainLooper()); handler = new Handler(Looper.getMainLooper());
checkRunnable = new Runnable() { checkRunnable = new Runnable() {

View File

@@ -5,6 +5,7 @@ import android.app.DatePickerDialog;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@@ -14,9 +15,13 @@ import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@@ -26,10 +31,13 @@ import android.widget.Toast;
import com.anggastudio.printama.Printama; import com.anggastudio.printama.Printama;
import com.example.quiz.data.model.dtos.paris.SoldeResponse; import com.example.quiz.data.model.dtos.paris.SoldeResponse;
import com.example.quiz.databinding.FragmentSoldBinding; import com.example.quiz.databinding.FragmentSoldBinding;
import com.example.quiz.utils.BitMapUtils;
import com.example.quiz.utils.LoaderDialog; import com.example.quiz.utils.LoaderDialog;
import com.example.quiz.utils.MessageDialog; import com.example.quiz.utils.MessageDialog;
import com.example.quiz.utils.MobiIotPrinterManager;
import com.example.quiz.utils.Result; import com.example.quiz.utils.Result;
import com.example.quiz.utils.SharedPrefsHelper; import com.example.quiz.utils.SharedPrefsHelper;
import com.example.quiz.utils.SunmiPrinterManager;
import com.example.quiz.viewModel.LogsViewModel; import com.example.quiz.viewModel.LogsViewModel;
import com.example.quiz.viewModel.PariViewModel; import com.example.quiz.viewModel.PariViewModel;
import com.google.android.material.appbar.MaterialToolbar; import com.google.android.material.appbar.MaterialToolbar;
@@ -51,6 +59,11 @@ public class Sold extends Fragment {
FragmentSoldBinding binding; FragmentSoldBinding binding;
SunmiPrinterManager sunmiPrinterManager;
MutableLiveData<Boolean> isPrinterReady = new MutableLiveData<>(false);
String mobileName;
LoaderDialog dialog; LoaderDialog dialog;
PariViewModel pariViewModel; PariViewModel pariViewModel;
@@ -73,6 +86,16 @@ public class Sold extends Fragment {
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
prefsHelper = SharedPrefsHelper.getInstance(getContext()); prefsHelper = SharedPrefsHelper.getInstance(getContext());
mobileName = Build.MANUFACTURER;
sunmiPrinterManager = SunmiPrinterManager.getInstance(requireContext());
if(mobileName.toLowerCase().contains("sunmi")){
sunmiPrinterManager.connectPrinter(status ->{
isPrinterReady.setValue(status);
sunmiPrinterManager.disableSystemMessages();
});
}else{
MobiIotPrinterManager.getInstance().init(requireContext());
}
AppCompatActivity activity = (AppCompatActivity) getActivity(); AppCompatActivity activity = (AppCompatActivity) getActivity();
if(activity != null){ if(activity != null){
MaterialToolbar toolbar = activity.findViewById(R.id.toolbar); MaterialToolbar toolbar = activity.findViewById(R.id.toolbar);
@@ -171,7 +194,27 @@ public class Sold extends Fragment {
dialog.dismiss(); dialog.dismiss();
}) })
.setPositiveButton("Imprimer", (dialog, which)->{ .setPositiveButton("Imprimer", (dialog, which)->{
Printama printama = Printama.with(getContext()); if(mobileName.toLowerCase().contains("sunmi")){
if(isPrinterReady.getValue() == null || !isPrinterReady.getValue()){
sunmiPrinterManager.connectPrinter(status ->{
isPrinterReady.setValue(status);
});
}
if(sunmiPrinterManager.printerStatus() != 1){
MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
return;
}
}else{
try {
int status = MobiIotPrinterManager.getInstance().getPrinterStatus();
if(status != 1){
MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
return;
}
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
Bitmap logo = BitmapFactory.decodeResource(getResources(), R.drawable.pmu_logo); Bitmap logo = BitmapFactory.decodeResource(getResources(), R.drawable.pmu_logo);
String title = "SOLDE AU "+date; String title = "SOLDE AU "+date;
StringBuilder text = new StringBuilder(); StringBuilder text = new StringBuilder();
@@ -180,14 +223,43 @@ public class Sold extends Fragment {
text.append("PAIEMENTS: ").append(String.valueOf(soldeResponse.getMontantPaiements())).append(" XOF").append("\n"); text.append("PAIEMENTS: ").append(String.valueOf(soldeResponse.getMontantPaiements())).append(" XOF").append("\n");
text.append("NBR. ANNULATIONS: ").append(soldeResponse.getNombreAnnulations()).append("\n"); text.append("NBR. ANNULATIONS: ").append(soldeResponse.getNombreAnnulations()).append("\n");
text.append("ANNULATIONS: ").append(String.valueOf(soldeResponse.getMontantAnnulations())).append(" XOF").append("\n"); text.append("ANNULATIONS: ").append(String.valueOf(soldeResponse.getMontantAnnulations())).append(" XOF").append("\n");
text.append(printama.lineSeparator()).append("\n"); text.append(sunmiPrinterManager.separationText()).append("\n");
text.append("SOLDE: ").append(String.valueOf(solde)).append(" XOF").append("\n"); text.append("SOLDE: ").append(String.valueOf(solde)).append(" XOF").append("\n");
text.append(printama.lineSeparator()).append("\n"); text.append(sunmiPrinterManager.separationText()).append("\n");
text.append("AGENT : ").append(prefsHelper.get("code")).append("\n"); text.append("AGENT : ").append(prefsHelper.get("code")).append("\n");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
String now = formatter.format(LocalDateTime.now()); String now = formatter.format(LocalDateTime.now());
text.append("DATE : ").append(now).append("\n"); text.append("DATE : ").append(now).append("\n");
printama.printSold(logo, title, text); if(mobileName.toLowerCase().contains("sunmi")){
sunmiPrinterManager.printSold(BitMapUtils.resizeToPrinterWidth(logo, 384), title, text, status -> {
if(!status){
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
MessageDialog.showError(getContext(), "Erreur d'impression"+"\n"+"Veuillez rééssayer SVP.");
}
});
}
});
}else{
try {
MobiIotPrinterManager.getInstance().printSold(BitMapUtils.bitmapToBmp(BitMapUtils.resizeToPrinterWidth(logo, 384)), title, text, new MobiIotPrinterManager.MobiIotPrinterStatus() {
@Override
public void printStatusCode(int status) {
if(status != 1){
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
MessageDialog.showError(getContext(), "Erreur d'impression"+"\n"+"Veuillez rééssayer SVP.");
}
});
}
}
});
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}) })
.show(); .show();
}catch (SecurityException e){ }catch (SecurityException e){

View File

@@ -4,15 +4,20 @@ import android.app.AlertDialog;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -21,10 +26,13 @@ import android.widget.Toast;
import com.anggastudio.printama.Printama; import com.anggastudio.printama.Printama;
import com.example.quiz.data.model.dtos.paris.SoldeResponse; import com.example.quiz.data.model.dtos.paris.SoldeResponse;
import com.example.quiz.databinding.FragmentSoldByCourseBinding; import com.example.quiz.databinding.FragmentSoldByCourseBinding;
import com.example.quiz.utils.BitMapUtils;
import com.example.quiz.utils.LoaderDialog; import com.example.quiz.utils.LoaderDialog;
import com.example.quiz.utils.MessageDialog; import com.example.quiz.utils.MessageDialog;
import com.example.quiz.utils.MobiIotPrinterManager;
import com.example.quiz.utils.Result; import com.example.quiz.utils.Result;
import com.example.quiz.utils.SharedPrefsHelper; import com.example.quiz.utils.SharedPrefsHelper;
import com.example.quiz.utils.SunmiPrinterManager;
import com.example.quiz.viewModel.LogsViewModel; import com.example.quiz.viewModel.LogsViewModel;
import com.example.quiz.viewModel.PariViewModel; import com.example.quiz.viewModel.PariViewModel;
import com.google.zxing.WriterException; import com.google.zxing.WriterException;
@@ -44,6 +52,9 @@ import dagger.hilt.android.AndroidEntryPoint;
public class SoldByCourse extends Fragment { public class SoldByCourse extends Fragment {
FragmentSoldByCourseBinding binding; FragmentSoldByCourseBinding binding;
String mobileName;
SunmiPrinterManager sunmiPrinterManager;
MutableLiveData<Boolean> isPrinterReady = new MutableLiveData<>(false);
LoaderDialog loader; LoaderDialog loader;
@@ -74,6 +85,16 @@ public class SoldByCourse extends Fragment {
// Inflate the layout for this fragment // Inflate the layout for this fragment
binding = FragmentSoldByCourseBinding.inflate(inflater, container, false); binding = FragmentSoldByCourseBinding.inflate(inflater, container, false);
logsViewModel = new ViewModelProvider(this).get(LogsViewModel.class); logsViewModel = new ViewModelProvider(this).get(LogsViewModel.class);
sunmiPrinterManager = SunmiPrinterManager.getInstance(requireContext());
mobileName = Build.MANUFACTURER;
if(mobileName.toLowerCase().contains("sunmi")) {
sunmiPrinterManager.connectPrinter(status -> {
isPrinterReady.setValue(status);
sunmiPrinterManager.disableSystemMessages();
});
}else{
MobiIotPrinterManager.getInstance().init(requireContext());
}
return binding.getRoot(); return binding.getRoot();
} }
@@ -128,7 +149,27 @@ public class SoldByCourse extends Fragment {
dialog.dismiss(); dialog.dismiss();
}) })
.setPositiveButton("Imprimer", (dialog, which)->{ .setPositiveButton("Imprimer", (dialog, which)->{
Printama printama = Printama.with(getContext()); if(mobileName.toLowerCase().contains("sunmi")){
if(isPrinterReady.getValue() == null || !isPrinterReady.getValue()){
sunmiPrinterManager.connectPrinter(status ->{
isPrinterReady.setValue(status);
});
}
if(sunmiPrinterManager.printerStatus() != 1){
MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
return;
}
}else{
try {
int status = MobiIotPrinterManager.getInstance().getPrinterStatus();
if(status != 1){
MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
return;
}
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
Bitmap logo = BitmapFactory.decodeResource(getResources(), R.drawable.pmu_logo); Bitmap logo = BitmapFactory.decodeResource(getResources(), R.drawable.pmu_logo);
String title = "SOLDE DE LA COURSE "+numero; String title = "SOLDE DE LA COURSE "+numero;
StringBuilder text = new StringBuilder(); StringBuilder text = new StringBuilder();
@@ -137,14 +178,43 @@ public class SoldByCourse extends Fragment {
text.append("PAIEMENTS: ").append(String.valueOf(soldeResponse.getMontantPaiements())).append(" XOF").append("\n"); text.append("PAIEMENTS: ").append(String.valueOf(soldeResponse.getMontantPaiements())).append(" XOF").append("\n");
text.append("NBR. ANNULATIONS: ").append(soldeResponse.getNombreAnnulations()).append("\n"); text.append("NBR. ANNULATIONS: ").append(soldeResponse.getNombreAnnulations()).append("\n");
text.append("ANNULATIONS: ").append(String.valueOf(soldeResponse.getMontantAnnulations())).append(" XOF").append("\n"); text.append("ANNULATIONS: ").append(String.valueOf(soldeResponse.getMontantAnnulations())).append(" XOF").append("\n");
text.append(printama.lineSeparator()).append("\n"); text.append(sunmiPrinterManager.separationText()).append("\n");
text.append("SOLDE: ").append(String.valueOf(solde)).append(" XOF").append("\n"); text.append("SOLDE: ").append(String.valueOf(solde)).append(" XOF").append("\n");
text.append(printama.lineSeparator()).append("\n"); text.append(sunmiPrinterManager.separationText()).append("\n");
text.append("AGENT : ").append(prefsHelper.get("code")).append("\n"); text.append("AGENT : ").append(prefsHelper.get("code")).append("\n");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
String now = formatter.format(LocalDateTime.now()); String now = formatter.format(LocalDateTime.now());
text.append("DATE : ").append(now).append("\n"); text.append("DATE : ").append(now).append("\n");
printama.printSold(logo, title, text); if(mobileName.toLowerCase().contains("sunmi")){
sunmiPrinterManager.printSold(BitMapUtils.resizeToPrinterWidth(logo, 384), title, text, status -> {
if(!status){
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
MessageDialog.showError(getContext(), "Une erreur s'est produite lors de l'impréssion."+"\n"+"Veuillez rééssayer S.V.P!");
}
});
}
});
}else{
try {
MobiIotPrinterManager.getInstance().printSold(BitMapUtils.bitmapToBmp(BitMapUtils.resizeToPrinterWidth(logo, 384)), title, text, new MobiIotPrinterManager.MobiIotPrinterStatus() {
@Override
public void printStatusCode(int status) {
if(status != 1){
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
MessageDialog.showError(getContext(), "Une erreur s'est produite lors de l'impréssion."+"\n"+"Veuillez rééssayer S.V.P!");
}
});
}
}
});
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}) })
.show(); .show();
}catch (SecurityException e){ }catch (SecurityException e){

View File

@@ -15,17 +15,14 @@ import com.example.quiz.data.model.dtos.auth.LoginPayload;
import com.example.quiz.data.model.dtos.auth.LoginResponse; import com.example.quiz.data.model.dtos.auth.LoginResponse;
import com.example.quiz.data.model.TpeResponse; import com.example.quiz.data.model.TpeResponse;
import com.example.quiz.data.model.dtos.auth.User; import com.example.quiz.data.model.dtos.auth.User;
import com.example.quiz.data.model.dtos.paris.CancelParisPaylaod;
import com.example.quiz.data.model.dtos.paris.SoldeResponse; import com.example.quiz.data.model.dtos.paris.SoldeResponse;
import java.util.List; import java.util.List;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.Body; import retrofit2.http.Body;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.PATCH; import retrofit2.http.PATCH;
import retrofit2.http.POST; import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Path; import retrofit2.http.Path;
import retrofit2.http.Query; import retrofit2.http.Query;

View File

@@ -0,0 +1,185 @@
package com.example.quiz.utils;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class BitMapUtils {
private static final int MAX_CHAR_2_INCH = 32;
private static void writeDWord(ByteArrayOutputStream baos, int value) {
baos.write(value & 0xFF);
baos.write((value >> 8) & 0xFF);
baos.write((value >> 16) & 0xFF);
baos.write((value >> 24) & 0xFF);
}
private static void writeWord(ByteArrayOutputStream baos, int value) {
baos.write(value & 0xFF);
baos.write((value >> 8) & 0xFF);
}
public static byte[] bitmapToBmp(Bitmap bitmap) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int width = bitmap.getWidth();
int height = bitmap.getHeight();
// Calcul de la taille des données image (lignes alignées sur 32 bits)
int rowSize = ((width * 3 + 3) & ~3); // 3 bytes par pixel (RGB), aligné sur 4 octets
int imageDataSize = rowSize * height;
int fileSize = 54 + imageDataSize; // 54 = taille entête BMP
// Entête BITMAPFILEHEADER (14 octets)
writeWord(baos, 0x4D42); // Signature "BM"
writeDWord(baos, fileSize); // Taille du fichier
writeWord(baos, 0); // Réservé
writeWord(baos, 0); // Réservé
writeDWord(baos, 54); // Offset des données pixel
// Entête BITMAPINFOHEADER (40 octets)
writeDWord(baos, 40); // Taille de l'entête
writeDWord(baos, width); // Largeur
writeDWord(baos, height); // Hauteur
writeWord(baos, 1); // Nombre de plans
writeWord(baos, 24); // Bits par pixel (RGB)
writeDWord(baos, 0); // Compression (0 = BI_RGB)
writeDWord(baos, imageDataSize); // Taille des données image
writeDWord(baos, 0); // Résolution horizontale (peut être 0)
writeDWord(baos, 0); // Résolution verticale
writeDWord(baos, 0); // Couleurs utilisées
writeDWord(baos, 0); // Couleurs importantes
// Extraction des pixels (BMP stocke de bas en haut)
byte[] pixels = new byte[imageDataSize];
for (int y = height - 1; y >= 0; y--) {
int rowStart = (height - 1 - y) * rowSize;
for (int x = 0; x < width; x++) {
int pixel = bitmap.getPixel(x, y);
// Extraire les composantes ARGB
int blue = pixel & 0xFF;
int green = (pixel >> 8) & 0xFF;
int red = (pixel >> 16) & 0xFF;
// Alpha ignoré pour BMP 24 bits
int pos = rowStart + x * 3;
pixels[pos] = (byte) (blue);
pixels[pos + 1] = (byte) (green);
pixels[pos + 2] = (byte) (red);
}
}
try {
baos.write(pixels);
} catch (IOException e) {
e.printStackTrace();
return null;
}
return baos.toByteArray();
}
public static 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 static 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 formatLineWithNumbers(String[] numbers, String separator, int requiredHorse) {
StringBuilder currentLine = new StringBuilder();
StringBuilder finalOutput = new StringBuilder();
List<String> formatted = new ArrayList<>();
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<String> firstPart = Arrays.stream(numbers).limit(requiredHorse).map(String::valueOf).collect(Collectors.toList());
List<String> secondPart = Arrays.stream(numbers).skip(requiredHorse).map(String::valueOf).collect(Collectors.toList());
formatted.addAll(firstPart);
formatted.add("R");
formatted.addAll(secondPart);
}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() > MAX_CHAR_2_INCH) {
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();
}
}

View File

@@ -0,0 +1,242 @@
package com.example.quiz.utils;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.sagereal.printer.PrinterInterface;
import java.io.ByteArrayOutputStream;
public class MobiIotPrinterManager {
private PrinterInterface mPrinter;
private static MobiIotPrinterManager instance;
private boolean isBinding = false;
String TAG = "MOBITAG";
private static final int MAX_CHAR_2_INCH = 32;
private MobiIotPrinterManager() {
}
public static synchronized MobiIotPrinterManager getInstance() {
if (instance == null) {
instance = new MobiIotPrinterManager();
}
return instance;
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mPrinter = PrinterInterface.Stub.asInterface(iBinder);
isBinding = false;
Log.d("PrinterManager", "✅ Service d'impression connecté !");
// Test rapide pour voir si l'imprimante répond
testPrinterStatus();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mPrinter = null;
Log.d("PrinterManager", "❌ Service d'impression déconnecté.");
}
};
private void testPrinterStatus() {
new Thread(() -> {
try {
if (mPrinter != null) {
int status = mPrinter.getPrinterStatus();
Log.d("PrinterManager", "Statut imprimante au démarrage : " + status);
}
} catch (Exception e) {
Log.e("PrinterManager", "Erreur test statut", e);
}
}).start();
}
// Initialisation CORRIGÉE
public void init(Context context) {
if (mPrinter != null) {
Log.d("PrinterManager", "Service déjà connecté");
return;
}
if (isBinding) {
Log.d("PrinterManager", "Connexion déjà en cours");
return;
}
// Utiliser l'action correcte trouvée dans le dump
Intent intent = new Intent("sagereal.intent.action.START_PRINTER_SERVICE_AIDL");
intent.setPackage("com.sagereal.printer");
try {
isBinding = true;
boolean success = context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
if (!success) {
isBinding = false;
Log.e("PrinterManager", "Impossible de binder le service. Vérifiez les permissions.");
} else {
Log.d("PrinterManager", "Tentative de connexion au service...");
}
} catch (SecurityException e) {
isBinding = false;
Log.e("PrinterManager", "Permission refusée. Ajoutez les permissions système.", e);
}
}
public void printText(String text) {
if (mPrinter == null) {
Log.e("PrinterManager", "❌ L'imprimante n'est pas connectée");
return;
}
new Thread(() -> {
try {
// Vérifier le statut avant d'imprimer
int status = mPrinter.getPrinterStatus();
Log.d("PrinterManager", "Statut avant impression : " + status);
if (status == 1) { // 0 = Prêt
mPrinter.printText(text);
mPrinter.printEndLine();
Log.d("PrinterManager", "✅ Impression réussie");
} else {
Log.e("PrinterManager", "Imprimante non prête, statut : " + status);
Log.e("PrinterManager", "Erreur : Impression non effectuée!");
}
} catch (Exception e) {
Log.e("PrinterManager", "Erreur lors de l'impression", e);
}
}).start();
}
public void printPari(byte[] logo, byte[] barcode, String numeroTicket, String pariText, MobiIotPrinterStatus listener) {
if (mPrinter == null) {
Log.e("PrinterManager", "❌ L'imprimante n'est pas connectée");
listener.printStatusCode(0);
return;
}
new Thread(()->{
try {
int status = mPrinter.getPrinterStatus();
if (status != 1) {
listener.printStatusCode(2);
return;
}
mPrinter.printBitmap_in(logo);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 1, false, false);
mPrinter.printBitmap_in(barcode);
mPrinter.printText_FullParm("\n" + numeroTicket, 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm(pariText, 0, 0, 0, 0, false, false);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm("Powered by PMU-MALI", 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 0, false, false);
mPrinter.printText_FullParm("\n\n\n\n\n\n", 0, 10, 0, 0, false, false);
Thread.sleep(3000);
if (getPrinterStatus() != 1) {
listener.printStatusCode(0);
} else {
listener.printStatusCode(1);
}
} catch (RemoteException | InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
}
public void printSold(byte[] logo, String title, StringBuilder text, MobiIotPrinterStatus listener) throws RemoteException {
if (mPrinter == null) {
Log.e("PrinterManager", "❌ L'imprimante n'est pas connectée");
listener.printStatusCode(0);
return;
}
new Thread(()->{
try {
mPrinter.printBitmap_in(logo);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm(title, 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm(text.toString(), 0, 0, 0, 0, false, false);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm("Powered by PMU-MALI", 0, 0, 0, 1, false, false);
mPrinter.printText_FullParm("-".repeat(MAX_CHAR_2_INCH), 0, 0, 0, 0, false, false);
mPrinter.printText_FullParm("\n\n\n\n\n\n", 0, 10, 0, 0, false, false);
Thread.sleep(3000);
if (getPrinterStatus() != 1) {
listener.printStatusCode(0);
} else {
listener.printStatusCode(1);
}
} catch (RemoteException | InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
}
public int getPrinterStatus() throws RemoteException {
return mPrinter.getPrinterStatus();
}
public void printTestText(String text) throws RemoteException {
Log.d(TAG, "#### " + String.valueOf(mPrinter.getPrinterStatus()));
mPrinter.printText_FullParm(text, 0, 0, 0, 0, false, false);
}
private byte[] pathToPrintableBytes(String path) {
Bitmap bitmap = BitmapFactory.decodeFile(path);
if (bitmap == null) return null;
// Redimensionnement à 384px (largeur standard 58mm)
int width = 384;
int height = (int) (bitmap.getHeight() * (384.0 / bitmap.getWidth()));
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
// Conversion en binaire (8 pixels par byte)
int bwWidth = (width + 7) / 8 * 8;
byte[] data = new byte[(bwWidth / 8) * height];
int k = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < bwWidth; x += 8) {
byte value = 0;
for (int bit = 0; bit < 8; bit++) {
if (x + bit < scaledBitmap.getWidth()) {
int pixel = scaledBitmap.getPixel(x + bit, y);
int gray = (int) (Color.red(pixel) * 0.3 + Color.green(pixel) * 0.59 + Color.blue(pixel) * 0.11);
if (gray < 128) value |= (1 << (7 - bit));
}
}
data[k++] = value;
}
}
return data;
}
public void disconnect(Context context) {
if (mPrinter != null) {
context.unbindService(mConnection);
mPrinter = null;
Log.d("PrinterManager", "Service déconnecté");
}
}
public interface MobiIotPrinterStatus {
void printStatusCode(int status);
}
}

View File

@@ -33,6 +33,29 @@ public class SunmiPrinterManager {
return instance; return instance;
} }
public void disableSystemMessages() {
try {
// Commande ESC/POS pour désactiver les messages d'erreur système
// Format standard Sunmi : 1F 11 12 n (n=0 pour désactiver)
byte[] command = new byte[]{0x1F, 0x11, 0x12, 0x00};
sunmiPrinter.sendRAWData(command, new InnerResultCallback() {
@Override
public void onRunResult(boolean isSuccess) {
Log.d(TAG, "Messages système désactivés via RAW : " + isSuccess);
}
@Override
public void onReturnString(String result) {}
@Override
public void onRaiseException(int code, String msg) {}
@Override
public void onPrintResult(int code, String msg) {}
});
} catch (RemoteException e) {
e.printStackTrace();
}
}
// 🔌 Bind UNE SEULE FOIS // 🔌 Bind UNE SEULE FOIS
public void connectPrinter(Consumer<Boolean> status) { public void connectPrinter(Consumer<Boolean> status) {
Log.d("######", "Point d'entrée printer"); Log.d("######", "Point d'entrée printer");
@@ -66,13 +89,7 @@ public class SunmiPrinterManager {
@Override @Override
public void onRunResult(boolean isSuccess) { 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 @Override
@@ -95,6 +112,7 @@ public class SunmiPrinterManager {
}); });
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "Fassery: "+e.getMessage());
if (listener != null) { if (listener != null) {
listener.onError(e.getMessage()); listener.onError(e.getMessage());
} }
@@ -105,12 +123,17 @@ public class SunmiPrinterManager {
return MAX_CHAR_2_INCH; return MAX_CHAR_2_INCH;
} }
public boolean printPari(Bitmap logo, Bitmap barcode, String numeroTicket, String text){ public void printPari(Bitmap logo, Bitmap barcode, String numeroTicket, String text, PrintStatusListener listener){
try{ try{
sunmiPrinter.enterPrinterBuffer(true);
printSimpleImage(logo); printSimpleImage(logo);
printTextSimple("\n"+separationText()+"\n"); printTextSimple("\n" + separationText() + "\n");
printSimpleImage(barcode); printSimpleImage(barcode);
setAlignment(1, new PrinterListener() { setAlignment(1, new PrinterListener() {
@Override
public void onSuccess(boolean isSuccess) {
}
@Override @Override
public void onResult(int code) { public void onResult(int code) {
@@ -123,29 +146,152 @@ public class SunmiPrinterManager {
printTextSimple(numeroTicket); printTextSimple(numeroTicket);
printTextSimple("\n"+separationText()+"\n"); printTextSimple("\n"+separationText()+"\n");
setAlignment(0, new PrinterListener() { setAlignment(0, new PrinterListener() {
@Override
public void onSuccess(boolean isSuccess) {
}
@Override @Override
public void onResult(int code) {} public void onResult(int code) {}
@Override @Override
public void onError(String errorMessage) {} public void onError(String errorMessage) {}
}); });
printTextSimple(text); printTextSimple(text);
printTextSimple("\n"+separationText()+"\n"); printTextSimple("\n" + separationText() + "\n");
setAlignment(1, new PrinterListener() { setAlignment(1, new PrinterListener() {
@Override
public void onSuccess(boolean isSuccess) {
}
@Override @Override
public void onResult(int code) {} public void onResult(int code) {}
@Override @Override
public void onError(String errorMessage) {} public void onError(String errorMessage) {}
}); });
printTextSimple("Powered by PMU-MALI"); printTextSimple("Powered by PMU-MALI");
printTextSimple("\n"+separationText()+"\n"); printTextSimple("\n" + separationText() + "\n");
printTextSimple(" "); printTextSimple(" ");
printTextSimple(" "); printTextSimple(" ");
return true; sunmiPrinter.exitPrinterBufferWithCallback(true, new InnerResultCallback() {
@Override
public void onRunResult(boolean isSuccess) throws RemoteException {
}
@Override
public void onReturnString(String result) throws RemoteException {
Log.e(TAG, "onReturnString: "+result);
}
@Override
public void onRaiseException(int code, String msg) throws RemoteException {
}
@Override
public void onPrintResult(int code, String msg) throws RemoteException {
listener.onStatusChanged(code == 0);
}
});
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
public void printSold(Bitmap logo, String title, StringBuilder text, PrintStatusListener listener){
try{
sunmiPrinter.enterPrinterBuffer(true);
printSimpleImage(logo);
setAlignment(1, new PrinterListener() {
@Override
public void onSuccess(boolean isSuccess) {
}
@Override
public void onResult(int code) {
}
@Override
public void onError(String errorMessage) {
}
});
printTextSimple("\n" + separationText() + "\n");
setAlignment(1, new PrinterListener() {
@Override
public void onSuccess(boolean isSuccess) {
}
@Override
public void onResult(int code) {
}
@Override
public void onError(String errorMessage) {
}
});
printTextSimple(title);
printTextSimple("\n"+separationText()+"\n");
setAlignment(0, new PrinterListener() {
@Override
public void onSuccess(boolean isSuccess) {
}
@Override
public void onResult(int code) {
}
@Override
public void onError(String errorMessage) {
}
});
printTextSimple(text.toString());
printTextSimple("\n" + separationText() + "\n");
setAlignment(1, new PrinterListener() {
@Override
public void onSuccess(boolean isSuccess) {
}
@Override
public void onResult(int code) {}
@Override
public void onError(String errorMessage) {}
});
printTextSimple("Powered by PMU-MALI");
printTextSimple("\n" + separationText() + "\n");
printTextSimple(" ");
printTextSimple(" ");
sunmiPrinter.exitPrinterBufferWithCallback(true, new InnerResultCallback() {
@Override
public void onRunResult(boolean isSuccess) throws RemoteException {
}
@Override
public void onReturnString(String result) throws RemoteException {
Log.e(TAG, "onReturnString: "+result);
}
@Override
public void onRaiseException(int code, String msg) throws RemoteException {
}
@Override
public void onPrintResult(int code, String msg) throws RemoteException {
listener.onStatusChanged(code == 0);
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private boolean readyForPrinting(){
return printerStatus() == 1;
}
public String separationText(){ public String separationText(){
return "-".repeat(MAX_CHAR_2_INCH); return "-".repeat(MAX_CHAR_2_INCH);
} }
@@ -154,13 +300,11 @@ public class SunmiPrinterManager {
public void printSimpleImage(Bitmap bitmap){ public void printSimpleImage(Bitmap bitmap){
printImage(bitmap, new PrinterListener() { printImage(bitmap, new PrinterListener() {
@Override @Override
public void onResult(int code) { public void onSuccess(boolean isSuccess) {}
Log.d(TAG, "Print OK");
}
@Override @Override
public void onError(String errorMessage) { public void onResult(int code) {}
Log.e(TAG, errorMessage); @Override
} public void onError(String errorMessage) {}
}); });
} }
@@ -168,14 +312,12 @@ public class SunmiPrinterManager {
public void printTextSimple(String text) { public void printTextSimple(String text) {
printText(text, new PrinterListener() { printText(text, new PrinterListener() {
@Override @Override
public void onResult(int code) { public void onSuccess(boolean isSuccess) {}
Log.d(TAG, "Print OK"); @Override
} public void onResult(int code) {}
@Override @Override
public void onError(String errorMessage) { public void onError(String errorMessage) {}
Log.e(TAG, errorMessage);
}
}); });
} }
@@ -223,24 +365,13 @@ public class SunmiPrinterManager {
// Le paramètre '0' correspond généralement à l'alignement (0 = gauche) // Le paramètre '0' correspond généralement à l'alignement (0 = gauche)
sunmiPrinter.printBitmap(bitmap, new InnerResultCallback() { sunmiPrinter.printBitmap(bitmap, new InnerResultCallback() {
@Override @Override
public void onRunResult(boolean isSuccess) { 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 @Override
public void onReturnString(String result) { public void onReturnString(String result) {}
// Pas utile
}
@Override @Override
public void onRaiseException(int code, String msg) { public void onRaiseException(int code, String msg) {
Log.e(TAG, "onRaiseException: "+String.valueOf(code));
listener.onResult(code); listener.onResult(code);
listener.onError(msg); listener.onError(msg);
} }
@@ -248,8 +379,8 @@ public class SunmiPrinterManager {
@Override @Override
public void onPrintResult(int code, String msg) { public void onPrintResult(int code, String msg) {
Log.e(TAG, "onPrintResult: "+String.valueOf(code)); Log.e(TAG, "onPrintResult: "+String.valueOf(code));
listener.onResult(code); listener.onResult(code);
listener.onError(msg); listener.onError(msg);
} }
// N'oublie pas les autres callbacks vides (onRunResult, etc.) // N'oublie pas les autres callbacks vides (onRunResult, etc.)
}); });
@@ -268,7 +399,12 @@ public class SunmiPrinterManager {
} }
} }
public interface PrintStatusListener {
void onStatusChanged(boolean status);
}
public interface PrinterListener { public interface PrinterListener {
void onSuccess(boolean isSuccess);
void onResult(int code); void onResult(int code);
void onError(String errorMessage); void onError(String errorMessage);
} }

View File

@@ -0,0 +1,18 @@
package com.sagereal.print.util;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.net.Uri;
/* JADX INFO: loaded from: classes.dex */
public class BitmapUtils {
private static final Uri imageUri = Uri.parse("file:///sdcard/test.bmp");
public static Bitmap zoomImg(Bitmap bitmap, int i, int i2) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Matrix matrix = new Matrix();
matrix.postScale(i / width, i2 / height);
return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
}
}

View File

@@ -0,0 +1,18 @@
package com.sagereal.print.util;
import java.io.IOException;
/* JADX INFO: loaded from: classes.dex */
public class DataFormatConversion {
public static byte[] writeDword(long j) throws IOException {
return new byte[]{(byte) (j & 255), (byte) ((j >> 8) & 255), (byte) ((j >> 16) & 255), (byte) ((j >> 24) & 255)};
}
public static byte[] writeLong(long j) throws IOException {
return new byte[]{(byte) (j & 255), (byte) ((j >> 8) & 255), (byte) ((j >> 16) & 255), (byte) ((j >> 24) & 255)};
}
public static byte[] writeWord(int i) {
return new byte[]{(byte) (i & 255), (byte) ((i >> 8) & 255)};
}
}

View File

@@ -0,0 +1,101 @@
package com.sagereal.print.util;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.util.Log;
import java.lang.ref.WeakReference;
import java.util.Arrays;
/* JADX INFO: loaded from: classes.dex */
public class QrcodeOrBarcode {
private static int height;
private static int width;
public static byte[] getPrintPhotoByteArray(Bitmap bitmap) {
if (bitmap == null) {
return null;
}
try {
int i = (height * 384) / width;
Bitmap bitmapZoomImg = BitmapUtils.zoomImg(bitmap, 384, i);
int width2 = bitmapZoomImg.getWidth();
int height2 = bitmapZoomImg.getHeight();
int i2 = width2 * 3;
int i3 = ((width2 % 4) + i2) * height2;
int i4 = i3 + 54;
byte[] bArr = new byte[i4];
int i5 = 0;
System.arraycopy(DataFormatConversion.writeWord(19778), 0, bArr, 0, 2);
System.arraycopy(DataFormatConversion.writeDword(i4), 0, bArr, 2, 4);
long j = 0;
System.arraycopy(DataFormatConversion.writeDword(j), 0, bArr, 6, 2);
System.arraycopy(DataFormatConversion.writeDword(j), 0, bArr, 8, 2);
System.arraycopy(DataFormatConversion.writeDword(54L), 0, bArr, 10, 4);
Log.d("jiangcunbin", "totalSize14 : " + Arrays.toString(bArr));
Bitmap bitmap2 = bitmapZoomImg;
System.arraycopy(DataFormatConversion.writeDword(40L), 0, bArr, 14, 4);
System.arraycopy(DataFormatConversion.writeLong((long) width2), 0, bArr, 18, 4);
System.arraycopy(DataFormatConversion.writeLong(height2), 0, bArr, 22, 4);
System.arraycopy(DataFormatConversion.writeWord(1), 0, bArr, 26, 2);
System.arraycopy(DataFormatConversion.writeWord(24), 0, bArr, 28, 2);
System.arraycopy(DataFormatConversion.writeDword(0L), 0, bArr, 30, 4);
System.arraycopy(DataFormatConversion.writeDword(((long) i) * 1152), 0, bArr, 34, 4);
System.arraycopy(DataFormatConversion.writeLong(0L), 0, bArr, 38, 4);
System.arraycopy(DataFormatConversion.writeLong(0L), 0, bArr, 42, 4);
System.arraycopy(DataFormatConversion.writeDword(0L), 0, bArr, 46, 4);
System.arraycopy(DataFormatConversion.writeDword(0L), 0, bArr, 50, 4);
Log.d("jiangcunbin", "totalSize54 : " + Arrays.toString(bArr));
byte[] bArr2 = new byte[i3];
int i6 = i2 + (width2 % 4);
int i7 = height2 + (-1);
int i8 = 0;
while (i8 < height2) {
int i9 = i5;
int i10 = i9;
while (i9 < width2) {
Bitmap bitmap3 = bitmap2;
int pixel = bitmap3.getPixel(i9, i8);
int i11 = (i7 * i6) + i10;
bArr2[i11] = (byte) Color.blue(pixel);
bArr2[i11 + 1] = (byte) Color.green(pixel);
bArr2[i11 + 2] = (byte) Color.red(pixel);
i9++;
i10 += 3;
bitmap2 = bitmap3;
}
i8++;
i7--;
i5 = 0;
}
System.arraycopy(bArr2, 0, bArr, 54, i3);
return bArr;
} catch (Exception e) {
Log.d("jiangcunbin", " Exception print QRcode" + e.getMessage());
e.printStackTrace();
return null;
}
}
public static Bitmap convertToBitmap(String str) {
float f;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
BitmapFactory.decodeFile(str, options);
width = options.outWidth;
height = options.outHeight;
int i = (height * 384) / width;
Log.e("jiangcunbin", "photo height is : " + height);
float f2 = 0.0f;
if (width > 384 || height > i) {
f2 = width / 384;
f = height / i;
} else {
f = 0.0f;
}
options.inJustDecodeBounds = false;
options.inSampleSize = (int) Math.max(f2, f);
return Bitmap.createScaledBitmap((Bitmap) new WeakReference(BitmapFactory.decodeFile(str, options)).get(), 384, i, true);
}
}

View File

@@ -1,6 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<queries>
<intent>
<action android:name="com.sagereal.printer.PrinterService" />
</intent>
</queries>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--BLUETOOTH PERMISSION--> <!--BLUETOOTH PERMISSION-->
<!-- Request legacy Bluetooth permissions on older devices. --> <!-- Request legacy Bluetooth permissions on older devices. -->
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH" />