Compare commits

...

3 Commits

Author SHA1 Message Date
OnlyPapy98
4ef19bd4e8 ticket fomattage 2026-04-30 13:08:05 +02:00
OnlyPapy98
c0e5072523 total review 2026-04-03 19:30:06 +02:00
OnlyPapy98
4eaca7e1d8 Version avec integration slave master 2026-04-01 19:51:19 +02:00
96 changed files with 5687 additions and 586 deletions

View File

@@ -4,10 +4,10 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-02-13T13:45:19.610071900Z"> <DropdownSelection timestamp="2026-04-03T13:27:33.582048600Z">
<Target type="DEFAULT_BOOT"> <Target type="DEFAULT_BOOT">
<handle> <handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=5051918437" /> <DeviceId pluginId="PhysicalDevice" identifier="serial=5051917024" />
</handle> </handle>
</Target> </Target>
</DropdownSelection> </DropdownSelection>

View File

@@ -1,3 +1,5 @@
import java.util.Properties
plugins { plugins {
alias(libs.plugins.android.application) alias(libs.plugins.android.application)
alias(libs.plugins.google.android.libraries.mapsplatform.secrets.gradle.plugin) alias(libs.plugins.google.android.libraries.mapsplatform.secrets.gradle.plugin)
@@ -5,6 +7,13 @@ plugins {
alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.android)
} }
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("local.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(keystorePropertiesFile.inputStream())
}
android { android {
namespace = "com.example.quiz" namespace = "com.example.quiz"
compileSdk = 34 compileSdk = 34
@@ -19,22 +28,37 @@ android {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
signingConfigs {
create("release") {
storeFile = file(keystoreProperties["STORE_FILE"] as String)
storePassword = keystoreProperties["STORE_PASSWORD"] as String
keyAlias = keystoreProperties["KEY_ALIAS"] as String
keyPassword = keystoreProperties["KEY_PASSWORD"] as String
}
}
buildTypes { buildTypes {
release { release {
isMinifyEnabled = false isMinifyEnabled = false
signingConfig = signingConfigs.getByName("release")
proguardFiles( proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro" "proguard-rules.pro"
) )
} }
} }
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11
} }
buildFeatures { buildFeatures {
viewBinding = true viewBinding = true
} }
kotlinOptions { kotlinOptions {
jvmTarget = "17" jvmTarget = "17"
} }
@@ -42,35 +66,52 @@ android {
dependencies { dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar")))) implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
implementation(libs.room.runtime) implementation(libs.room.runtime)
annotationProcessor(libs.room.compiler) annotationProcessor(libs.room.compiler)
implementation("com.github.NaikSoftware:StompProtocolAndroid:1.6.6")
implementation("io.reactivex.rxjava2:rxjava:2.2.21")
implementation("io.reactivex.rxjava2:rxandroid:2.1.1")
implementation(libs.retrofit)
implementation(libs.okhttp)
implementation(libs.logging.interceptor)
implementation(libs.converter.gson)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.hilt.android)
annotationProcessor(libs.hilt.compiler)
implementation(libs.recyclerview.v7)
implementation(libs.appcompat)
implementation(libs.material)
implementation(libs.constraintlayout)
implementation(libs.printerlibrary)
implementation(libs.navigation.fragment)
implementation(libs.navigation.ui)
implementation(libs.play.services.maps)
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.camera:camera-camera2:1.2.3") implementation("androidx.camera:camera-camera2:1.2.3")
implementation("androidx.camera:camera-lifecycle:1.2.3") implementation("androidx.camera:camera-lifecycle:1.2.3")
implementation("androidx.camera:camera-view:1.2.3") implementation("androidx.camera:camera-view:1.2.3")
implementation("androidx.camera:camera-core:1.2.3") implementation("androidx.camera:camera-core:1.2.3")
implementation("com.google.mlkit:barcode-scanning:17.2.0") implementation("com.google.mlkit:barcode-scanning:17.2.0")
implementation(libs.retrofit)
implementation(libs.okhttp)
implementation(libs.logging.interceptor)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.converter.gson)
implementation(libs.hilt.android)
annotationProcessor(libs.hilt.compiler)
implementation(libs.rxjava)
implementation(libs.rxandroid)
implementation(libs.recyclerview.v7)
implementation(libs.appcompat)
implementation(libs.material)
implementation(libs.google.core)
implementation(libs.zxing.android.embedded) implementation(libs.zxing.android.embedded)
implementation(libs.constraintlayout)
implementation(libs.navigation.fragment)
implementation(libs.navigation.ui)
implementation(libs.play.services.maps)
implementation(project(":printama")) implementation(project(":printama"))
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit) androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core) androidTestImplementation(libs.espresso.core)
implementation("androidx.annotation:annotation:1.7.1")
} }

View File

@@ -14,6 +14,13 @@
android:usesPermissionFlags="neverForLocation" /> android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="com.sunmi.peripheral.printer.permission.PRINTER" />
<!-- Pour certains modèles -->
<uses-permission android:name="android.permission.BIND_PRINTER_SERVICE" />
<!-- ✅ Location Permissions --> <!-- ✅ Location Permissions -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
@@ -38,6 +45,10 @@
<uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" /> <uses-feature android:name="android.hardware.camera.autofocus" />
<queries>
<package android:name="woyou.aidlservice.jiuiv5" />
</queries>
<application <application
android:name=".PmuHorseBetting" android:name=".PmuHorseBetting"
android:allowBackup="true" android:allowBackup="true"
@@ -50,10 +61,6 @@
android:theme="@style/Theme.Quiz" android:theme="@style/Theme.Quiz"
tools:targetApi="31" tools:targetApi="31"
android:usesCleartextTraffic="true"> android:usesCleartextTraffic="true">
<activity
android:name=".PageQuiz"
android:exported="true"
android:theme="@style/Theme.Quiz" />
<!-- <!--
TODO: Before you run your application, you need a Google Maps API key. TODO: Before you run your application, you need a Google Maps API key.
@@ -71,7 +78,7 @@
<activity <activity
android:name=".MainActivity" android:name=".PageQuiz"
android:exported="true" android:exported="true"
android:theme="@style/Theme.Quiz"> android:theme="@style/Theme.Quiz">
<intent-filter> <intent-filter>

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
package com.example.quiz; package com.example.quiz;
import android.app.AlertDialog;
import android.os.Bundle; import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
@@ -88,10 +89,49 @@ public class AnnulationTicket extends Fragment {
binding.annuleTicketBtn.setOnClickListener(v->{ binding.annuleTicketBtn.setOnClickListener(v->{
String reference = binding.referenceTicket.getText().toString(); String reference = binding.referenceTicket.getText().toString();
if(reference.isEmpty()){ if(reference.isEmpty()){
Toast.makeText(getContext(),"Entrez la référence du ticket", Toast.LENGTH_SHORT).show(); MessageDialog.showError(getContext(), "Veuillez donner la reference du ticket");
return; return;
} }
viewModel.annulerPari(reference).observe(getViewLifecycleOwner(), new Observer<Result<ParisResponse>>() { viewModel.getPariByNumero(reference).observe(getViewLifecycleOwner(), new Observer<Result<ParisResponse>>() {
@Override
public void onChanged(Result<ParisResponse> parisResponseResult) {
switch (parisResponseResult.status){
case LOADING:{
dialog.show("Recherche du ticket");
break;
}
case ERROR:{
MessageDialog.showError(getContext(), parisResponseResult.message);
break;
}
case SUCCESS:{
if(parisResponseResult.data.getNumeroTicket().equals(reference)){
dialog.dismiss();
if(parisResponseResult.data.getStatutPari() == ParisResponse.StatutPari.ANNULE){
MessageDialog.showError(getContext(), "Le ticket est déjà annulé");
return;
}
_showPariDialog(reference);
}else{
MessageDialog.showError(getContext(), "Le ticket n'existe pas");
}
}
}
}
});
});
}
void _showPariDialog(String ticketReference){
new AlertDialog.Builder(getContext())
.setTitle("Annulation du ticket")
.setMessage("Etes-vous sûr de vouloir annuler le ticket "+ticketReference+" ?")
.setCancelable(true)
.setNegativeButton("Annuler", (dialog, which) -> {
dialog.dismiss();
})
.setPositiveButton("Confirmer", (annulationDialog, which) -> {
viewModel.annulerPari(ticketReference).observe(getViewLifecycleOwner(), new Observer<Result<ParisResponse>>() {
@Override @Override
public void onChanged(Result<ParisResponse> pariResult) { public void onChanged(Result<ParisResponse> pariResult) {
switch (pariResult.status){ switch (pariResult.status){
@@ -113,7 +153,7 @@ public class AnnulationTicket extends Fragment {
} }
} }
}); });
}); }).show();
} }
@Override @Override

View File

@@ -1,13 +1,19 @@
package com.example.quiz; package com.example.quiz;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter; import android.app.Dialog;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
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.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
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.activity.result.ActivityResultLauncher; import androidx.activity.result.ActivityResultLauncher;
@@ -15,10 +21,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; 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.core.app.ActivityCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
@@ -37,38 +40,49 @@ import android.widget.Toast;
import com.anggastudio.printama.Printama; import com.anggastudio.printama.Printama;
import com.example.quiz.data.model.Course; 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.Pari;
import com.example.quiz.data.model.PariMise; import com.example.quiz.data.model.PariMise;
import com.example.quiz.data.model.ParisResponse; import com.example.quiz.data.model.ParisResponse;
import com.example.quiz.data.model.Reunion;
import com.example.quiz.data.model.TypeOfBet; import com.example.quiz.data.model.TypeOfBet;
import com.example.quiz.data.model.dtos.PariCourseDto; import com.example.quiz.data.model.dtos.NotifPayload;
import com.example.quiz.data.model.enums.PariStatut; import com.example.quiz.data.remote.StompManager;
import com.example.quiz.databinding.FragmentBetValidationBinding; import com.example.quiz.databinding.FragmentBetValidationBinding;
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.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.PariMiseViewModel;
import com.example.quiz.viewModel.PariViewModel; import com.example.quiz.viewModel.PariViewModel;
import com.example.quiz.viewModel.SharedViewModel; import com.example.quiz.viewModel.SharedViewModel;
import com.google.android.material.appbar.MaterialToolbar; 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.BarcodeFormat;
import com.google.zxing.MultiFormatWriter; 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.time.LocalDate; import java.lang.reflect.Type;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
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.stream.Collectors; import java.util.stream.Collectors;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
/** /**
@@ -82,10 +96,21 @@ public class BetValidation extends Fragment {
FragmentBetValidationBinding binding; FragmentBetValidationBinding binding;
SharedViewModel shared; SharedViewModel shared;
PariMiseViewModel pariMiseViewModel;
List<MiseInitiale> misesInitiales;
private AlertDialog dialog; private AlertDialog dialog;
@Inject
StompManager stompManager;
SunmiPrinterManager sunmiPrinterManager;
private int nombreX; private int nombreX;
LogsViewModel logsViewModel; LogsViewModel logsViewModel;
@@ -95,16 +120,20 @@ public class BetValidation extends Fragment {
private long mise; private long mise;
String mobileName;
private TypeOfBet typeOfBet; private TypeOfBet typeOfBet;
MutableLiveData<Boolean> isPrinterReady = new MutableLiveData<>(false);
SharedPrefsHelper prefsHelper; SharedPrefsHelper prefsHelper;
private Course course; private Course course;
LoaderDialog loader;
LoaderDialog loader;
private int coeff; private int coeff;
@@ -117,7 +146,6 @@ public class BetValidation extends Fragment {
PariViewModel pariViewModel; PariViewModel pariViewModel;
public BetValidation() { public BetValidation() {
// Required empty public constructor // Required empty public constructor
} }
@@ -133,7 +161,15 @@ public class BetValidation 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);
});
} }
}
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
@@ -185,7 +221,6 @@ public class BetValidation extends Fragment {
createNumberItem("X"); createNumberItem("X");
grid.addView(createNumberItem("X")); grid.addView(createNumberItem("X"));
} }
private TextView createNumberItem(String horse) { private TextView createNumberItem(String horse) {
TextView textView = new TextView(requireContext()); TextView textView = new TextView(requireContext());
textView.setText(horse); textView.setText(horse);
@@ -253,6 +288,41 @@ public class BetValidation extends Fragment {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
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.getBetInitMise().observe(
getViewLifecycleOwner(),
result -> {
switch (result.status) {
case LOADING: {
loader.show("Chargement des mise");
break;
}
case SUCCESS: {
misesInitiales = result.data;
loader.dismiss();
break;
}
case ERROR: {
loader.dismiss();
MessageDialog.showError(getContext(), result.message);
}
default:
break;
}
}
);
if (shared.selectedCourse.getValue() != null) {
stompManager.subscribe("courses/" + shared.selectedCourse.getValue().getId(), json -> {
Type type = new TypeToken<NotifPayload<Course>>() {
}.getType();
NotifPayload<Course> notif = new Gson().fromJson(json, type);
Course updatedCourse = notif.getPayload();
if (shared.selectedCourse.getValue().getId() == updatedCourse.getId()) {
shared.setSelectedCourse(updatedCourse);
setupNumberGrid(binding.gridNumbers);
}
});
}
setSelectedTypeOfBetImage(); setSelectedTypeOfBetImage();
pariViewModel = new ViewModelProvider(this).get(PariViewModel.class); pariViewModel = new ViewModelProvider(this).get(PariViewModel.class);
typeOfBet = shared.typeOfBet.getValue(); typeOfBet = shared.typeOfBet.getValue();
@@ -266,7 +336,6 @@ public class BetValidation extends Fragment {
activity.getSupportActionBar().setTitle("Pari " + shared.selectedCourse.getValue().getNom()); activity.getSupportActionBar().setTitle("Pari " + shared.selectedCourse.getValue().getNom());
} }
} }
setupNumberGrid(binding.gridNumbers); setupNumberGrid(binding.gridNumbers);
binding.paymentType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { binding.paymentType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override @Override
@@ -324,7 +393,7 @@ public class BetValidation extends Fragment {
binding.betValidateBtn.setOnClickListener(v -> { binding.betValidateBtn.setOnClickListener(v -> {
if (binding.paymentType.getSelectedItem().toString().equals("Orange Money") && binding.phoneNumber.getText().toString().isEmpty()) { if (binding.paymentType.getSelectedItem().toString().equals("Orange Money") && binding.phoneNumber.getText().toString().isEmpty()) {
Toast.makeText(getContext(), "Veuillez entrer un numéro de téléphone", Toast.LENGTH_SHORT).show(); MessageDialog.showError(getContext(), "Veuillez saisir le numéro de téléphone");
return; return;
} }
@@ -334,7 +403,7 @@ public class BetValidation extends Fragment {
int required = typeOfBet.getNumberOfHorse(); int required = typeOfBet.getNumberOfHorse();
if(selectedHorses.getValue().size() < required){ if (Objects.requireNonNull(selectedHorses.getValue()).size() < required) {
Toast.makeText(getContext(), "Veuillez sélectionner au moins" + required + " chevaux", Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(), "Veuillez sélectionner au moins" + required + " chevaux", Toast.LENGTH_SHORT).show();
return; return;
} }
@@ -357,6 +426,9 @@ public class BetValidation extends Fragment {
"XOF", "XOF",
"Pari" "Pari"
); );
if (dialog != null && dialog.isShowing()) {
return;
}
_showPariDialog(pari); _showPariDialog(pari);
}); });
@@ -370,7 +442,6 @@ public class BetValidation extends Fragment {
} }
String _getCombinaison() { String _getCombinaison() {
if (selectedHorses.getValue().contains("X") && shared.typeOfBet.getValue().getNumberOfHorse() < selectedHorses.getValue().size()) { if (selectedHorses.getValue().contains("X") && shared.typeOfBet.getValue().getNumberOfHorse() < selectedHorses.getValue().size()) {
String first = selectedHorses.getValue().subList(0, shared.typeOfBet.getValue().getNumberOfHorse()).stream() String first = selectedHorses.getValue().subList(0, shared.typeOfBet.getValue().getNumberOfHorse()).stream()
@@ -431,12 +502,9 @@ public class BetValidation extends Fragment {
} }
public void printPari(ParisResponse pari) throws WriterException { public 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 = generateBarcodeBitmap(pari.getNumeroTicket(), 384, 100); Bitmap barcode = generateBarcodeBitmap(pari.getNumeroTicket(), 384, 100);
StringBuilder tspl = new StringBuilder(); StringBuilder tspl = new StringBuilder();
Printama printama = Printama.with(getContext());
tspl.append("Bamako").append("\n"); tspl.append("Bamako").append("\n");
tspl.append(shared.selectedCourse.getValue().getNom()).append("\n"); tspl.append(shared.selectedCourse.getValue().getNom()).append("\n");
OffsetDateTime dateTime = OffsetDateTime.parse(shared.selectedCourse.getValue().getHeureDepartPrevue()); OffsetDateTime dateTime = OffsetDateTime.parse(shared.selectedCourse.getValue().getHeureDepartPrevue());
@@ -444,22 +512,31 @@ public class BetValidation extends Fragment {
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(shared.selectedCourse.getValue().getId())).append("\n");
tspl.append(shared.selectedCourse.getValue().getTypesParisOuverts().get(0)).append("\n"); tspl.append(sunmiPrinterManager.separationText()+ "\n");
tspl.append(printama.lineSeparator()+"\n");
boolean isElargie = selectedHorses.getValue().size() > shared.typeOfBet.getValue().getNumberOfHorse(); boolean isElargie = selectedHorses.getValue().size() > shared.typeOfBet.getValue().getNumberOfHorse();
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(order ? "COMBINAISON COMPLETE" + "\n" : "");
String combinationText = selectedHorses.getValue().stream() String combinationText = formatLineWithNumbers(selectedHorses.getValue().stream().map(String::valueOf).toArray(String[]::new), "-") ;
.map(String::valueOf)
.collect(Collectors.joining("-"));
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(coeff)).append(".0");
tspl.append("\n").append(printama.lineSeparator()).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(printama.lineSeparator()).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(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.needsBluetoothPermissions()) {
if (!BluetoothUtils.hasBluetoothPermission(requireContext())) { if (!BluetoothUtils.hasBluetoothPermission(requireContext())) {
// Demande la permission si non accordée // Demande la permission si non accordée
@@ -470,8 +547,28 @@ public class BetValidation extends Fragment {
// 2⃣ Permission OK, on peut afficher la liste // 2⃣ Permission OK, on peut afficher la liste
Printama.with(getContext()).printTextBuilder(tspl, bitmap, pari.getNumeroTicket(), barcode); 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(); _initializeToZero();
}
}
});
} catch (SecurityException e) { } catch (SecurityException e) {
Toast.makeText(requireContext(), Toast.makeText(requireContext(),
"Permission Bluetooth non accordée", Toast.LENGTH_SHORT).show(); "Permission Bluetooth non accordée", Toast.LENGTH_SHORT).show();
@@ -479,6 +576,44 @@ public class BetValidation extends Fragment {
} }
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"})
void _showPariDialog(Pari pari) { void _showPariDialog(Pari pari) {
LayoutInflater inflater = getLayoutInflater(); LayoutInflater inflater = getLayoutInflater();
@@ -500,11 +635,40 @@ public class BetValidation extends Fragment {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setView(view); builder.setView(view);
builder.setCancelable(false); builder.setCancelable(false);
dialog = builder.create(); dialog = builder.create();
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(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<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) {
@@ -520,8 +684,8 @@ public class BetValidation extends Fragment {
case SUCCESS: case SUCCESS:
try { try {
loader.dismiss(); 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); printPari(pariResult.data);
logsViewModel.insertLog(prefsHelper.get("id"), "BET", "Création du pari "+pariResult.data.getNumeroTicket()+" type de paris: "+pariResult.data.getTypesParisMises().get(0).getTypePari(), System.currentTimeMillis());
dialog.dismiss(); dialog.dismiss();
MessageDialog.showSuccess(getContext(), "Pari créé avec succès"); MessageDialog.showSuccess(getContext(), "Pari créé avec succès");
} catch (WriterException e) { } catch (WriterException e) {
@@ -564,19 +728,44 @@ public class BetValidation extends Fragment {
} }
int typeOfBetHorses = shared.typeOfBet.getValue().getNumberOfHorse(); int typeOfBetHorses = shared.typeOfBet.getValue().getNumberOfHorse();
int partants = shared.selectedCourse.getValue().getNombrePartants(); int partants = shared.selectedCourse.getValue().getNombrePartants();
Course.TypeParis courseName = shared.typeOfBet.getValue().getName();
if (shared.typeOfBet.getValue().getNumberOfHorse() > nombreChevauxSelectionnes) { if (shared.typeOfBet.getValue().getNumberOfHorse() > nombreChevauxSelectionnes) {
binding.mise.setText(mise + " CFA"); binding.mise.setText(mise + " CFA");
return; return;
} }
if(typeOfBetHorses == 2){ MiseInitiale miseModel = misesInitiales.stream()
mise = 500; .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 { } else {
if(typeOfBetHorses == 5){ // Pour les autres types: coefficient <= 20
mise = 300; if (coeff > 20) {
}else{ // Erreur: coefficient trop élevé
mise = 200; MessageDialog.showError(getContext(), "Le coefficient doit être inférieur ou égal à 20 pour " + typePari.toString());
_initializeToZero();
return;
} }
} }
mise = mise * coeff; mise = mise * coeff;
if (nombreX > 0) { if (nombreX > 0) {
if (nombreChevauxSelectionnes == typeOfBetHorses) { if (nombreChevauxSelectionnes == typeOfBetHorses) {
@@ -626,6 +815,51 @@ public class BetValidation extends Fragment {
} }
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 { public Bitmap generateBarcodeBitmap(String contents, int width, int height) throws WriterException {
BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, BarcodeFormat.CODE_128, width, height); BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, BarcodeFormat.CODE_128, width, height);
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
@@ -636,6 +870,7 @@ public class BetValidation extends Fragment {
} }
return bmp; return bmp;
} }
public static String generate12Digits() { public static String generate12Digits() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
@@ -651,4 +886,10 @@ public class BetValidation extends Fragment {
super.onDestroyView(); super.onDestroyView();
binding = null; binding = null;
} }
@Override
public void onDestroy() {
super.onDestroy();
stompManager.disconnect();
}
} }

View File

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

View File

@@ -1,9 +1,15 @@
package com.example.quiz; package com.example.quiz;
import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
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.Observer; import androidx.lifecycle.Observer;
@@ -11,15 +17,20 @@ 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.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.anggastudio.printama.Printama;
import com.example.quiz.data.adapter.LastBetsAdapter; import com.example.quiz.data.adapter.LastBetsAdapter;
import com.example.quiz.data.model.Course;
import com.example.quiz.data.model.Pari; import com.example.quiz.data.model.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.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.Result; import com.example.quiz.utils.Result;
@@ -27,8 +38,18 @@ import com.example.quiz.utils.SharedPrefsHelper;
import com.example.quiz.viewModel.LogsViewModel; import com.example.quiz.viewModel.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;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
@@ -45,13 +66,18 @@ public class DerniersParis extends Fragment {
LogsViewModel logsViewModel; LogsViewModel logsViewModel;
LoaderDialog loader; LoaderDialog loader;
SharedPrefsHelper prefsHelper; SharedPrefsHelper prefsHelper;
PariViewModel viewModel; PariViewModel viewModel;
AlertDialog dialog;
LastBetsAdapter adapter; LastBetsAdapter adapter;
Map<String, Integer> listProduits = Map.of("QUINTE", 5, "QUARTE", 4, "TIERCE", 3, "COUPLE_GAGNANT", 2, "COUPLE_PLACE", 2);
public DerniersParis() { public DerniersParis() {
// Required empty public constructor // Required empty public constructor
} }
@@ -70,6 +96,7 @@ public class DerniersParis extends Fragment {
prefsHelper = SharedPrefsHelper.getInstance(getContext()); prefsHelper = SharedPrefsHelper.getInstance(getContext());
} }
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@@ -84,6 +111,10 @@ public class DerniersParis extends Fragment {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
binding.lastBetsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); binding.lastBetsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
viewModel = new ViewModelProvider(this).get(PariViewModel.class); viewModel = new ViewModelProvider(this).get(PariViewModel.class);
_getLastBets();
}
void _getLastBets(){
viewModel.getDerniersParis(prefsHelper.get("id")).observe(getViewLifecycleOwner(), new Observer<Result<List<ParisResponse>>>() { viewModel.getDerniersParis(prefsHelper.get("id")).observe(getViewLifecycleOwner(), new Observer<Result<List<ParisResponse>>>() {
@Override @Override
public void onChanged(Result<List<ParisResponse>> listResult) { public void onChanged(Result<List<ParisResponse>> listResult) {
@@ -97,9 +128,31 @@ public class DerniersParis extends Fragment {
break; break;
case SUCCESS: case SUCCESS:
loader.dismiss(); loader.dismiss();
adapter = new LastBetsAdapter(listResult.data, pari -> { if(adapter == null){
}); adapter = new LastBetsAdapter(listResult.data);
}else{
adapter.setData(listResult.data);
}
logsViewModel.insertLog(prefsHelper.get("id"), "LAST BETS", "Affichage derniers paris", System.currentTimeMillis()); logsViewModel.insertLog(prefsHelper.get("id"), "LAST BETS", "Affichage derniers paris", System.currentTimeMillis());
adapter.setPariClickListener(new LastBetsAdapter.onPariClickListener() {
@Override
public void onItemClick(ParisResponse pari) {
if(pari.getStatutPari() != null && pari.getStatutPari() != ParisResponse.StatutPari.ENREGISTRE){
MessageDialog.showError(getContext(), "Ce pari ne peut pas être réimprimé!");
return;
}
_showPariDialog(pari);
}
@Override
public void onItemCancel(ParisResponse pari) {
if(pari.getStatutPari() != null && pari.getStatutPari() != ParisResponse.StatutPari.ENREGISTRE){
MessageDialog.showError(getContext(), "Ce pari ne peut pas être annulé");
return;
}
_cancelConfirmationDialog(pari);
}
});
binding.lastBetsRecyclerView.setAdapter(adapter); binding.lastBetsRecyclerView.setAdapter(adapter);
break; break;
} }
@@ -107,6 +160,154 @@ public class DerniersParis extends Fragment {
}); });
} }
void _cancelConfirmationDialog(ParisResponse pari){
new AlertDialog.Builder(getContext())
.setTitle("Annulation du pari")
.setMessage("Êtes-vous sûr de vouloir annuler le pari "+pari.getNumeroTicket()+" ?")
.setPositiveButton("Oui", (cancelDialog, which) -> {
_cancelPari(pari);
cancelDialog.dismiss();
})
.setNegativeButton("Non", (cancelDialog, which) -> {
cancelDialog.dismiss();
}).show();
}
void _cancelPari(ParisResponse pari){
viewModel.annulerPari(pari.getNumeroTicket()).observe(getViewLifecycleOwner(), new Observer<Result<ParisResponse>>() {
@Override
public void onChanged(Result<ParisResponse> result) {
switch (result.status){
case LOADING:
loader.show("Annulation du pari");
break;
case ERROR:
loader.dismiss();
MessageDialog.showError(getContext(), result.message);
break;
case SUCCESS:
loader.dismiss();
MessageDialog.showSuccess(getContext(), "Pari annulé avec succès");
_getLastBets();
break;
}
}
});
}
@SuppressLint({"MissingInflatedId", "SetTextI18n"})
void _showPariDialog(ParisResponse pari){
LayoutInflater inflater = getLayoutInflater();
View view = inflater.inflate(R.layout.pari_confirmation, null);
TextView numero_course = (TextView) view.findViewById(R.id.alert_course_numero);
numero_course.setText("Numero Course: "+String.valueOf(pari.getCourseId()));
TextView alert_pari_type = (TextView) view.findViewById(R.id.alert_pari_type);
alert_pari_type.setText(String.valueOf(pari.getTypesParisMises().get(0).getTypePari()));
TextView is_elargie = (TextView) view.findViewById(R.id.alert_is_elargie);
List<String> selectedHorses = Arrays.stream(pari.getCombinaison().split(","))
.map(String::trim)
.collect(Collectors.toList());
is_elargie.setText(selectedHorses.size() > listProduits.get(pari.getTypesParisMises().get(0).getTypePari())?"CE":"SI");
TextView alert_combinaison = (TextView) view.findViewById(R.id.alert_combinaison);
alert_combinaison.setText(selectedHorses.stream()
.map(String::valueOf)
.collect(Collectors.joining("-")));
TextView aler_coeff = (TextView) view.findViewById(R.id.alert_coeff);
aler_coeff.setText("Coef:"+String.valueOf(pari.getCoefficient()));
TextView alert_montant = (TextView) view.findViewById(R.id.alert_montant);
alert_montant.setText("Montant: "+String.valueOf(pari.getTypesParisMises().get(0).getMiseTotale())+" CFA");
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setView(view);
builder.setCancelable(false);
dialog = builder.create();
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
view.findViewById(R.id.alert_validate).setOnClickListener(v->{
try {
_printPari(pari);
dialog.dismiss();
} catch (WriterException e) {
throw new RuntimeException(e);
}
});
view.findViewById(R.id.alert_cancel).setOnClickListener(v -> {
dialog.dismiss();
});
dialog.show();
}
void _printPari(ParisResponse pari) throws WriterException {
try {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pmu_logo);
Bitmap barcode = generateBarcodeBitmap(pari.getNumeroTicket(), 384, 100);
StringBuilder tspl = new StringBuilder();
Printama printama = Printama.with(getContext());
tspl.append("Bamako").append("\n");
tspl.append(pari.getCourseNom()).append("\n");
OffsetDateTime dateTime = OffsetDateTime.parse(pari.getHeureDepartPrevue() != null ? pari.getHeureDepartPrevue() : OffsetDateTime.now().toString());
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
String formattedDate = dateTime.format(formatter);
tspl.append(formattedDate).append("\n");
tspl.append("Course ").append(String.valueOf(pari.getCourseId())).append("\n");
tspl.append(printama.lineSeparator()+"\n");
List<String> selectedHorses = Arrays.stream(pari.getCombinaison().split(",")).collect(Collectors.toList());
boolean isElargie = selectedHorses.size()> listProduits.get(pari.getTypesParisMises().get(0).getTypePari());
String typePari = pari.getTypesParisMises().get(0).getTypePari();
tspl.append(isElargie?typePari+"/Elargie":typePari).append("\n");
tspl.append(pari.getFormules().contains("FORMULE_COMPLETE") ?"COMBINAISON COMPLETE"+"\n":"");
String combinationText = selectedHorses.stream()
.map(String::valueOf)
.collect(Collectors.joining("-"));
tspl.append(combinationText).append("\n");
tspl.append("COEF: ").append(String.valueOf(pari.getCoefficient()));
tspl.append("\n").append(printama.lineSeparator()).append("\n");
tspl.append("MONTANT: ").append(pari.getMiseTotale()).append(" XOF");
tspl.append("\n").append(printama.lineSeparator()).append("\n");
tspl.append("AGENT: ").append(pari.getAgentCode()).append("\n");
tspl.append("DATE: ").append(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss").format(OffsetDateTime.parse(pari.getDateHeurePrise()))).append("\n");
if (BluetoothUtils.needsBluetoothPermissions()) {
if (!BluetoothUtils.hasBluetoothPermission(requireContext())) {
// Demande la permission si non accordée
BluetoothUtils.requestBluetoothPermission(requireActivity());
return; // arrête ici, la popup va apparaître
}
}
// 2⃣ Permission OK, on peut afficher la liste
Printama.with(getContext()).printTextBuilder(tspl, bitmap, pari.getNumeroTicket(), barcode, new Printama.PrintCallback() {
@Override
public void onResult(boolean success, String errorMessage) {
if(!success){
MessageDialog.showError(getContext(), errorMessage);
}else{
MessageDialog.showSuccess(getContext(), "Pari imprimé avec succès");
}
}
});
} catch (SecurityException e) {
Toast.makeText(requireContext(),
"Permission Bluetooth non accordée", Toast.LENGTH_SHORT).show();
}
}
public Bitmap generateBarcodeBitmap(String contents, int width, int height) throws WriterException {
BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, BarcodeFormat.CODE_128, width, height);
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
bmp.setPixel(x, y, bitMatrix.get(x, y) ? android.graphics.Color.BLACK : android.graphics.Color.WHITE);
}
}
return bmp;
}
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();

View File

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

View File

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

View File

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

View File

@@ -1,17 +1,26 @@
package com.example.quiz; package com.example.quiz;
import android.graphics.Color; import android.graphics.Color;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import com.anggastudio.printama.Pref;
import com.anggastudio.printama.Printama;
import com.example.quiz.utils.AuthNavigator; import com.example.quiz.utils.AuthNavigator;
import com.example.quiz.utils.BluetoothUtils;
import com.example.quiz.utils.MessageDialog;
import com.example.quiz.utils.SessionManager; import com.example.quiz.utils.SessionManager;
import com.example.quiz.utils.SharedPrefsHelper; import com.example.quiz.utils.SunmiPrinterManager;
import com.example.quiz.viewModel.LoginViewModel; import com.example.quiz.viewModel.LoginViewModel;
import com.example.quiz.viewModel.LogsViewModel; import com.example.quiz.viewModel.LogsViewModel;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavController; import androidx.navigation.NavController;
import androidx.navigation.Navigation; import androidx.navigation.Navigation;
@@ -20,6 +29,11 @@ import androidx.navigation.ui.NavigationUI;
import com.example.quiz.databinding.ActivityPageQuizBinding; import com.example.quiz.databinding.ActivityPageQuizBinding;
import java.util.HashSet;
import java.util.Set;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
@AndroidEntryPoint @AndroidEntryPoint
@@ -32,6 +46,11 @@ public class PageQuiz extends AppCompatActivity {
AuthNavigator authNavigator; AuthNavigator authNavigator;
private SessionManager sessionManager; private SessionManager sessionManager;
String mobileName;
private Set<Class<?>> exemptedFragment = new HashSet<>();
private Handler handler = new Handler(); private Handler handler = new Handler();
private Runnable checkRunnable; private Runnable checkRunnable;
@@ -41,12 +60,15 @@ public class PageQuiz extends AppCompatActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
exemptedFragment.add(ServerConfig.class);
exemptedFragment.add(Logs.class);
sessionManager = SessionManager.newInstance(this); sessionManager = SessionManager.newInstance(this);
mobileName = Build.MANUFACTURER;
binding = ActivityPageQuizBinding.inflate(getLayoutInflater()); binding = ActivityPageQuizBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
logsViewModel = new ViewModelProvider(this).get(LogsViewModel.class); logsViewModel = new ViewModelProvider(this).get(LogsViewModel.class);
viewModel = new ViewModelProvider(this).get(LoginViewModel.class); viewModel = new ViewModelProvider(this).get(LoginViewModel.class);
authNavigator = new AuthNavigator(this, getSupportFragmentManager(), sessionManager, viewModel, logsViewModel); authNavigator = new AuthNavigator(this, getSupportFragmentManager(), sessionManager, viewModel, logsViewModel, this);
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
binding.toolbar.setBackgroundColor(Color.parseColor("#501C5A29")); binding.toolbar.setBackgroundColor(Color.parseColor("#501C5A29"));
} }
@@ -117,19 +139,107 @@ public class PageQuiz extends AppCompatActivity {
// } // }
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
View touchedView = findTouchedView(ev);
Fragment currentFragment = getCurrentFragment();
if(currentFragment != null && exemptedFragment.contains(currentFragment.getClass())){
return super.dispatchTouchEvent(ev);
}
if(touchedView!=null && "tag_pin_exempt".equals(touchedView.getTag())){
return super.dispatchTouchEvent(ev);
}
if (authNavigator.isSessionExpired()) {
authNavigator.showPinDialog(() -> {
authNavigator.updatedLastExpiredDate();
});
return false; // Consomme l'événement pour ne pas le propager
}
authNavigator.updatedLastExpiredDate();
return super.dispatchTouchEvent(ev);
}
private Fragment getCurrentFragment() {
FragmentManager fm = getSupportFragmentManager();
// Méthode 1: Par conteneur
Fragment fragment = fm.findFragmentById(R.id.nav_host_fragment_content_main);
// Méthode 2: Par tag si vous utilisez des tags
if (fragment == null) {
fragment = fm.findFragmentByTag("current_fragment");
}
return fragment;
}
private View findTouchedView(MotionEvent ev) {
float x = ev.getRawX();
float y = ev.getRawY();
View rootView = getWindow().getDecorView().getRootView();
return findViewAtPosition(rootView, (int) x, (int) y);
}
private View findViewAtPosition(View view, int x, int y) {
if (view == null) return null;
int[] location = new int[2];
view.getLocationOnScreen(location);
int left = location[0];
int top = location[1];
int right = left + view.getWidth();
int bottom = top + view.getHeight();
if (x >= left && x <= right && y >= top && y <= bottom) {
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
for (int i = group.getChildCount() - 1; i >= 0; i--) {
View child = group.getChildAt(i);
View found = findViewAtPosition(child, x, y);
if (found != null) return found;
}
}
return view;
}
return null;
}
private void checkPermission(){
Pref.init(this);
if (BluetoothUtils.needsBluetoothPermissions()) {
if (!BluetoothUtils.hasBluetoothPermission(this)) {
// Demande la permission si non accordée
BluetoothUtils.requestBluetoothPermission(this);
return; // arrête ici, la popup va apparaître
}
}
// 2⃣ Permission OK, on peut afficher la liste
try {
Printama printama = Printama.with(this);
if(printama.getConnectedPrinter() == null){
BluetoothUtils.showPrinterList(this, this);
}
} catch (SecurityException e) {
MessageDialog.showError(this, "Permission refusée");
}
}
@Override @Override
protected void onResume() { protected void onResume() {
if(!mobileName.toLowerCase().contains("sunmi")){
checkPermission();
}
super.onResume(); super.onResume();
handler = new Handler(Looper.getMainLooper()); handler = new Handler(Looper.getMainLooper());
checkRunnable = new Runnable() { checkRunnable = new Runnable() {
@Override @Override
public void run() { public void run() {
if (sessionManager.isExpired()) { if (sessionManager.isExpired()) {
if (!authNavigator.dialogIsShowing()) { if (!authNavigator.dialogIsShowing()) {
authNavigator.showPinDialog(() -> { authNavigator.showPinDialog(() -> {
sessionManager.updateLastExpiredDate(); authNavigator.updatedLastExpiredDate();
handler.postDelayed(checkRunnable, 1); handler.postDelayed(checkRunnable, 1);
}); });
} }

View File

@@ -1,7 +1,9 @@
package com.example.quiz; package com.example.quiz;
import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@@ -17,7 +19,9 @@ import androidx.recyclerview.widget.RecyclerView;
import android.provider.Settings; import android.provider.Settings;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -41,6 +45,7 @@ import java.net.NetworkInterface;
import java.util.Collections; import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.List; import java.util.List;
import java.util.Objects;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
@@ -61,6 +66,8 @@ public class ServerConfig extends Fragment {
PointDeVenteAdapter pointDeVenteAdapter; PointDeVenteAdapter pointDeVenteAdapter;
TpeResponse existingTpe;
PointDeVente pdv = null; PointDeVente pdv = null;
private PointDeVenteViewModel pointDeVenteViewModel; private PointDeVenteViewModel pointDeVenteViewModel;
@@ -100,9 +107,66 @@ public class ServerConfig extends Fragment {
toolbar.setTitle("Configuration du terminal"); toolbar.setTitle("Configuration du terminal");
toolbar.setBackgroundColor( getResources().getColor(R.color.primary_green)); toolbar.setBackgroundColor( getResources().getColor(R.color.primary_green));
} }
if(sharedPrefsHelper.get("terminalId") != null){
viewModel.getTpeById(sharedPrefsHelper.get("terminalId")).observe(getViewLifecycleOwner(), new Observer<Result<TpeResponse>>() {
@Override
public void onChanged(Result<TpeResponse> tpeResult) {
switch (tpeResult.status){
case LOADING:
loader.show("Chargement du TPE");
break;
case SUCCESS:
loader.dismiss();
existingTpe = tpeResult.data;
if( existingTpe != null && Objects.equals(sharedPrefsHelper.get("terminalId"), String.valueOf(existingTpe.getId()))){
_checkPdv(existingTpe);
return;
}else{
MessageDialog.showError(getContext(), "Ce TPE n'est pas encore configuré");
_startAutocomplete();
break;
}
case ERROR:
loader.dismiss();
MessageDialog.showError(getContext(), "Erreur lors du chargement du TPE");
break;
}
}
});
}
_startAutocomplete();
super.onViewCreated(view, savedInstanceState);
}
@SuppressLint("ClickableViewAccessibility")
void _startAutocomplete(){
pointDeVenteAdapter = new PointDeVenteAdapter(); pointDeVenteAdapter = new PointDeVenteAdapter();
RecyclerView recyclerView = binding.pointRecyclerView; RecyclerView recyclerView = binding.pointRecyclerView;
recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
binding.getRoot().setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// Vérifier si le clic est en dehors de l'autocomplete
if (binding.autocompleteContainer.getVisibility() == View.VISIBLE && !binding.pointDeVente.isEnabled()) {
int[] location = new int[2];
binding.autocompleteContainer.getLocationOnScreen(location);
Rect rect = new Rect(
location[0],
location[1],
location[0] + binding.autocompleteContainer.getWidth(),
location[1] + binding.autocompleteContainer.getHeight()
);
int x = (int) event.getRawX();
int y = (int) event.getRawY();
if (!rect.contains(x, y)) {
binding.autocompleteContainer.setVisibility(View.GONE);
}
}
}
return false;
});
binding.pointDeVente.addTextChangedListener(new TextWatcher() { binding.pointDeVente.addTextChangedListener(new TextWatcher() {
@Override @Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
@@ -112,7 +176,7 @@ public class ServerConfig extends Fragment {
@Override @Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if(!isUserTap) return; if(!isUserTap) return;
if(charSequence.toString().isEmpty() || charSequence.toString().length()==0){ if(charSequence.toString().isEmpty() || !binding.pointDeVente.isEnabled()){
recyclerView.setVisibility(View.GONE); recyclerView.setVisibility(View.GONE);
binding.autocompleteContainer.setVisibility(View.GONE); binding.autocompleteContainer.setVisibility(View.GONE);
return; return;
@@ -157,7 +221,34 @@ public class ServerConfig extends Fragment {
} }
}); });
super.onViewCreated(view, savedInstanceState); }
void _checkPdv(Tpe eTpe){
binding.numeroSerie.setText(eTpe.getNumeroSerie());
binding.brand.setText(eTpe.getTypeTerminal());
binding.model.setText(eTpe.getModeleAppareil());
binding.type.setText(eTpe.getTypeTerminal());
pointDeVenteViewModel.getPointDeVenteById(String.valueOf(eTpe.getPointDeVenteId())).observe(getViewLifecycleOwner(), new Observer<Result<PointDeVente>>() {
@Override
public void onChanged(Result<PointDeVente> pointDeVenteResult) {
switch (pointDeVenteResult.status){
case LOADING:
loader.show("Chargement du point de vente");
break;
case SUCCESS:
loader.dismiss();
binding.pointDeVente.setEnabled(false);
binding.pointDeVente.setText(pointDeVenteResult.data.getNom());
binding.validate.setClickable(false);
MessageDialog.showSuccess(getContext(), "Ce tpe est déjà configuré");
break;
case ERROR:
loader.dismiss();
MessageDialog.showError(getContext(), "Erreur lors du chargement du point de vente");
break;
}
}
});
} }
@Override @Override

View File

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

View File

@@ -2,11 +2,15 @@ package com.example.quiz;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.DatePickerDialog; import android.app.DatePickerDialog;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
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.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
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;
@@ -19,6 +23,8 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Toast; import android.widget.Toast;
import com.anggastudio.printama.Printama;
import com.example.quiz.data.model.dtos.paris.SoldeResponse;
import com.example.quiz.databinding.FragmentSoldBinding; import com.example.quiz.databinding.FragmentSoldBinding;
import com.example.quiz.utils.LoaderDialog; import com.example.quiz.utils.LoaderDialog;
import com.example.quiz.utils.MessageDialog; import com.example.quiz.utils.MessageDialog;
@@ -27,7 +33,10 @@ import com.example.quiz.utils.SharedPrefsHelper;
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;
import com.google.zxing.WriterException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Calendar; import java.util.Calendar;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
@@ -116,9 +125,9 @@ public class Sold extends Fragment {
getContext(), getContext(),
(view, year, month, dayOfMonth) -> { (view, year, month, dayOfMonth) -> {
String date = year + "-" + _reformatDateForDate(month + 1) + "-" + _reformatDateForDate(dayOfMonth); String date = year + "-" + _reformatDateForDate(month + 1) + "-" + _reformatDateForDate(dayOfMonth);
pariViewModel.getSoldeByDay(prefsHelper.get("id"), date).observe(getViewLifecycleOwner(), new Observer<Result<Double>>() { pariViewModel.getSoldeByDay(prefsHelper.get("id"), date).observe(getViewLifecycleOwner(), new Observer<Result<SoldeResponse>>() {
@Override @Override
public void onChanged(Result<Double> doubleResult) { public void onChanged(Result<SoldeResponse> doubleResult) {
switch (doubleResult.status){ switch (doubleResult.status){
case LOADING:{ case LOADING:{
dialog.show("Solde du jour"); dialog.show("Solde du jour");
@@ -131,7 +140,12 @@ public class Sold extends Fragment {
} }
case SUCCESS:{ case SUCCESS:{
dialog.dismiss(); dialog.dismiss();
_showSold(doubleResult.data);
try {
_showSold(doubleResult.data, date);
} catch (WriterException e) {
throw new RuntimeException(e);
}
logsViewModel.insertLog(prefsHelper.get("id"), "SOLDE JOUR", "Solde du "+date, System.currentTimeMillis()); logsViewModel.insertLog(prefsHelper.get("id"), "SOLDE JOUR", "Solde du "+date, System.currentTimeMillis());
break; break;
} }
@@ -147,12 +161,38 @@ public class Sold extends Fragment {
datePickerDialog.show(); datePickerDialog.show();
} }
void _showSold(double solde){ void _showSold(SoldeResponse soldeResponse, String date) throws WriterException {
try {
int solde = soldeResponse.getMontantParis() - soldeResponse.getMontantAnnulations() - soldeResponse.getMontantPaiements();
new AlertDialog.Builder(getContext()) new AlertDialog.Builder(getContext())
.setTitle("Solde") .setTitle("Solde")
.setMessage("Solde la course "+solde) .setMessage("Solde la course "+solde)
.setPositiveButton("Ok", (dialog, which)->{ .setNeutralButton("Ok", (dialog, which)->{
dialog.dismiss(); dialog.dismiss();
}).show(); })
.setPositiveButton("Imprimer", (dialog, which)->{
Printama printama = Printama.with(getContext());
Bitmap logo = BitmapFactory.decodeResource(getResources(), R.drawable.pmu_logo);
String title = "SOLDE AU "+date;
StringBuilder text = new StringBuilder();
text.append("VENTES HIPPIQUES: ").append(String.valueOf(soldeResponse.getMontantParis())).append(" XOF").append("\n");
text.append("NBR. PAIEMENTS: ").append(soldeResponse.getNombrePaiements()).append("\n");
text.append("PAIEMENTS: ").append(String.valueOf(soldeResponse.getMontantPaiements())).append(" XOF").append("\n");
text.append("NBR. ANNULATIONS: ").append(soldeResponse.getNombreAnnulations()).append("\n");
text.append("ANNULATIONS: ").append(String.valueOf(soldeResponse.getMontantAnnulations())).append(" XOF").append("\n");
text.append(printama.lineSeparator()).append("\n");
text.append("SOLDE: ").append(String.valueOf(solde)).append(" XOF").append("\n");
text.append(printama.lineSeparator()).append("\n");
text.append("AGENT : ").append(prefsHelper.get("code")).append("\n");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
String now = formatter.format(LocalDateTime.now());
text.append("DATE : ").append(now).append("\n");
printama.printSold(logo, title, text);
})
.show();
}catch (SecurityException e){
MessageDialog.showError(getContext(), "Erreur lors de l'affichage du solde");
}
} }
} }

View File

@@ -1,10 +1,14 @@
package com.example.quiz; package com.example.quiz;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
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.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
@@ -14,6 +18,8 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Toast; import android.widget.Toast;
import com.anggastudio.printama.Printama;
import com.example.quiz.data.model.dtos.paris.SoldeResponse;
import com.example.quiz.databinding.FragmentSoldByCourseBinding; import com.example.quiz.databinding.FragmentSoldByCourseBinding;
import com.example.quiz.utils.LoaderDialog; import com.example.quiz.utils.LoaderDialog;
import com.example.quiz.utils.MessageDialog; import com.example.quiz.utils.MessageDialog;
@@ -21,6 +27,10 @@ import com.example.quiz.utils.Result;
import com.example.quiz.utils.SharedPrefsHelper; import com.example.quiz.utils.SharedPrefsHelper;
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 java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
@@ -78,9 +88,9 @@ public class SoldByCourse extends Fragment {
binding.etRaceNumber.setError("Veuillez entrer un numéro de course"); binding.etRaceNumber.setError("Veuillez entrer un numéro de course");
return; return;
} }
pariViewModel.getSoldeByCourse(prefsHelper.get("id"), binding.etRaceNumber.getText().toString()).observe(getViewLifecycleOwner(), new Observer<Result<Double>>() { pariViewModel.getSoldeByCourse(prefsHelper.get("id"), binding.etRaceNumber.getText().toString()).observe(getViewLifecycleOwner(), new Observer<Result<SoldeResponse>>() {
@Override @Override
public void onChanged(Result<Double> doubleResult) { public void onChanged(Result<SoldeResponse> doubleResult) {
switch (doubleResult.status){ switch (doubleResult.status){
case LOADING:{ case LOADING:{
loader.show("Chargement du solde"); loader.show("Chargement du solde");
@@ -93,7 +103,11 @@ public class SoldByCourse extends Fragment {
} }
case SUCCESS:{ case SUCCESS:{
loader.dismiss(); loader.dismiss();
_showPariDialog(doubleResult.data); try {
_showPariDialog(doubleResult.data, binding.etRaceNumber.getText().toString());
} catch (WriterException e) {
throw new RuntimeException(e);
}
logsViewModel.insertLog(prefsHelper.get("id"), "SOLDE COURSE", "Solde de la course "+binding.etRaceNumber.getText(), System.currentTimeMillis()); logsViewModel.insertLog(prefsHelper.get("id"), "SOLDE COURSE", "Solde de la course "+binding.etRaceNumber.getText(), System.currentTimeMillis());
break; break;
} }
@@ -103,14 +117,38 @@ public class SoldByCourse extends Fragment {
}); });
} }
void _showPariDialog(double solde){ void _showPariDialog(SoldeResponse soldeResponse, String numero) throws WriterException {
try {
int solde = soldeResponse.getMontantParis() - soldeResponse.getMontantAnnulations() - soldeResponse.getMontantPaiements();
new AlertDialog.Builder(getContext()) new AlertDialog.Builder(getContext())
.setTitle("Solde") .setTitle("Solde")
.setMessage("Solde la course "+solde) .setMessage("Solde la course "+solde)
.setPositiveButton("Ok", (dialog, which) -> { .setNeutralButton("Ok", (dialog, which) -> {
binding.etRaceNumber.setText(""); binding.etRaceNumber.setText("");
dialog.dismiss(); dialog.dismiss();
}) })
.setPositiveButton("Imprimer", (dialog, which)->{
Printama printama = Printama.with(getContext());
Bitmap logo = BitmapFactory.decodeResource(getResources(), R.drawable.pmu_logo);
String title = "SOLDE DE LA COURSE "+numero;
StringBuilder text = new StringBuilder();
text.append("VENTES HIPPIQUES: ").append(String.valueOf(soldeResponse.getMontantParis())).append(" XOF").append("\n");
text.append("NBR. PAIEMENTS: ").append(soldeResponse.getNombrePaiements()).append("\n");
text.append("PAIEMENTS: ").append(String.valueOf(soldeResponse.getMontantPaiements())).append(" XOF").append("\n");
text.append("NBR. ANNULATIONS: ").append(soldeResponse.getNombreAnnulations()).append("\n");
text.append("ANNULATIONS: ").append(String.valueOf(soldeResponse.getMontantAnnulations())).append(" XOF").append("\n");
text.append(printama.lineSeparator()).append("\n");
text.append("SOLDE: ").append(String.valueOf(solde)).append(" XOF").append("\n");
text.append(printama.lineSeparator()).append("\n");
text.append("AGENT : ").append(prefsHelper.get("code")).append("\n");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
String now = formatter.format(LocalDateTime.now());
text.append("DATE : ").append(now).append("\n");
printama.printSold(logo, title, text);
})
.show(); .show();
}catch (SecurityException e){
MessageDialog.showError(getContext(), "Erreur lors de l'affichage du solde");
}
} }
} }

View File

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

View File

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

View File

@@ -1,26 +1,59 @@
package com.example.quiz; package com.example.quiz;
import android.annotation.SuppressLint;
import android.graphics.Color;
import android.os.Bundle; import android.os.Bundle;
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.Observer;
import androidx.lifecycle.ViewModelProvider;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.example.quiz.data.model.ParisResponse;
import com.example.quiz.data.model.enums.PariStatut;
import com.example.quiz.databinding.FragmentWinTicketBinding; import com.example.quiz.databinding.FragmentWinTicketBinding;
import com.example.quiz.utils.LoaderDialog;
import com.example.quiz.utils.MessageDialog;
import com.example.quiz.utils.Result;
import com.example.quiz.utils.SharedPrefsHelper;
import com.example.quiz.viewModel.LogsViewModel;
import com.example.quiz.viewModel.PariViewModel;
import com.google.android.material.appbar.MaterialToolbar; import com.google.android.material.appbar.MaterialToolbar;
import org.w3c.dom.Text;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import dagger.hilt.android.AndroidEntryPoint;
/** /**
* A simple {@link Fragment} subclass. * A simple {@link Fragment} subclass.
* Use the {@link WinTicket#newInstance} factory method to * Use the {@link WinTicket#newInstance} factory method to
* create an instance of this fragment. * create an instance of this fragment.
*/ */
@AndroidEntryPoint
public class WinTicket extends Fragment { public class WinTicket extends Fragment {
FragmentWinTicketBinding binding; FragmentWinTicketBinding binding;
PariViewModel pariViewModel;
LogsViewModel logsViewModel;
SharedPrefsHelper sharedPrefsHelper;
LoaderDialog loader;
public WinTicket() { public WinTicket() {
// Required empty public constructor // Required empty public constructor
@@ -48,6 +81,10 @@ public class WinTicket extends Fragment {
@Override @Override
public void onViewCreated(View view, Bundle savedInstanceState) { public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
pariViewModel = new ViewModelProvider(this).get(PariViewModel.class);
logsViewModel = new ViewModelProvider(this).get(LogsViewModel.class);
sharedPrefsHelper = SharedPrefsHelper.getInstance(getContext());
loader = new LoaderDialog(getContext());
binding.winTicketBtnBack.setOnClickListener(v -> { binding.winTicketBtnBack.setOnClickListener(v -> {
getActivity().onBackPressed(); getActivity().onBackPressed();
}); });
@@ -58,6 +95,173 @@ public class WinTicket extends Fragment {
}); });
scannerDialog.show(getParentFragmentManager(), "scanner"); scannerDialog.show(getParentFragmentManager(), "scanner");
}); });
binding.winTicketVerificationBtn.setOnClickListener(v -> {
String reference = binding.referenceTicket.getText().toString();
if(reference.isEmpty()){
MessageDialog.showError(getContext(), "Veuillez donner la reference du ticket");
return;
}
pariViewModel.getPariByNumero(reference).observe(getViewLifecycleOwner(), new Observer<Result<ParisResponse>>() {
@Override
public void onChanged(Result<ParisResponse> parisResponseResult) {
switch (parisResponseResult.status){
case LOADING:{
loader.show("Recherche du ticket");
break;
}
case ERROR:{
MessageDialog.showError(getContext(), parisResponseResult.message);
break;
}
case SUCCESS:{
if(parisResponseResult.data.getNumeroTicket().equals(reference)){
loader.dismiss();
_showDialog(parisResponseResult.data);
}else{
MessageDialog.showError(getContext(), "Le ticket n'existe pas");
}
break;
}
}
}
});
});
}
@SuppressLint("SetTextI18n")
void _showDialog(ParisResponse pari){
LayoutInflater inflater = getLayoutInflater();
View view = inflater.inflate(R.layout.pay_confirmation_layout, null);
TextView statut = (TextView) view.findViewById(R.id.tv_statut);
statut.setText("Statut: "+String.valueOf(pari.getStatutPari()));
TextView dateHeure = (TextView) view.findViewById(R.id.tv_date_heure);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
dateHeure.setText(formatter.format(LocalDateTime.now()));
TextView numeroCourse = (TextView) view.findViewById(R.id.tv_numero_course);
numeroCourse.setText(String.valueOf(pari.getCourseNumero()));
TextView nomCourse = (TextView) view.findViewById(R.id.tv_nom_course);
nomCourse.setText(pari.getCourseNom());
TextView numeroTicket = (TextView) view.findViewById(R.id.tv_numero_ticket);
numeroTicket.setText(pari.getNumeroTicket());
TextView datePrise = (TextView) view.findViewById(R.id.tv_date_prise);
datePrise.setText(formatter.format(OffsetDateTime.parse(pari.getDateHeurePrise())));
TextView combinaison = (TextView) view.findViewById(R.id.tv_combinaison);
combinaison.setText(pari.getCombinaison());
TextView montant = (TextView) view.findViewById(R.id.tv_montant);
LinearLayout montantLayout = (LinearLayout) view.findViewById(R.id.tv_montant_layout);
Button confirmer = (Button) view.findViewById(R.id.btn_confirmer);
LinearLayout notesLayout = (LinearLayout) view.findViewById(R.id.notes_layout);
TextView notes = (TextView) view.findViewById(R.id.notes);
TextView messageStatut = (TextView) view.findViewById(R.id.tv_message_statut);
switch (pari.getStatutPari()){
case GAGNANT:{
messageStatut.setText("Veuillez confirmer le paiement de ce pari");
montant.setText(pari.getGainCalcule()+" CFA");
break;
}
case A_REMBOURSER:{
messageStatut.setText("Veuillez confirmer le remboursement de ce pari");
montant.setText(pari.getMiseTotale()+" CFA");
break;
}
case PAYE:{
messageStatut.setText("Ce pari a déjà été payé");
montant.setText(pari.getGainCalcule()+" CFA");
notesLayout.setVisibility(View.VISIBLE);
notes.setText(pari.getNotes());
confirmer.setVisibility(View.GONE);
break;
}
default:{
montantLayout.setVisibility(View.GONE);
statut.setTextColor(Color.parseColor("#cf1c08"));
messageStatut.setVisibility(View.GONE);
confirmer.setVisibility(View.GONE);
break;
}
}
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setView(view);
AlertDialog dialog = builder.create();
Button annuler = (Button) view.findViewById(R.id.btn_annuler);
annuler.setOnClickListener(v -> {
dialog.dismiss();
});
confirmer.setOnClickListener(v -> {
switch (pari.getStatutPari()){
case A_REMBOURSER:{
pariViewModel.rembourserPari(pari.getNumeroTicket()).observe(getViewLifecycleOwner(), new Observer<Result<ParisResponse>>() {
@Override
public void onChanged(Result<ParisResponse> pariResponseResult) {
switch (pariResponseResult.status){
case LOADING: {
loader.show("Remboursement en cours");
break;
}
case ERROR: {
loader.dismiss();
MessageDialog.showError(getContext(), pariResponseResult.message);
break;
}
case SUCCESS: {
loader.dismiss();
if(sharedPrefsHelper.get("id") == null){
MessageDialog.showError(getContext(), "Erreur de connexion");
return;
}
logsViewModel.insertLog(sharedPrefsHelper.get("id"), "REMBOURSEMENT TICKET", "Remboursement du ticket numéro "+ pari.getNumeroTicket()+" montant "+pari.getMiseTotale(), System.currentTimeMillis());
MessageDialog.showSuccess(getContext(), "Remboursement effectué avec succès");
binding.referenceTicket.setText("");
dialog.dismiss();
break;
}
}
}
});
break;
}
case GAGNANT:{
pariViewModel.payerPari(pari.getNumeroTicket()).observe(getViewLifecycleOwner(), new Observer<Result<ParisResponse>>() {
@Override
public void onChanged(Result<ParisResponse> pariResponseResult) {
switch (pariResponseResult.status){
case LOADING: {
loader.show("Paiement en cours");
break;
}
case ERROR: {
loader.dismiss();
MessageDialog.showError(getContext(), pariResponseResult.message);
break;
}
case SUCCESS: {
loader.dismiss();
if(sharedPrefsHelper.get("id") == null){
MessageDialog.showError(getContext(), "Erreur de connexion");
return;
}
logsViewModel.insertLog(sharedPrefsHelper.get("id"), "PAIEMENT TICKET", "Paiement du ticket numéro "+ pari.getNumeroTicket()+" montant "+pari.getGainCalcule(), System.currentTimeMillis());
binding.referenceTicket.setText("");
MessageDialog.showSuccess(getContext(), "Paiement effectué avec succès");
dialog.dismiss();
break;
}
}
}
});
break;
}
default:{
dialog.dismiss();
MessageDialog.showError(getContext(), "Action non comprise pour ce ticket");
binding.referenceTicket.setText("");
break;
}
}
});
dialog.show();
} }
@Override @Override

View File

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

View File

@@ -1,9 +1,16 @@
package com.example.quiz.data.adapter; package com.example.quiz.data.adapter;
import static java.security.AccessController.getContext;
import android.graphics.Color;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@@ -17,21 +24,23 @@ import java.util.List;
public class LastBetsAdapter extends RecyclerView.Adapter<LastBetsAdapter.LastBetsViewHolder> { public class LastBetsAdapter extends RecyclerView.Adapter<LastBetsAdapter.LastBetsViewHolder> {
private List<ParisResponse> listeParis = new ArrayList<>(); // évite les NPE private List<ParisResponse> listeParis = new ArrayList<>(); // évite les NPE
private OnBetClick onBetClick; // peut être null si tu veux
public interface OnBetClick { private onPariClickListener listener;
public interface onPariClickListener {
void onItemClick(ParisResponse pari); void onItemClick(ParisResponse pari);
void onItemCancel(ParisResponse pari);
} }
public LastBetsAdapter(List<ParisResponse> listeParis, OnBetClick onBetClick) { public void setPariClickListener(onPariClickListener listener) {
this.listener = listener;
}
public LastBetsAdapter(List<ParisResponse> listeParis) {
if (listeParis != null) this.listeParis = listeParis; if (listeParis != null) this.listeParis = listeParis;
this.onBetClick = onBetClick;
} }
// Constructeur vide utile si tu veux créer puis setData ensuite
public LastBetsAdapter(OnBetClick onBetClick) {
this.onBetClick = onBetClick;
}
public void setData(List<ParisResponse> newList) { public void setData(List<ParisResponse> newList) {
if (newList == null) { if (newList == null) {
@@ -77,10 +86,44 @@ public class LastBetsAdapter extends RecyclerView.Adapter<LastBetsAdapter.LastBe
// Mise (défensif) // Mise (défensif)
holder.mise.setText("Mise: " + pari.getMiseTotale() + " CFA"); holder.mise.setText("Mise: " + pari.getMiseTotale() + " CFA");
holder.tvStatut.setText(pari.getStatutPari() != null ? String.valueOf(pari.getStatutPari()) : "Pas de statut");
switch (pari.getStatutPari()){
case ANNULE:{
holder.tvStatut.setTextColor(Color.RED);
holder.btnAnnuler.setVisibility(View.GONE);
holder.btnImprimer.setVisibility(View.GONE);
break;
}
case PAYE:{
holder.tvStatut.setTextColor(Color.GREEN);
holder.btnAnnuler.setVisibility(View.GONE);
holder.btnImprimer.setVisibility(View.GONE);
break;
}
case ENREGISTRE:{
holder.tvStatut.setTextColor(Color.YELLOW);
holder.btnAnnuler.setVisibility(View.VISIBLE);
holder.btnImprimer.setVisibility(View.VISIBLE);
break;
}
default:{
holder.tvStatut.setTextColor(Color.BLACK);
holder.btnAnnuler.setVisibility(View.GONE);
holder.btnImprimer.setVisibility(View.GONE);
break;
}
}
// click listener défensif holder.btnAnnuler.setOnClickListener(v -> {
holder.itemView.setOnClickListener(v -> { if (listener != null) {
if (onBetClick != null) onBetClick.onItemClick(pari); listener.onItemCancel(pari);
}
});
holder.btnImprimer.setOnClickListener(v -> {
if (listener != null) {
listener.onItemClick(pari);
}
}); });
} }
@@ -90,7 +133,8 @@ public class LastBetsAdapter extends RecyclerView.Adapter<LastBetsAdapter.LastBe
} }
public static class LastBetsViewHolder extends RecyclerView.ViewHolder { public static class LastBetsViewHolder extends RecyclerView.ViewHolder {
TextView betType, horses, course, mise, refenceTicket, typeOfCourse; TextView betType, horses, course, mise, refenceTicket, typeOfCourse, tvStatut;
Button btnAnnuler, btnImprimer;;
public LastBetsViewHolder(@NonNull View itemView) { public LastBetsViewHolder(@NonNull View itemView) {
super(itemView); super(itemView);
@@ -100,6 +144,9 @@ public class LastBetsAdapter extends RecyclerView.Adapter<LastBetsAdapter.LastBe
mise = itemView.findViewById(R.id.last_bet_mise); mise = itemView.findViewById(R.id.last_bet_mise);
refenceTicket = itemView.findViewById(R.id.reference_ticket); refenceTicket = itemView.findViewById(R.id.reference_ticket);
typeOfCourse = itemView.findViewById(R.id.type_of_course); typeOfCourse = itemView.findViewById(R.id.type_of_course);
btnAnnuler = itemView.findViewById(R.id.btn_annuler);
btnImprimer = itemView.findViewById(R.id.btn_imprimer);
tvStatut = itemView.findViewById(R.id.tv_statut);
} }
} }
} }

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
package com.example.quiz.data.model; package com.example.quiz.data.model;
import java.io.Serializable; import java.io.Serializable;
import java.time.temporal.TemporalAccessor;
import java.util.List; import java.util.List;
public class ParisResponse implements Serializable { public class ParisResponse implements Serializable {
@@ -58,7 +59,10 @@ public class ParisResponse implements Serializable {
PAYE, PAYE,
ANNULE, ANNULE,
PERDANT, PERDANT,
GAGNANT GAGNANT,
A_REMBOURSER,
REMBOURSE,
ECHEC_CACUL
} }
// ================= INNER CLASS ================= // ================= INNER CLASS =================

View File

@@ -0,0 +1,36 @@
package com.example.quiz.data.model;
public class ResponseError {
private String message;
private int status;
private String timestamp;
public ResponseError(){
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -20,7 +20,7 @@ import retrofit2.converter.gson.GsonConverterFactory;
@Module @Module
@InstallIn(SingletonComponent.class) @InstallIn(SingletonComponent.class)
public class ApiClient { public class ApiClient {
private static final String BASE_URL = "https://boxer-adapting-bluegill.ngrok-free.app/api/"; private static final String BASE_URL = "https://alr.pmu.ml/api/";
@Provides @Provides

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,10 +7,10 @@ import androidx.lifecycle.MutableLiveData;
import com.example.quiz.data.model.Course; import com.example.quiz.data.model.Course;
import com.example.quiz.data.model.PagedModel; import com.example.quiz.data.model.PagedModel;
import com.example.quiz.data.model.ResponseError;
import com.example.quiz.data.remote.ApiService; import com.example.quiz.data.remote.ApiService;
import com.example.quiz.utils.Result; import com.example.quiz.utils.Result;
import com.google.gson.Gson;
import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
@@ -20,6 +20,7 @@ import retrofit2.Response;
public class CourseRepository { public class CourseRepository {
private ApiService apiService; private ApiService apiService;
private final Course.Statut OPENED_STATUT = Course.Statut.OUVERT;
@Inject @Inject
public CourseRepository(ApiService apiService) { public CourseRepository(ApiService apiService) {
@@ -29,13 +30,20 @@ public class CourseRepository {
public LiveData<Result<PagedModel<Course>>> getCourses(String reunionDate) { public LiveData<Result<PagedModel<Course>>> getCourses(String reunionDate) {
MutableLiveData<Result<PagedModel<Course>>> liveCourses = new MutableLiveData<Result<PagedModel<Course>>>(); MutableLiveData<Result<PagedModel<Course>>> liveCourses = new MutableLiveData<Result<PagedModel<Course>>>();
liveCourses.setValue(Result.loading()); liveCourses.setValue(Result.loading());
apiService.getCourses(reunionDate).enqueue(new Callback<PagedModel<Course>>() { apiService.getCourses(reunionDate,String.valueOf(OPENED_STATUT)).enqueue(new Callback<PagedModel<Course>>() {
@Override @Override
public void onResponse(Call<PagedModel<Course>> call, Response<PagedModel<Course>> response) { public void onResponse(Call<PagedModel<Course>> call, Response<PagedModel<Course>> response) {
if(response.isSuccessful()){ if(response.isSuccessful()){
liveCourses.postValue(Result.success(response.body())); liveCourses.postValue(Result.success(response.body()));
}else{ }else{
liveCourses.postValue(Result.error(response.message())); try{
String error = response.errorBody().string();
Gson gson = new Gson();
ResponseError errorResponse = gson.fromJson(error, ResponseError.class);
liveCourses.postValue(Result.error(errorResponse.getMessage()));
} catch (Exception e) {
throw new RuntimeException(e);
}
} }
} }

View File

@@ -5,11 +5,17 @@ import android.util.Log;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import com.example.quiz.data.model.ResponseError;
import com.example.quiz.data.model.dtos.auth.ChangePin;
import com.example.quiz.data.model.dtos.auth.LoginPayload; import com.example.quiz.data.model.dtos.auth.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.dtos.auth.User;
import com.example.quiz.data.remote.ApiService; import com.example.quiz.data.remote.ApiService;
import com.example.quiz.data.remote.TokenManager; import com.example.quiz.data.remote.TokenManager;
import com.example.quiz.utils.Result; import com.example.quiz.utils.Result;
import com.google.gson.Gson;
import java.io.IOException;
import javax.inject.Inject; import javax.inject.Inject;
@@ -20,6 +26,7 @@ import retrofit2.Response;
public class LoginRepository { public class LoginRepository {
private ApiService apiService; private ApiService apiService;
private TokenManager tokenManager; private TokenManager tokenManager;
@Inject @Inject
public LoginRepository(ApiService apiService, TokenManager tokenManager) { public LoginRepository(ApiService apiService, TokenManager tokenManager) {
this.tokenManager = tokenManager; this.tokenManager = tokenManager;
@@ -34,18 +41,54 @@ public class LoginRepository {
public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) { public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
if(response.isSuccessful()){ if(response.isSuccessful()){
liveLogin.postValue(Result.success(response.body())); liveLogin.postValue(Result.success(response.body()));
Log.d("TOKEN", response.body().getToken());
tokenManager.saveToken(response.body().getToken()); tokenManager.saveToken(response.body().getToken());
}else{ }else{
liveLogin.postValue(Result.error(response.message())); try {
String error = response.errorBody().string();
Gson gson = new Gson();
ResponseError errorResponse = gson.fromJson(error, ResponseError.class);
liveLogin.postValue(Result.error(errorResponse.getMessage()));
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
} }
@Override @Override
public void onFailure(Call<LoginResponse> call, Throwable throwable) { public void onFailure(Call<LoginResponse> call, Throwable throwable) {
liveLogin.postValue(Result.error(throwable.getMessage())); liveLogin.postValue(Result.error(throwable.toString()));
} }
}); });
return liveLogin; return liveLogin;
} }
public LiveData<Result<User>> changePin(String oldPin, String newPin){
MutableLiveData<Result<User>> liveUser = new MutableLiveData<Result<User>>();
liveUser.setValue(Result.loading());
ChangePin changePin = new ChangePin();
changePin.setOldPin(oldPin);
changePin.setNewPin(newPin);
apiService.changePin(changePin).enqueue(new Callback<User>(){
@Override
public void onResponse(Call<User> call, Response<User> response) {
if(response.isSuccessful()) {
liveUser.postValue(Result.success(response.body()));
}else {
try {
String error = response.errorBody().string();
Gson gson = new Gson();
ResponseError errorResponse = gson.fromJson(error, ResponseError.class);
liveUser.postValue(Result.error(errorResponse.getMessage()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Override
public void onFailure(Call<User> call, Throwable throwable) {
liveUser.postValue(Result.error(throwable.getMessage()));
}
});
return liveUser;
}
} }

View File

@@ -0,0 +1,58 @@
package com.example.quiz.data.repository;
import android.util.Log;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.quiz.data.model.MiseInitiale;
import com.example.quiz.data.model.PariMise;
import com.example.quiz.data.model.ResponseError;
import com.example.quiz.data.remote.ApiService;
import com.example.quiz.data.remote.TokenManager;
import com.example.quiz.utils.Result;
import com.google.gson.Gson;
import java.util.List;
import javax.inject.Inject;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class PariMiseRepository {
private ApiService apiService;
@Inject
public PariMiseRepository(ApiService apiService, TokenManager tokenManager) {
this.apiService = apiService;
}
public LiveData<Result<List<MiseInitiale>>> getBetInitMise() {
MutableLiveData<Result<List<MiseInitiale>>> livePariMise = new MutableLiveData<Result<List<MiseInitiale>>>();
livePariMise.setValue(Result.loading());
apiService.getBetInitMise().enqueue(new Callback<List<MiseInitiale>>() {
@Override
public void onResponse(Call<List<MiseInitiale>> call, Response<List<MiseInitiale>> response) {
if (response.isSuccessful()) {
livePariMise.postValue(Result.success(response.body()));
}else{
try{
String error = response.errorBody().string();
Gson gson = new Gson();
ResponseError errorResponse = gson.fromJson(error, ResponseError.class);
livePariMise.postValue(Result.error(errorResponse.getMessage()));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@Override
public void onFailure(Call<List<MiseInitiale>> call, Throwable throwable) {
livePariMise.postValue(Result.error(throwable.getMessage()));
}
});
return livePariMise;
}
}

View File

@@ -8,8 +8,12 @@ import androidx.lifecycle.MutableLiveData;
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.data.model.ResponseError;
import com.example.quiz.data.model.dtos.paris.CancelParisPaylaod;
import com.example.quiz.data.model.dtos.paris.SoldeResponse;
import com.example.quiz.data.remote.ApiService; import com.example.quiz.data.remote.ApiService;
import com.example.quiz.utils.Result; import com.example.quiz.utils.Result;
import com.google.gson.Gson;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -42,13 +46,10 @@ public class PariRepository {
} else { } else {
// On récupère l'erreur exacte envoyée par le backend // On récupère l'erreur exacte envoyée par le backend
try { try {
Map<String, String> errorBody = Collections.emptyMap(); String error = response.errorBody().string();
if(response.errorBody() != null){ Gson gson = new Gson();
errorBody = (Map<String, String>) response.errorBody(); ResponseError errorResponse = gson.fromJson(error, ResponseError.class);
} pariResponse.postValue(Result.error(errorResponse.getMessage()));
Log.d("PariRepository", response.errorBody().toString());
pariResponse.postValue(Result.error(errorBody.get("message")));
} catch (Exception e) { } catch (Exception e) {
pariResponse.postValue(Result.error("Erreur serveur")); pariResponse.postValue(Result.error("Erreur serveur"));
} }
@@ -74,10 +75,10 @@ public class PariRepository {
derniersParis.postValue(Result.success(response.body())); derniersParis.postValue(Result.success(response.body()));
}else{ }else{
try{ try{
String errorBody = response.errorBody() != null ? String error = response.errorBody().string();
response.errorBody().string() : "Erreur inconnue"; Gson gson = new Gson();
ResponseError errorResponse = gson.fromJson(error, ResponseError.class);
derniersParis.postValue(Result.error(errorBody)); derniersParis.postValue(Result.error(errorResponse.getMessage()));
}catch (Exception e){ }catch (Exception e){
derniersParis.postValue(Result.error(e.getMessage())); derniersParis.postValue(Result.error(e.getMessage()));
} }
@@ -93,6 +94,60 @@ public class PariRepository {
return derniersParis; return derniersParis;
} }
public LiveData<Result<ParisResponse>> payTicket(String numeroTicket){
MutableLiveData<Result<ParisResponse>> pariResponse = new MutableLiveData<>();
pariResponse.setValue(Result.loading());
apiService.payerPari(numeroTicket).enqueue(new Callback<ParisResponse>(){
@Override
public void onResponse(Call<ParisResponse> call, Response<ParisResponse> response) {
if(response.isSuccessful()){
pariResponse.postValue(Result.success(response.body()));
}else{
try{
String error = response.errorBody().string();
Gson gson = new Gson();
ResponseError errorResponse = gson.fromJson(error, ResponseError.class);
pariResponse.postValue(Result.error(errorResponse.getMessage()));
}catch (Exception e){
pariResponse.postValue(Result.error(e.getMessage()));
}
}
};
@Override
public void onFailure(Call<ParisResponse> call, Throwable throwable) {
pariResponse.postValue(Result.error(throwable.getMessage()));
}
});
return pariResponse;
}
public LiveData<Result<ParisResponse>> rembourserTicket(String numeroTicket){
MutableLiveData<Result<ParisResponse>> pariResponse = new MutableLiveData<>();
pariResponse.setValue(Result.loading());
apiService.rembourserPari(numeroTicket).enqueue(new Callback<ParisResponse>(){
@Override
public void onResponse(Call<ParisResponse> call, Response<ParisResponse> response) {
if(response.isSuccessful()){
pariResponse.postValue(Result.success(response.body()));
}else{
try{
String error = response.errorBody().string();
Gson gson = new Gson();
ResponseError errorResponse = gson.fromJson(error, ResponseError.class);
pariResponse.postValue(Result.error(errorResponse.getMessage()));
}catch (Exception e){
pariResponse.postValue(Result.error(e.getMessage()));
}
}
}
@Override
public void onFailure(Call<ParisResponse> call, Throwable throwable) {
pariResponse.postValue(Result.error(throwable.getMessage()));
}
});
return pariResponse;
}
public LiveData<Result<ParisResponse>> annulerPari(String numeroTicket){ public LiveData<Result<ParisResponse>> annulerPari(String numeroTicket){
MutableLiveData<Result<ParisResponse>> pariResponse = new MutableLiveData<>(); MutableLiveData<Result<ParisResponse>> pariResponse = new MutableLiveData<>();
pariResponse.setValue(Result.loading()); pariResponse.setValue(Result.loading());
@@ -103,10 +158,10 @@ public class PariRepository {
pariResponse.postValue(Result.success(response.body())); pariResponse.postValue(Result.success(response.body()));
}else{ }else{
try{ try{
String errorBody = response.errorBody() != null ? String error = response.errorBody().string();
response.errorBody().string() : "Erreur inconnue"; Gson gson = new Gson();
ResponseError errorResponse = gson.fromJson(error, ResponseError.class);
pariResponse.postValue(Result.error(errorBody)); pariResponse.postValue(Result.error(errorResponse.getMessage()));
}catch (Exception e){ }catch (Exception e){
pariResponse.postValue(Result.error(e.getMessage())); pariResponse.postValue(Result.error(e.getMessage()));
} }
@@ -121,17 +176,20 @@ public class PariRepository {
return pariResponse; return pariResponse;
} }
public LiveData<Result<Double>> getSoldeByCourse(String agentId, String courseId){ public LiveData<Result<SoldeResponse>> getSoldeByCourse(String agentId, String courseId){
MutableLiveData<Result<Double>> solde = new MutableLiveData<>(); MutableLiveData<Result<SoldeResponse>> solde = new MutableLiveData<>();
solde.setValue(Result.loading()); solde.setValue(Result.loading());
apiService.getSoldeByCourse(agentId, courseId).enqueue(new Callback<Double>() { apiService.getSoldeByCourse(agentId, courseId).enqueue(new Callback<SoldeResponse>() {
@Override @Override
public void onResponse(Call<Double> call, Response<Double> response) { public void onResponse(Call<SoldeResponse> call, Response<SoldeResponse> response) {
if(response.isSuccessful()){ if(response.isSuccessful()){
solde.postValue(Result.success(response.body())); solde.postValue(Result.success(response.body()));
}else{ }else{
try{ try{
solde.postValue(Result.error(response.errorBody().string())); String error = response.errorBody().string();
Gson gson = new Gson();
ResponseError errorResponse = gson.fromJson(error, ResponseError.class);
solde.postValue(Result.error(errorResponse.getMessage()));
}catch (Exception e){ }catch (Exception e){
solde.postValue(Result.error(e.getMessage())); solde.postValue(Result.error(e.getMessage()));
} }
@@ -139,24 +197,27 @@ public class PariRepository {
} }
@Override @Override
public void onFailure(Call<Double> call, Throwable throwable) { public void onFailure(Call<SoldeResponse> call, Throwable throwable) {
solde.postValue(Result.error(throwable.getMessage())); solde.postValue(Result.error(throwable.getMessage()));
} }
}); });
return solde; return solde;
} }
public LiveData<Result<Double>> getSoldeByDay(String agentId, String day){ public LiveData<Result<SoldeResponse>> getSoldeByDay(String agentId, String day){
MutableLiveData<Result<Double>> solde = new MutableLiveData<>(); MutableLiveData<Result<SoldeResponse>> solde = new MutableLiveData<>();
solde.setValue(Result.loading()); solde.setValue(Result.loading());
apiService.getSoldeByDay(agentId, day).enqueue(new Callback<Double>() { apiService.getSoldeByDay(agentId, day).enqueue(new Callback<SoldeResponse>() {
@Override @Override
public void onResponse(Call<Double> call, Response<Double> response) { public void onResponse(Call<SoldeResponse> call, Response<SoldeResponse> response) {
if(response.isSuccessful()){ if(response.isSuccessful()){
solde.postValue(Result.success(response.body())); solde.postValue(Result.success(response.body()));
}else{ }else{
try { try {
solde.postValue(Result.error(response.errorBody().string())); String error = response.errorBody().string();
Gson gson = new Gson();
ResponseError errorResponse = gson.fromJson(error, ResponseError.class);
solde.postValue(Result.error(errorResponse.getMessage()));
}catch (Exception e){ }catch (Exception e){
solde.postValue(Result.error(e.getMessage())); solde.postValue(Result.error(e.getMessage()));
} }
@@ -164,11 +225,39 @@ public class PariRepository {
} }
@Override @Override
public void onFailure(Call<Double> call, Throwable throwable) { public void onFailure(Call<SoldeResponse> call, Throwable throwable) {
solde.postValue(Result.error(throwable.getMessage())); solde.postValue(Result.error(throwable.getMessage()));
} }
}); });
return solde; return solde;
} }
public LiveData<Result<ParisResponse>> getPariByNumero(String numeroTicket) {
MutableLiveData<Result<ParisResponse>> pari = new MutableLiveData<>();
pari.setValue(Result.loading());
apiService.getPariByNumero(numeroTicket).enqueue(new Callback<ParisResponse>() {
@Override
public void onResponse(Call<ParisResponse> call, Response<ParisResponse> response) {
if (response.isSuccessful()) {
pari.postValue(Result.success(response.body()));
}else{
try {
String error = response.errorBody().string();
Gson gson = new Gson();
ResponseError errorResponse = gson.fromJson(error, ResponseError.class);
pari.postValue(Result.error(errorResponse.getMessage()));
}catch (Exception e){
pari.postValue(Result.error(e.getMessage()));
}
}
}
@Override
public void onFailure(Call<ParisResponse> call, Throwable throwable) {
pari.postValue(Result.error(throwable.getMessage()));
}
});
return pari;
}
} }

View File

@@ -5,10 +5,14 @@ import androidx.lifecycle.MutableLiveData;
import com.example.quiz.data.model.PagedModel; import com.example.quiz.data.model.PagedModel;
import com.example.quiz.data.model.PointDeVente; import com.example.quiz.data.model.PointDeVente;
import com.example.quiz.data.model.ResponseError;
import com.example.quiz.data.remote.ApiService; import com.example.quiz.data.remote.ApiService;
import com.example.quiz.utils.Result; import com.example.quiz.utils.Result;
import com.google.gson.Gson;
import java.io.IOException;
import javax.inject.Inject; import javax.inject.Inject;
import retrofit2.*; import retrofit2.*;
@@ -36,18 +40,14 @@ public class PointDeVenteRepository {
if (response.isSuccessful() && response.body() != null) { if (response.isSuccessful() && response.body() != null) {
result.postValue(Result.success(response.body())); result.postValue(Result.success(response.body()));
} else { } else {
String errorMessage = "Erreur inconnue";
try { try {
if (response.errorBody() != null) { String error = response.errorBody().string();
errorMessage = response.errorBody().string(); Gson gson = new Gson();
ResponseError errorResponse = gson.fromJson(error, ResponseError.class);
result.postValue(Result.error(errorResponse.getMessage()));
} catch (IOException e) {
throw new RuntimeException(e);
} }
} catch (Exception e) {
errorMessage = e.getMessage();
}
result.postValue(Result.error(errorMessage));
} }
} }
@@ -62,4 +62,33 @@ public class PointDeVenteRepository {
return result; return result;
} }
public LiveData<Result<PointDeVente>> getPointDeVenteById(String id) {
MutableLiveData<Result<PointDeVente>> result = new MutableLiveData<>();
result.setValue(Result.loading());
apiService.getPointDeVenteById(id).enqueue(new Callback<PointDeVente>() {
@Override
public void onResponse(Call<PointDeVente> call, Response<PointDeVente> response) {
if(response.isSuccessful()){
result.postValue(Result.success(response.body()));
}else{
try {
String error = response.errorBody().string();
Gson gson = new Gson();
ResponseError errorResponse = gson.fromJson(error, ResponseError.class);
result.postValue(Result.error(errorResponse.getMessage()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Override
public void onFailure(Call<PointDeVente> call, Throwable throwable) {
result.postValue(Result.error(throwable.getMessage()));
}
});
return result;
}
} }

View File

@@ -3,10 +3,12 @@ package com.example.quiz.data.repository;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import com.example.quiz.data.model.ResponseError;
import com.example.quiz.data.model.Tpe; import com.example.quiz.data.model.Tpe;
import com.example.quiz.data.model.TpeResponse; import com.example.quiz.data.model.TpeResponse;
import com.example.quiz.data.remote.ApiService; import com.example.quiz.data.remote.ApiService;
import com.example.quiz.utils.Result; import com.example.quiz.utils.Result;
import com.google.gson.Gson;
import javax.inject.Inject; import javax.inject.Inject;
@@ -33,7 +35,43 @@ public class TpeRepository {
if (response.isSuccessful()) { if (response.isSuccessful()) {
tpeLiveData.postValue(Result.success(response.body())); tpeLiveData.postValue(Result.success(response.body()));
} else { } else {
tpeLiveData.postValue(Result.error(response.message())); try{
String error = response.errorBody().string();
Gson gson = new Gson();
ResponseError errorResponse = gson.fromJson(error, ResponseError.class);
tpeLiveData.postValue(Result.error(errorResponse.getMessage()));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@Override
public void onFailure(Call<TpeResponse> call, Throwable throwable) {
tpeLiveData.postValue(Result.error(throwable.getMessage()));
}
});
return tpeLiveData;
}
public LiveData<Result<TpeResponse>> getTpeById(String id) {
MutableLiveData<Result<TpeResponse>> tpeLiveData = new MutableLiveData<>();
tpeLiveData.setValue(Result.loading());
apiService.getTpeById(id).enqueue(new Callback<TpeResponse>() {
@Override
public void onResponse(Call<TpeResponse> call, Response<TpeResponse> response) {
if (response.isSuccessful()) {
tpeLiveData.postValue(Result.success(response.body()));
} else {
try{
String error = response.errorBody().string();
Gson gson = new Gson();
ResponseError errorResponse = gson.fromJson(error, ResponseError.class);
tpeLiveData.postValue(Result.error(errorResponse.getMessage()));
} catch (Exception e) {
throw new RuntimeException(e);
}
} }
} }

View File

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

View File

@@ -31,6 +31,10 @@ public class SessionManager {
lastExpiredDate = LocalDateTime.now(); lastExpiredDate = LocalDateTime.now();
} }
public void setToExpire(){
lastExpiredDate = null;
}
public boolean isExpired() { public boolean isExpired() {
if (lastExpiredDate == null) { if (lastExpiredDate == null) {
return true; return true;

View File

@@ -0,0 +1,275 @@
package com.example.quiz.utils;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.RemoteException;
import android.util.Log;
import com.sunmi.peripheral.printer.InnerPrinterCallback;
import com.sunmi.peripheral.printer.InnerPrinterManager;
import com.sunmi.peripheral.printer.InnerResultCallback;
import com.sunmi.peripheral.printer.SunmiPrinterService;
import java.util.List;
import java.util.function.Consumer;
public class SunmiPrinterManager {
private static final String TAG = "SunmiPrinterManager";
private static SunmiPrinterManager instance;
private static final int MAX_CHAR_2_INCH = 32;
private static Context context;
private SunmiPrinterService sunmiPrinter;
private SunmiPrinterManager() {}
public static SunmiPrinterManager getInstance(Context ctx) {
if (instance == null) {
instance = new SunmiPrinterManager();
context = ctx.getApplicationContext();
}
return instance;
}
// 🔌 Bind UNE SEULE FOIS
public void connectPrinter(Consumer<Boolean> status) {
Log.d("######", "Point d'entrée printer");
try {
InnerPrinterManager.getInstance().bindService(context, new InnerPrinterCallback() {
@Override
protected void onConnected(SunmiPrinterService service) {
Log.d("######", "Connecté printer");
sunmiPrinter = service;
status.accept(true);
}
@Override
protected void onDisconnected() {
Log.d("######", "Déconnecté printer");
sunmiPrinter = null;
status.accept(false);
}
});
} catch (Exception e) {
Log.e(TAG, "#################Erreur bindService"+String.valueOf(e));
}
}
// ✅ simple check
// 🖨️ Impression robuste
public void printText(String text, PrinterListener listener) {
try {
sunmiPrinter.printText(text + "\n", new InnerResultCallback() {
@Override
public void onRunResult(boolean isSuccess) {
if(!isSuccess){
try {
throw new Exception("Impression", new Throwable("Erreur lors d'impression!"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@Override
public void onReturnString(String result) {
// rarement utile
}
@Override
public void onRaiseException(int code, String msg) {
listener.onError(msg);
listener.onResult(code);
}
@Override
public void onPrintResult(int code, String msg) {
listener.onResult(code);
listener.onError(msg);
}
});
} catch (Exception e) {
if (listener != null) {
listener.onError(e.getMessage());
}
}
}
public int getMaxChar(){
return MAX_CHAR_2_INCH;
}
public boolean printPari(Bitmap logo, Bitmap barcode, String numeroTicket, String text){
try{
printSimpleImage(logo);
printTextSimple("\n"+separationText()+"\n");
printSimpleImage(barcode);
setAlignment(1, new PrinterListener() {
@Override
public void onResult(int code) {
}
@Override
public void onError(String errorMessage) {
}
});
printTextSimple(numeroTicket);
printTextSimple("\n"+separationText()+"\n");
setAlignment(0, new PrinterListener() {
@Override
public void onResult(int code) {}
@Override
public void onError(String errorMessage) {}
});
printTextSimple(text);
printTextSimple("\n"+separationText()+"\n");
setAlignment(1, new PrinterListener() {
@Override
public void onResult(int code) {}
@Override
public void onError(String errorMessage) {}
});
printTextSimple("Powered by PMU-MALI");
printTextSimple("\n"+separationText()+"\n");
printTextSimple(" ");
printTextSimple(" ");
return true;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String separationText(){
return "-".repeat(MAX_CHAR_2_INCH);
}
public void printSimpleImage(Bitmap bitmap){
printImage(bitmap, new PrinterListener() {
@Override
public void onResult(int code) {
Log.d(TAG, "Print OK");
}
@Override
public void onError(String errorMessage) {
Log.e(TAG, errorMessage);
}
});
}
// version simple
public void printTextSimple(String text) {
printText(text, new PrinterListener() {
@Override
public void onResult(int code) {
Log.d(TAG, "Print OK");
}
@Override
public void onError(String errorMessage) {
Log.e(TAG, errorMessage);
}
});
}
// Dans SunmiPrinterManager.java
public void setAlignment(int alignment, PrinterListener listener) {
try {
// Appel au SDK Sunmi : 0=gauche, 1=centre, 2=droite
sunmiPrinter.setAlignment(alignment, new InnerResultCallback() {
@Override
public void onRunResult(boolean isSuccess) throws RemoteException {
if(!isSuccess){
try {
throw new Exception("Alignement", new Throwable("L'alignement n'a pas pu être défini"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@Override
public void onReturnString(String result) throws RemoteException {
}
@Override
public void onRaiseException(int code, String msg) throws RemoteException {
listener.onError(msg);
listener.onResult(code);
}
@Override
public void onPrintResult(int code, String msg) throws RemoteException {
}
});
} catch (Exception e) {
if (listener != null) listener.onError(e.getMessage());
}
}
public void printImage(Bitmap bitmap, PrinterListener listener){
try {
// La plupart des imprimantes Sunmi utilisent cette méthode
// Le paramètre '0' correspond généralement à l'alignement (0 = gauche)
sunmiPrinter.printBitmap(bitmap, new InnerResultCallback() {
@Override
public void onRunResult(boolean isSuccess) {
if(!isSuccess){
try {
throw new Exception("Impression", new Throwable("Erreur lors d'impression d'une image!"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@Override
public void onReturnString(String result) {
// Pas utile
}
@Override
public void onRaiseException(int code, String msg) {
Log.e(TAG, "onRaiseException: "+String.valueOf(code));
listener.onResult(code);
listener.onError(msg);
}
@Override
public void onPrintResult(int code, String msg) {
Log.e(TAG, "onPrintResult: "+String.valueOf(code));
listener.onResult(code);
listener.onError(msg);
}
// N'oublie pas les autres callbacks vides (onRunResult, etc.)
});
} catch (Exception e) {
listener.onError(e.getMessage());
}
}
public int printerStatus(){
try {
return sunmiPrinter.updatePrinterState();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
public interface PrinterListener {
void onResult(int code);
void onError(String errorMessage);
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModel;
import com.example.quiz.data.model.Pari; import com.example.quiz.data.model.Pari;
import com.example.quiz.data.model.ParisResponse; import com.example.quiz.data.model.ParisResponse;
import com.example.quiz.data.model.dtos.paris.SoldeResponse;
import com.example.quiz.data.repository.PariRepository; import com.example.quiz.data.repository.PariRepository;
import com.example.quiz.utils.Result; import com.example.quiz.utils.Result;
@@ -23,9 +24,16 @@ public class PariViewModel extends ViewModel {
private LiveData<Result<ParisResponse>> pariAnnule; private LiveData<Result<ParisResponse>> pariAnnule;
private LiveData<Result<Double>> solde; private LiveData<Result<ParisResponse>> pariPaye;
private LiveData<Result<Double>> soldeByDay;
private LiveData<Result<ParisResponse>> pariByNumero;
private LiveData<Result<SoldeResponse>> solde;
private LiveData<Result<SoldeResponse>> soldeByDay;
private LiveData<Result<ParisResponse>> pariRembourse;
@Inject @Inject
public PariViewModel(PariRepository repository){ public PariViewModel(PariRepository repository){
@@ -47,13 +55,28 @@ public class PariViewModel extends ViewModel {
return pariAnnule; return pariAnnule;
} }
public LiveData<Result<Double>> getSoldeByCourse(String agentId, String courseId){ public LiveData<Result<SoldeResponse>> getSoldeByCourse(String agentId, String courseId){
solde = pariRepository.getSoldeByCourse(agentId, courseId); solde = pariRepository.getSoldeByCourse(agentId, courseId);
return solde; return solde;
} }
public LiveData<Result<Double>> getSoldeByDay(String agentId, String day){ public LiveData<Result<SoldeResponse>> getSoldeByDay(String agentId, String day){
soldeByDay = pariRepository.getSoldeByDay(agentId, day); soldeByDay = pariRepository.getSoldeByDay(agentId, day);
return soldeByDay; return soldeByDay;
} }
public LiveData<Result<ParisResponse>> getPariByNumero(String numeroTicket){
pariByNumero = pariRepository.getPariByNumero(numeroTicket);
return pariByNumero;
}
public LiveData<Result<ParisResponse>> payerPari(String numeroTicket){
pariPaye = pariRepository.payTicket(numeroTicket);
return pariPaye;
}
public LiveData<Result<ParisResponse>> rembourserPari(String numeroTicket){
pariRembourse = pariRepository.rembourserTicket(numeroTicket);
return pariRembourse;
}
} }

View File

@@ -17,6 +17,8 @@ public class PointDeVenteViewModel extends ViewModel {
private final PointDeVenteRepository pointDeVenteRepository; private final PointDeVenteRepository pointDeVenteRepository;
private LiveData<Result<PagedModel<PointDeVente>>> pointsDeVente; private LiveData<Result<PagedModel<PointDeVente>>> pointsDeVente;
private LiveData<Result<PointDeVente>> pointDeVenteById;
@Inject @Inject
public PointDeVenteViewModel(PointDeVenteRepository pointDeVenteRepository){ public PointDeVenteViewModel(PointDeVenteRepository pointDeVenteRepository){
this.pointDeVenteRepository = pointDeVenteRepository; this.pointDeVenteRepository = pointDeVenteRepository;
@@ -26,4 +28,9 @@ public class PointDeVenteViewModel extends ViewModel {
this.pointsDeVente = pointDeVenteRepository.getPointsDeVente(search); this.pointsDeVente = pointDeVenteRepository.getPointsDeVente(search);
return this.pointsDeVente; return this.pointsDeVente;
} }
public LiveData<Result<PointDeVente>> getPointDeVenteById(String id) {
pointDeVenteById = pointDeVenteRepository.getPointDeVenteById(id);
return pointDeVenteById;
}
} }

View File

@@ -18,6 +18,8 @@ public class TpeViewModel extends ViewModel {
private LiveData<Result<TpeResponse>> tpeLiveData; private LiveData<Result<TpeResponse>> tpeLiveData;
private LiveData<Result<TpeResponse>> tpeByIdLiveData;
@Inject @Inject
public TpeViewModel(TpeRepository repository){ public TpeViewModel(TpeRepository repository){
this.tpeRepository = repository; this.tpeRepository = repository;
@@ -27,4 +29,9 @@ public class TpeViewModel extends ViewModel {
tpeLiveData = tpeRepository.createTpe(tpe); tpeLiveData = tpeRepository.createTpe(tpe);
return tpeLiveData; return tpeLiveData;
} }
public LiveData<Result<TpeResponse>> getTpeById(String id){
tpeByIdLiveData = tpeRepository.getTpeById(id);
return tpeByIdLiveData;
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
@@ -10,7 +11,7 @@
android:layout_width="64dp" android:layout_width="64dp"
android:layout_height="64dp" android:layout_height="64dp"
android:src="@android:drawable/ic_delete" android:src="@android:drawable/ic_delete"
android:tint="#F44336"/> app:tint="#F44336" />
<TextView <TextView
android:id="@+id/txtMessage" android:id="@+id/txtMessage"

View File

@@ -1,4 +1,5 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
@@ -10,7 +11,8 @@
android:layout_width="64dp" android:layout_width="64dp"
android:layout_height="64dp" android:layout_height="64dp"
android:src="@android:drawable/checkbox_on_background" android:src="@android:drawable/checkbox_on_background"
android:tint="#4CAF50"/> android:outlineSpotShadowColor="#4CAF50"
/>
<TextView <TextView
android:id="@+id/txtMessage" android:id="@+id/txtMessage"

View File

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

View File

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

View File

@@ -29,7 +29,7 @@
android:textSize="14sp" android:textSize="14sp"
android:drawableStart="@drawable/hashtag" android:drawableStart="@drawable/hashtag"
android:background="@drawable/edittext_outline_white" android:background="@drawable/edittext_outline_white"
android:inputType="number" android:inputType="text"
/> />
<ImageButton <ImageButton
android:id="@+id/scan_ticket_btn" android:id="@+id/scan_ticket_btn"

View File

@@ -54,20 +54,20 @@
android:text="ANNULER TICKET" android:text="ANNULER TICKET"
android:textAllCaps="false" android:textAllCaps="false"
android:textColor="#FFFFFF"/> android:textColor="#FFFFFF"/>
<Button <!-- <Button-->
android:id="@+id/report_btn" <!-- android:id="@+id/report_btn"-->
android:layout_width="match_parent" <!-- android:layout_width="match_parent"-->
android:layout_height="57dp" <!-- android:layout_height="57dp"-->
android:textSize="21sp" <!-- android:textSize="21sp"-->
android:textAlignment="textStart" <!-- android:textAlignment="textStart"-->
android:layout_marginVertical="4sp" <!-- android:layout_marginVertical="4sp"-->
android:background="@drawable/rounded_button_green" <!-- android:background="@drawable/rounded_button_green"-->
android:fontFamily="sans-serif-medium" <!-- android:fontFamily="sans-serif-medium"-->
android:backgroundTint="@color/primary_green" <!-- android:backgroundTint="@color/primary_green"-->
android:drawableLeft="@drawable/tpe" <!-- android:drawableLeft="@drawable/tpe"-->
android:text="RAPPORTS" <!-- android:text="RAPPORTS"-->
android:textAllCaps="false" <!-- android:textAllCaps="false"-->
android:textColor="#FFFFFF"/> <!-- android:textColor="#FFFFFF"/>-->
<Button <Button
android:id="@+id/last_bets_btn" android:id="@+id/last_bets_btn"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -68,7 +68,7 @@
android:textColor="#FFFFFF"/> android:textColor="#FFFFFF"/>
<Button <Button
android:id="@+id/settings" android:id="@+id/settings"
android:tag="@id/tag_pin_exempt" android:tag="tag_pin_exempt"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="57dp" android:layout_height="57dp"
android:textSize="23sp" android:textSize="23sp"

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@@ -212,23 +213,28 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="30sp" android:layout_marginTop="30sp"
android:gravity="center"> android:gravity="center"
android:orientation="horizontal">
<Button <Button
android:id="@+id/cancel" android:id="@+id/cancel"
android:layout_width="200dp" android:layout_width="0dp"
android:layout_marginRight="3sp"
android:layout_height="40dp" android:layout_height="40dp"
android:layout_weight="1"
android:layout_marginEnd="8dp"
android:background="@color/primary_red" android:background="@color/primary_red"
android:text="@string/cancel"/> android:text="@string/cancel"/>
<Button <Button
android:id="@+id/validate" android:id="@+id/validate"
android:layout_marginLeft="3sp" android:layout_width="0dp"
android:layout_width="200dp"
android:layout_height="40dp" android:layout_height="40dp"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:background="@color/primary_green" android:background="@color/primary_green"
android:text="@string/validate"/> android:text="@string/validate"/>
</LinearLayout>
</LinearLayout> </LinearLayout>
</LinearLayout>
</ScrollView> </ScrollView>
</FrameLayout> </FrameLayout>

View File

@@ -12,19 +12,19 @@
android:gravity="start" android:gravity="start"
android:orientation="vertical" android:orientation="vertical"
android:visibility="visible"> android:visibility="visible">
<Button <!-- <Button-->
android:id="@+id/agent" <!-- android:id="@+id/agent"-->
android:layout_width="match_parent" <!-- android:layout_width="match_parent"-->
android:layout_height="60dp" <!-- android:layout_height="60dp"-->
android:textSize="21sp" <!-- android:textSize="21sp"-->
android:textAlignment="center" <!-- android:textAlignment="center"-->
android:layout_marginVertical="4sp" <!-- android:layout_marginVertical="4sp"-->
android:background="@drawable/rounded_button_green" <!-- android:background="@drawable/rounded_button_green"-->
android:fontFamily="sans-serif-medium" <!-- android:fontFamily="sans-serif-medium"-->
android:backgroundTint="@color/primary_green" <!-- android:backgroundTint="@color/primary_green"-->
android:text="@string/agents_setting" <!-- android:text="@string/agents_setting"-->
android:textAllCaps="false" <!-- android:textAllCaps="false"-->
android:textColor="#FFFFFF"/> <!-- android:textColor="#FFFFFF"/>-->
<Button <Button
android:id="@+id/change_pin" android:id="@+id/change_pin"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -40,6 +40,7 @@
android:textColor="#FFFFFF"/> android:textColor="#FFFFFF"/>
<Button <Button
android:id="@+id/server_config" android:id="@+id/server_config"
android:tag="tag_pin_exempt"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="60dp" android:layout_height="60dp"
android:textSize="21sp" android:textSize="21sp"
@@ -66,6 +67,7 @@
android:textColor="#FFFFFF"/> android:textColor="#FFFFFF"/>
<Button <Button
android:id="@+id/logs" android:id="@+id/logs"
android:tag="tag_pin_exempt"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="60dp" android:layout_height="60dp"
android:textSize="21sp" android:textSize="21sp"

View File

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

View File

@@ -29,7 +29,7 @@
android:textSize="14sp" android:textSize="14sp"
android:drawableStart="@drawable/hashtag" android:drawableStart="@drawable/hashtag"
android:background="@drawable/edittext_outline_white" android:background="@drawable/edittext_outline_white"
android:inputType="number" android:inputType="text"
/> />
<ImageButton <ImageButton
android:id="@+id/scan_ticket_btn" android:id="@+id/scan_ticket_btn"

View File

@@ -13,10 +13,10 @@
android:padding="18dp" android:padding="18dp"
android:background="@drawable/course_item_border"> android:background="@drawable/course_item_border">
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="horizontal"
android:layout_gravity="center_vertical"> android:layout_gravity="start">
<ImageView <ImageView
android:layout_width="50dp" android:layout_width="50dp"
android:layout_height="50dp" android:layout_height="50dp"
@@ -24,14 +24,15 @@
</ImageView> </ImageView>
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<TextView <TextView
android:id="@+id/tvName" android:id="@+id/tvName"
android:text="GP SANGARE"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="18sp" android:textSize="16sp"
android:textColor="@color/white" android:textColor="@color/white"
android:textFontWeight="500" android:textFontWeight="500"
android:fontFamily="sans-serif-medium"> android:fontFamily="sans-serif-medium">

View File

@@ -2,18 +2,30 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:orientation="vertical"
android:padding="12dp" android:padding="8dp"
android:layout_marginVertical="10dp"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/white"> android:background="@color/white">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:gravity="center_vertical" android:gravity="end">
android:layout_marginBottom="8dp"> <TextView
android:id="@+id/tv_statut"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Statut"
android:textSize="20sp">
</TextView>
</LinearLayout>
<!-- Première ligne : Course et Type -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView <TextView
android:id="@+id/last_bet_course" android:id="@+id/last_bet_course"
@@ -21,14 +33,13 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="Course: 5" android:text="Course: 5"
android:textSize="16sp" android:textSize="14sp"
android:gravity="start" android:padding="2dp" />
android:padding="4dp" />
<com.google.android.material.divider.MaterialDivider <com.google.android.material.divider.MaterialDivider
android:layout_width="1dp" android:layout_width="1dp"
android:layout_height="24dp" android:layout_height="16dp"
android:layout_marginHorizontal="8dp" android:layout_marginHorizontal="4dp"
app:dividerColor="@color/black" /> app:dividerColor="@color/black" />
<TextView <TextView
@@ -37,24 +48,18 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="QUARTE + TIERCE" android:text="QUARTE + TIERCE"
android:textSize="16sp" android:textSize="12sp"
android:gravity="end" android:gravity="end"
android:padding="4dp" /> android:padding="2dp" />
</LinearLayout> </LinearLayout>
<com.google.android.material.divider.MaterialDivider <!-- Deuxième ligne : Référence et Type de pari -->
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginBottom="5dp"
app:dividerColor="@color/black"/>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:gravity="center_vertical" android:gravity="center_vertical"
android:layout_marginBottom="3dp"> android:layout_marginTop="2dp">
<TextView <TextView
android:id="@+id/reference_ticket" android:id="@+id/reference_ticket"
@@ -62,14 +67,13 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="12573829288" android:text="12573829288"
android:textSize="16sp" android:textSize="12sp"
android:gravity="start" android:padding="2dp" />
android:padding="4dp" />
<com.google.android.material.divider.MaterialDivider <com.google.android.material.divider.MaterialDivider
android:layout_width="1dp" android:layout_width="1dp"
android:layout_height="24dp" android:layout_height="16dp"
android:layout_marginHorizontal="8dp" android:layout_marginHorizontal="4dp"
app:dividerColor="@color/black" /> app:dividerColor="@color/black" />
<TextView <TextView
@@ -78,25 +82,70 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="TIERCE" android:text="TIERCE"
android:textSize="16sp" android:textSize="13sp"
android:textFontWeight="900" android:textStyle="bold"
android:gravity="end" android:gravity="end"
android:padding="4dp" /> android:padding="2dp" />
</LinearLayout> </LinearLayout>
<!-- Troisième ligne : Chevaux et Mise -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="2dp">
<TextView <TextView
android:id="@+id/last_bet_horses" android:id="@+id/last_bet_horses"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Horses: 3-7-9" android:text="Horses: 3-7-9"
android:textSize="16sp" android:textSize="13sp"
android:layout_marginTop="4dp"/> android:padding="2dp" />
<TextView <TextView
android:id="@+id/last_bet_mise" android:id="@+id/last_bet_mise"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Mise: 500 CFA" android:text="Mise: 500 CFA"
android:textSize="16sp" android:textSize="13sp"
android:layout_marginTop="4dp"/> android:padding="2dp" />
</LinearLayout>
<!-- Séparateur avant le dropdown -->
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginVertical="6dp"
app:dividerColor="@color/black" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<Button
android:id="@+id/btn_annuler"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="8dp"
android:background="@drawable/rounded_button_red"
android:text="Annuler" />
<Button
android:id="@+id/btn_imprimer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:background="@drawable/rounded_button_green"
android:text="Imprimer" />
</LinearLayout>
</LinearLayout> </LinearLayout>

View File

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

View File

@@ -0,0 +1,283 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
app:cardElevation="8dp"
app:cardBackgroundColor="@android:color/white">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<!-- En-tête avec statut -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="16dp">
<TextView
android:id="@+id/tv_statut"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Statut: ENREGISTRÉ"
android:textSize="14sp"
android:textStyle="bold"
android:textColor="@color/primary_green" />
<TextView
android:id="@+id/tv_date_heure"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Date et heure"
android:textSize="12sp"
android:textColor="@color/colorAccent" />
</LinearLayout>
<!-- Séparateur -->
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/login_background"
android:layout_marginBottom="16dp" />
<!-- Numéro de la course -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="12dp">
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Numéro course :"
android:textSize="14sp"
android:textStyle="bold"
android:textColor="@color/primary_green" />
<TextView
android:id="@+id/tv_numero_course"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="5"
android:textSize="14sp"
android:textColor="@color/primary_green" />
</LinearLayout>
<!-- Nom de la course -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="12dp">
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="Nom course :"
android:textSize="14sp"
android:textStyle="bold"
android:textColor="@color/primary_green" />
<TextView
android:id="@+id/tv_nom_course"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Prix de l'Arc de Triomphe"
android:textSize="14sp"
android:textColor="@color/primary_green" />
</LinearLayout>
<!-- Numéro Ticket -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="12dp">
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="N° Ticket :"
android:textSize="14sp"
android:textStyle="bold"
android:textColor="@color/primary_green" />
<TextView
android:id="@+id/tv_numero_ticket"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="T-2024-001234"
android:textSize="14sp"
android:textColor="@color/primary_green"
android:textStyle="bold" />
</LinearLayout>
<!-- Date de prise -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="12dp">
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="Date prise :"
android:textSize="14sp"
android:textStyle="bold"
android:textColor="@color/primary_green" />
<TextView
android:id="@+id/tv_date_prise"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="15/03/2024 14:30"
android:textSize="14sp"
android:textColor="@color/primary_green" />
</LinearLayout>
<!-- Combinaison -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="12dp">
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="Combinaison :"
android:textSize="14sp"
android:textStyle="bold"
android:textColor="@color/primary_green" />
<TextView
android:id="@+id/tv_combinaison"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="5 - 8 - 12 - 15"
android:textSize="14sp"
android:textColor="@color/primary_green"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:id="@+id/notes_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone"
android:layout_marginBottom="12sp"
android:layout_gravity="start">
<TextView
android:id="@+id/notes"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="2 ordres, 6 desordres"
android:textSize="14sp"
android:textColor="@color/primary_green"
android:textStyle="bold" />
</LinearLayout>
<!-- Montant -->
<LinearLayout
android:id="@+id/tv_montant_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="20dp">
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="Montant :"
android:textSize="14sp"
android:textStyle="bold"
android:textColor="@color/primary_green" />
<TextView
android:id="@+id/tv_montant"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="1 000 FCFA"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="@color/primary_green" />
</LinearLayout>
<!-- Séparateur -->
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/background_gray"
android:layout_marginBottom="16dp" />
<!-- Message dynamique selon statut -->
<TextView
android:id="@+id/tv_message_statut"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Veuillez confirmer l'enregistrement de ce pari"
android:textSize="14sp"
android:textColor="@color/primary_green"
android:gravity="center"
android:layout_marginBottom="20dp"
android:padding="5dp"
android:background="@color/green_opacity_30"
android:visibility="visible" />
<!-- Boutons Confirmer et Annuler -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_annuler"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1"
android:layout_marginEnd="8dp"
android:text="Annuler"
android:textSize="14sp"
app:cornerRadius="8dp"
style="@style/Widget.Material3.Button.OutlinedButton" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_confirmer"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:text="Confirmer"
android:textSize="14sp"
app:cornerRadius="8dp"
style="@style/Widget.Material3.Button" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>

View File

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

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" /> <background android:drawable="@color/white" />
<foreground android:drawable="@drawable/ic_launcher_foreground" /> <foreground android:drawable="@drawable/logo_svg" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" /> <monochrome android:drawable="@drawable/logo_svg" />
</adaptive-icon> </adaptive-icon>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" /> <background android:drawable="@color/white"/>
<foreground android:drawable="@drawable/ic_launcher_foreground" /> <foreground android:drawable="@drawable/logo_svg"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground" /> <monochrome android:drawable="@drawable/logo_svg" />
</adaptive-icon> </adaptive-icon>

View File

@@ -4,4 +4,9 @@
<item>Espèce</item> <item>Espèce</item>
<item>Orange Money</item> <item>Orange Money</item>
</string-array> </string-array>
<string-array name="action_options">
<item>Imprimer</item>
<item>Annuler</item>
</string-array>
</resources> </resources>

View File

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

View File

@@ -14,12 +14,16 @@ navigationUi = "2.6.0"
googleAndroidLibrariesMapsplatformSecretsGradlePlugin = "2.0.1" googleAndroidLibrariesMapsplatformSecretsGradlePlugin = "2.0.1"
okhttp = "4.12.0" okhttp = "4.12.0"
playServicesMaps = "18.1.0" playServicesMaps = "18.1.0"
printerlibrary = "1.0.23"
recyclerviewV7 = "28.0.0" recyclerviewV7 = "28.0.0"
retrofit = "2.11.0" retrofit = "2.11.0"
rxandroid = "1.2.1" rxandroid = "1.2.1"
rxandroidVersion = "2.1.1"
rxjava = "1.3.8" rxjava = "1.3.8"
kotlin = "1.9.24" kotlin = "1.9.24"
coreKtx = "1.17.0" coreKtx = "1.17.0"
rxjavaVersion = "2.2.21"
stompprotocolandroid = "1.6.6"
swiperefreshlayout = "1.1.0" swiperefreshlayout = "1.1.0"
core = "1.5.0" core = "1.5.0"
fragmentTesting = "1.6.2" fragmentTesting = "1.6.2"
@@ -52,6 +56,7 @@ navigation-fragment = { group = "androidx.navigation", name = "navigation-fragme
navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" } navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
play-services-maps = { group = "com.google.android.gms", name = "play-services-maps", version.ref = "playServicesMaps" } play-services-maps = { group = "com.google.android.gms", name = "play-services-maps", version.ref = "playServicesMaps" }
printerlibrary = { module = "com.sunmi:printerlibrary", version.ref = "printerlibrary" }
recyclerview-v7 = { module = "com.android.support:recyclerview-v7", version.ref = "recyclerviewV7" } recyclerview-v7 = { module = "com.android.support:recyclerview-v7", version.ref = "recyclerviewV7" }
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomCommonJvm" } room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomCommonJvm" }
@@ -59,6 +64,9 @@ room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomCommo
rxandroid = { module = "io.reactivex:rxandroid", version.ref = "rxandroid" } rxandroid = { module = "io.reactivex:rxandroid", version.ref = "rxandroid" }
rxjava = { module = "io.reactivex:rxjava", version.ref = "rxjava" } rxjava = { module = "io.reactivex:rxjava", version.ref = "rxjava" }
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
rxjava2-rxandroid = { module = "io.reactivex.rxjava2:rxandroid", version.ref = "rxandroidVersion" }
stompprotocolandroid = { module = "com.github.NaikSoftware:StompProtocolAndroid", version.ref = "stompprotocolandroid" }
rxjava2-rxjava = { module = "io.reactivex.rxjava2:rxjava", version.ref = "rxjavaVersion" }
swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swiperefreshlayout" } swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swiperefreshlayout" }
core = { module = "androidx.test:core", version.ref = "core" } core = { module = "androidx.test:core", version.ref = "core" }
fragment-testing = { module = "androidx.fragment:fragment-testing", version.ref = "fragmentTesting" } fragment-testing = { module = "androidx.fragment:fragment-testing", version.ref = "fragmentTesting" }

BIN
key_store.jks Normal file

Binary file not shown.

View File

@@ -25,6 +25,7 @@ import com.anggastudio.printama.constants.PW;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
/** /**
* Printama is a lightweight Android library for printing text and bitmaps to ESC/POS Bluetooth * Printama is a lightweight Android library for printing text and bitmaps to ESC/POS Bluetooth
@@ -280,6 +281,8 @@ public class Printama {
Pref.setBoolean(Pref.IS_PRINTER_3INCH, is3inches); Pref.setBoolean(Pref.IS_PRINTER_3INCH, is3inches);
} }
public static boolean is3inchesPrinter() { public static boolean is3inchesPrinter() {
return Pref.getBoolean(Pref.IS_PRINTER_3INCH); return Pref.getBoolean(Pref.IS_PRINTER_3INCH);
} }
@@ -311,6 +314,27 @@ public class Printama {
}); });
} }
public void testAllPaperStatusCommands(){
_printama.connect(printama -> {
_util.testAllPaperStatusCommands();
});
}
public void checkPaperStatus(Consumer<String> status){
_printama.connect(printama -> {
String paperStatus = _util.checkPaperStatus();
status.accept(paperStatus);
});
}
public void isQueued(Consumer<Integer> status){
_printama.connect(printama -> {
int isQueued = _util.isQueued();
status.accept(isQueued);
});
}
public boolean isConnected() { public boolean isConnected() {
return _util.isConnected(); return _util.isConnected();
} }
@@ -356,8 +380,10 @@ public class Printama {
} }
public void printTextBuilder(StringBuilder text, Bitmap bitmap, String numeroTicket,Bitmap barCode){
public void printTextBuilder(StringBuilder text, Bitmap bitmap, String numeroTicket, Bitmap barCode, PrintCallback callback) {
_printama.connect(printama -> { _printama.connect(printama -> {
try {
_util.resetPrinter(); _util.resetPrinter();
_util.setBold(); _util.setBold();
_util.printImage(bitmap); _util.printImage(bitmap);
@@ -380,6 +406,45 @@ public class Printama {
_util.setAlign(PA.CENTER); _util.setAlign(PA.CENTER);
_util.printText("Powered by PMU-MALI"); _util.printText("Powered by PMU-MALI");
_util.printText("\n" + printama.lineSeparator() + "\n"); _util.printText("\n" + printama.lineSeparator() + "\n");
_util.printText("\n\n\n");
printama.feedPaper();
printama.close();
if (callback != null) callback.onResult(true, null);
} catch (Exception e) {
if (callback != null) callback.onResult(false, e.getMessage());
e.printStackTrace();
}
}, error -> {
if (callback != null) callback.onResult(false, error);
}
);
}
public interface PrintCallback{
void onResult(boolean success, String errorMessage);
}
public void printSold(Bitmap bitmap, String title, StringBuilder text){
_printama.connect(printama -> {
_util.resetPrinter();
_util.setBold();
_util.printImage(bitmap);
_util.printText(lineSeparator());
_util.setAlign(PA.CENTER);
_util.setBold();
_util.printText(title+"\n");
_util.setNormalText();
_util.printText(lineSeparator()+"\n");
_util.setNormalText();
_util.setAlign(PA.LEFT);
_util.printText(text.toString());
_util.printText(printama.lineSeparator()+"\n");
_util.setBold();
_util.setNormalText();
_util.setAlign(PA.CENTER);
_util.printText("Powered by PMU-MALI");
_util.printText("\n"+printama.lineSeparator()+"\n");
_util.printText("\n\n\n");
printama.feedPaper(); printama.feedPaper();
printama.close(); printama.close();
}); });
@@ -522,7 +587,19 @@ public class Printama {
//---------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------
public void printText(String text) { public void printText(String text) {
printText(text, PA.LEFT); _printama.connect((printama)->{
_util.printText(text);
});
}
public void myPrintText(String text, Consumer<String> callback) {
_printama.connect(printama -> {
if(_util.printText(text)){
callback.accept("L'impression a été effectuée avec succès");
}else{
callback.accept("L'impression a échoué");
}
});
} }
/** /**
@@ -676,6 +753,8 @@ public class Printama {
return spaces.toString(); return spaces.toString();
} }
//---------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------
// COLUMN FORMATTERS // COLUMN FORMATTERS
//---------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------

View File

@@ -17,6 +17,7 @@ import com.anggastudio.printama.constants.PW;
import com.anggastudio.printama.util.StrUtil; import com.anggastudio.printama.util.StrUtil;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Arrays; import java.util.Arrays;
import java.util.UUID; import java.util.UUID;
@@ -54,6 +55,7 @@ class PrinterUtil {
private final BluetoothDevice printer; private final BluetoothDevice printer;
private BluetoothSocket btSocket = null; private BluetoothSocket btSocket = null;
private OutputStream btOutputStream = null; private OutputStream btOutputStream = null;
private InputStream btInputStream = null;
private boolean is3InchPrinter; private boolean is3InchPrinter;
PrinterUtil(BluetoothDevice printer) { PrinterUtil(BluetoothDevice printer) {
@@ -67,6 +69,7 @@ class PrinterUtil {
btSocket = socket; btSocket = socket;
try { try {
btOutputStream = socket.getOutputStream(); btOutputStream = socket.getOutputStream();
btInputStream = socket.getInputStream();
successListener.onConnected(); successListener.onConnected();
} catch (IOException e) { } catch (IOException e) {
failedListener.onFailed(); failedListener.onFailed();
@@ -81,7 +84,26 @@ class PrinterUtil {
} }
boolean isConnected() { boolean isConnected() {
return btSocket != null && btSocket.isConnected(); if (btSocket == null) {
return false;
}
try {
// Tester la connexion en vérifiant le flux d'entrée
if (btSocket.isConnected()) {
// Vérifier si le flux est accessible
InputStream inputStream = btSocket.getInputStream();
// Tenter de lire avec un timeout très court
if (inputStream.available() >= 0) {
return true;
}
}
return false;
} catch (IOException e) {
// Exception signifie que la connexion est perdue
Log.e("Bluetooth", "Socket non connecté: " + e.getMessage());
return false;
}
} }
void finish() { void finish() {
@@ -122,12 +144,25 @@ class PrinterUtil {
String s = StrUtil.encodeNonAscii(text); String s = StrUtil.encodeNonAscii(text);
btOutputStream.write(s.getBytes()); btOutputStream.write(s.getBytes());
return true; return true;
} catch (IOException e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
return false; return false;
} }
} }
String textTest(String text){
try{
String s = StrUtil.encodeNonAscii(text);
btOutputStream.write(s.getBytes());
btOutputStream.flush();
int available = btInputStream.available();
return String.valueOf(available);
}catch (Exception e){
e.printStackTrace();
return "Erreur: "+e.getMessage();
}
}
boolean printTextBuilder(StringBuilder text){ boolean printTextBuilder(StringBuilder text){
try { try {
String tspl = ""+ text + "\r\n"; String tspl = ""+ text + "\r\n";
@@ -140,6 +175,61 @@ class PrinterUtil {
} }
} }
/**
* Vérifie si l'imprimante a une opération en attente ou en cours
*
* @return
* 0 = Aucune opération (imprimante prête)
* 1 = Opération en cours d'impression
* 2 = Opération en attente (buffer plein)
* -1 = Erreur / impossible de déterminer
*/
public int isQueued() {
try {
// Vider le buffer d'entrée avant d'envoyer la commande
while (btInputStream != null && btInputStream.available() > 0) {
btInputStream.read();
}
// Commande DLE EOT 1 - Statut en temps réel
byte[] command = new byte[]{0x10, 0x04, 0x01};
btOutputStream.write(command);
btOutputStream.flush();
// Attendre 100ms pour la réponse
Thread.sleep(100);
// Lire la réponse (1 octet)
if (btInputStream != null && btInputStream.available() > 0) {
byte[] response = new byte[1];
int bytesRead = btInputStream.read(response);
if (bytesRead > 0) {
byte status = response[0];
// Bit 1 (0x02) = Imprimante en cours d'impression
if ((status & 0x02) != 0) {
return 1; // Opération en cours
}
// Bit 3 (0x08) = Buffer plein / attente
if ((status & 0x08) != 0) {
return 2; // Opération en attente
}
return 0; // Aucune opération
}
}
// Pas de réponse
return -1;
} catch (Exception e) {
Log.e(TAG, "Erreur isQueued: " + e.getMessage());
return -1;
}
}
void setNormalizedForAll(){ void setNormalizedForAll(){
printUnicode(NORMALIZED_FOR_ALL); printUnicode(NORMALIZED_FOR_ALL);
} }
@@ -243,6 +333,191 @@ class PrinterUtil {
} }
} }
public void testAllPaperStatusCommands() {
// Liste des commandes à tester
int[][] commands = {
{0x1D, 0x72, 0x01}, // GS r 1 - Standard paper status
{0x10, 0x04, 0x01}, // DLE EOT 1 - Real-time status
{0x10, 0x04, 0x04}, // DLE EOT 4 - Paper sensor status
{0x1B, 0x76, 0x00}, // ESC v 0 - Paper sensor
{0x1D, 0x61, 0x01}, // GS a 1 - Enable real-time mode
{0x1D, 0x49, 0x01}, // GS I 1 - Printer ID
{0x1B, 0x69, 0x00} // ESC i 0 - Printer status
};
for (int i = 0; i < commands.length; i++) {
try {
Log.e("TEST", "========================================");
Log.e("TEST", "Test commande " + i + ": " + bytesToHex(commands[i]));
// Nettoyer buffer
while (btInputStream.available() > 0) {
btInputStream.read();
}
// Envoyer commande
byte[] cmd = new byte[commands[i].length];
for (int j = 0; j < commands[i].length; j++) {
cmd[j] = (byte) commands[i][j];
}
btOutputStream.write(cmd);
btOutputStream.flush();
// Attendre réponse
Thread.sleep(300);
// Lire réponse
byte[] response = new byte[16];
int totalRead = 0;
long startTime = System.currentTimeMillis();
while (totalRead < response.length && System.currentTimeMillis() - startTime < 1000) {
if (btInputStream.available() > 0) {
int read = btInputStream.read(response, totalRead, response.length - totalRead);
if (read > 0) {
totalRead += read;
}
}
Thread.sleep(20);
}
if (totalRead > 0) {
Log.e("TEST", "✓ RÉPONSE REÇUE ! " + totalRead + " octets");
StringBuilder hex = new StringBuilder();
for (int j = 0; j < totalRead; j++) {
hex.append(String.format("%02X ", response[j]));
}
Log.e("TEST", " Hex: " + hex.toString());
// Interpréter la réponse selon la commande
if (i == 0 && totalRead >= 1) { // GS r 1
interpretGS_r1(response[0]);
} else if (i == 1 && totalRead >= 1) { // DLE EOT 1
interpretDLE_EOT_1(response[0]);
} else if (i == 2 && totalRead >= 1) { // DLE EOT 4
interpretDLE_EOT_4(response[0]);
} else if (i == 5 && totalRead >= 1) { // GS I 1
Log.e("TEST", " ID Imprimante: " + new String(response, 0, totalRead));
}
} else {
Log.e("TEST", "✗ AUCUNE RÉPONSE");
}
} catch (Exception e) {
Log.e("TEST", "Erreur commande " + i, e);
}
}
}
private void interpretGS_r1(byte status) {
Log.e("TEST", "Interprétation GS r 1:");
Log.e("TEST", " Bit 0 (couvercle): " + ((status & 0x01) != 0 ? "OUVERT" : "FERMÉ"));
Log.e("TEST", " Bit 2 (papier): " + ((status & 0x04) != 0 ? "FAIBLE" : "OK"));
Log.e("TEST", " Bit 5 (papier): " + ((status & 0x20) != 0 ? "VIDE" : "OK"));
}
private void interpretDLE_EOT_1(byte status) {
Log.e("TEST", "Interprétation DLE EOT 1:");
Log.e("TEST", " Bit 2 (papier): " + ((status & 0x04) != 0 ? "VIDE" : "OK"));
}
private void interpretDLE_EOT_4(byte status) {
Log.e("TEST", "Interprétation DLE EOT 4:");
Log.e("TEST", " Bit 0 (couvercle): " + ((status & 0x01) != 0 ? "OUVERT" : "FERMÉ"));
Log.e("TEST", " Bit 2 (papier): " + ((status & 0x04) != 0 ? "FAIBLE" : "OK"));
Log.e("TEST", " Bit 5 (papier): " + ((status & 0x20) != 0 ? "VIDE" : "OK"));
}
private String bytesToHex(int[] bytes) {
StringBuilder sb = new StringBuilder();
for (int b : bytes) {
sb.append(String.format("%02X ", b));
}
return sb.toString();
}
public String getPrinterPaperStatus() {
try{
byte[] command = new byte[]{0x10, 0x04, 0x04};
btOutputStream.write(command);
btOutputStream.flush();
Thread.sleep(300);
int status = btInputStream.read();
return String.valueOf(status);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String checkPaperStatus() {
try {
// 1. Envoyer la commande d'activation ASB
byte[] cmd = {0x1D, 0x61, (byte) 0xFF};
btOutputStream.write(cmd);
btOutputStream.flush();
// 2. Attendre un peu plus longtemps pour la réponse
Thread.sleep(500);
// 3. Lire tout ce qui est disponible dans le buffer
int available = btInputStream.available();
if (available > 0) {
byte[] buffer = new byte[available];
btInputStream.read(buffer);
// On convertit le buffer en une chaîne lisible (Hex)
StringBuilder sb = new StringBuilder();
for (byte b : buffer) {
sb.append(String.format("%02X ", b));
}
return "Status: " + sb.toString();
} else {
return "0 (Aucune réponse)";
}
} catch (Exception e) {
return "Erreur: " + e.getMessage();
}
}
private int readStatusByte(int timeoutMs) throws IOException, InterruptedException {
long start = System.currentTimeMillis();
// petite attente initiale (important en Bluetooth)
Thread.sleep(50);
while (System.currentTimeMillis() - start < timeoutMs) {
int available = btInputStream.available();
Log.e("AVAILABLE", "STATUS: "+String.valueOf(available) );
if (available > 0) {
byte[] buffer = new byte[available];
int read = btInputStream.read(buffer);
if (read > 0) {
int value = buffer[0] & 0xFF;
Log.d("Printama", "Raw bytes: " + bytesToHex(buffer, read));
return value; // on prend le premier byte
}
}
Thread.sleep(20);
}
return -1;
}
private String bytesToHex(byte[] bytes, int length) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++) {
sb.append(String.format("%02X ", bytes[i]));
}
return sb.toString();
}
boolean printImage(Bitmap bitmap, int width) { boolean printImage(Bitmap bitmap, int width) {
return printImage(PA.CENTER, bitmap, width); return printImage(PA.CENTER, bitmap, width);
} }

View File

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

View File

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

View File

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