Compare commits

..

10 Commits

Author SHA1 Message Date
OnlyPapy98
acc5ec1b70 gestion des formats de tickets! 2026-02-16 10:16:49 +01:00
OnlyPapy98
2fc21fd433 correction on mise calculation 2025-12-16 16:22:43 +01:00
OnlyPapy98
b3d94ad038 70% of features done! 2025-12-12 17:55:12 +01:00
OnlyPapy98
dc93d1320f in progress! 2025-12-05 18:08:01 +01:00
OnlyPapy98
ed92a63015 print successfully integrated! 2025-12-03 09:41:46 +01:00
OnlyPapy98
87a3e952aa new printer integration! 2025-11-28 16:35:53 +01:00
OnlyPapy98
1031307b3a break for printer! 2025-11-27 11:54:52 +01:00
OnlyPapy98
8f80492287 create pari 2025-11-18 18:03:02 +01:00
OnlyPapy98
b1ce91ef20 http request call done 2025-11-12 16:52:48 +01:00
OnlyPapy98
159837bd6f type of game step done! 2025-10-31 16:17:15 +01:00
219 changed files with 17290 additions and 1274 deletions

2
.idea/compiler.xml generated
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="CompilerConfiguration"> <component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" /> <bytecodeTargetLevel target="17" />
</component> </component>
</project> </project>

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="2025-09-29T14:58:34.998303700Z"> <DropdownSelection timestamp="2026-02-13T13:45:19.610071900Z">
<Target type="DEFAULT_BOOT"> <Target type="DEFAULT_BOOT">
<handle> <handle>
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\T480\.android\avd\Pixel_7_API_34.avd" /> <DeviceId pluginId="PhysicalDevice" identifier="serial=5051918437" />
</handle> </handle>
</Target> </Target>
</DropdownSelection> </DropdownSelection>

3
.idea/gradle.xml generated
View File

@@ -6,11 +6,12 @@
<GradleProjectSettings> <GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" /> <option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" /> <option name="gradleJvm" value="17" />
<option name="modules"> <option name="modules">
<set> <set>
<option value="$PROJECT_DIR$" /> <option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" /> <option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/printama" />
</set> </set>
</option> </option>
<option name="resolveExternalAnnotations" value="false" /> <option name="resolveExternalAnnotations" value="false" />

View File

@@ -0,0 +1,8 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true">
<option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,java.io.PrintStream,printf,java.lang.foreign.Arena,ofAuto,java.lang.foreign.Arena,global,retrofit2.Response,errorBody" />
</inspection_tool>
</profile>
</component>

6
.idea/kotlinc.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.24" />
</component>
</project>

2
.idea/misc.xml generated
View File

@@ -1,6 +1,6 @@
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@@ -2,6 +2,7 @@ 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)
id("com.google.dagger.hilt.android") id("com.google.dagger.hilt.android")
alias(libs.plugins.kotlin.android)
} }
android { android {
@@ -34,10 +35,26 @@ android {
buildFeatures { buildFeatures {
viewBinding = true viewBinding = true
} }
kotlinOptions {
jvmTarget = "17"
}
} }
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)
annotationProcessor(libs.room.compiler)
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.camera:camera-camera2:1.2.3")
implementation("androidx.camera:camera-lifecycle:1.2.3")
implementation("androidx.camera:camera-view:1.2.3")
implementation("androidx.camera:camera-core:1.2.3")
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) implementation(libs.hilt.android)
annotationProcessor(libs.hilt.compiler) annotationProcessor(libs.hilt.compiler)
implementation(libs.rxjava) implementation(libs.rxjava)
@@ -45,10 +62,13 @@ dependencies {
implementation(libs.recyclerview.v7) implementation(libs.recyclerview.v7)
implementation(libs.appcompat) implementation(libs.appcompat)
implementation(libs.material) implementation(libs.material)
implementation(libs.google.core)
implementation(libs.zxing.android.embedded)
implementation(libs.constraintlayout) implementation(libs.constraintlayout)
implementation(libs.navigation.fragment) implementation(libs.navigation.fragment)
implementation(libs.navigation.ui) implementation(libs.navigation.ui)
implementation(libs.play.services.maps) implementation(libs.play.services.maps)
implementation(project(":printama"))
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit) androidTestImplementation(libs.ext.junit)

Binary file not shown.

View File

@@ -2,10 +2,41 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.BLUETOOTH"/> <!-- ✅ Bluetooth Permissions (Handle Both Legacy and Modern) -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.CAMERA" />
<!-- ✅ Required for Android 12+ -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<!-- ✅ Location Permissions -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- ✅ Storage and Network -->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
<!-- ✅ USB and Hardware -->
<uses-feature android:name="android.hardware.usb.host" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<application <application
android:name=".PmuHorseBetting" android:name=".PmuHorseBetting"
@@ -17,7 +48,8 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.Quiz" android:theme="@style/Theme.Quiz"
tools:targetApi="31"> tools:targetApi="31"
android:usesCleartextTraffic="true">
<activity <activity
android:name=".PageQuiz" android:name=".PageQuiz"
android:exported="true" android:exported="true"
@@ -37,6 +69,7 @@
android:name="com.google.android.geo.API_KEY" android:name="com.google.android.geo.API_KEY"
android:value="YOUR_API_KEY" /> android:value="YOUR_API_KEY" />
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 KiB

View File

@@ -0,0 +1,128 @@
package com.example.quiz;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.example.quiz.data.model.ParisResponse;
import com.example.quiz.databinding.FragmentAnnulationTicketBinding;
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 dagger.hilt.android.AndroidEntryPoint;
/**
* A simple {@link Fragment} subclass.
* Use the {@link AnnulationTicket#newInstance} factory method to
* create an instance of this fragment.
*/
@AndroidEntryPoint
public class AnnulationTicket extends Fragment {
FragmentAnnulationTicketBinding binding;
SharedPrefsHelper prefsHelper;
LogsViewModel logsViewModel;
PariViewModel viewModel;
LoaderDialog dialog;
public AnnulationTicket() {
// Required empty public constructor
}
public static AnnulationTicket newInstance() {
AnnulationTicket fragment = new AnnulationTicket();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentAnnulationTicketBinding.inflate(inflater, container, false);
prefsHelper = SharedPrefsHelper.getInstance(getContext());
logsViewModel = new ViewModelProvider(this).get(LogsViewModel.class);
dialog = new LoaderDialog(getContext());
return binding.getRoot();
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
viewModel = new ViewModelProvider(this).get(PariViewModel.class);
binding.annuleTicketBtnBack.setOnClickListener(v->{
getActivity().onBackPressed();
});
binding.scanTicketBtn.setOnClickListener(v -> {
ScannerDialog scannerDialog = new ScannerDialog();
scannerDialog.setOnBarcodeScannedListener(code -> {
binding.referenceTicket.setText(code);
});
scannerDialog.show(getParentFragmentManager(), "scanner");
});
binding.annuleTicketBtn.setOnClickListener(v->{
String reference = binding.referenceTicket.getText().toString();
if(reference.isEmpty()){
Toast.makeText(getContext(),"Entrez la référence du ticket", Toast.LENGTH_SHORT).show();
return;
}
viewModel.annulerPari(reference).observe(getViewLifecycleOwner(), new Observer<Result<ParisResponse>>() {
@Override
public void onChanged(Result<ParisResponse> pariResult) {
switch (pariResult.status){
case LOADING:{
dialog.show("Annulation du ticket");
break;
}
case ERROR:{
dialog.dismiss();
MessageDialog.showError(getContext(), pariResult.message);
break;
}
case SUCCESS:{
dialog.dismiss();
MessageDialog.showSuccess(getContext(), "Ticket annulé avec succès");
logsViewModel.insertLog(prefsHelper.get("id"), "ANNULATION TICKET", "Annulation du ticket "+pariResult.data.getNumeroTicket(), System.currentTimeMillis());
binding.referenceTicket.setText("");
}
}
}
});
});
}
@Override
public void onResume() {
super.onResume();
AppCompatActivity activity = (AppCompatActivity) getActivity();
if(activity != null){
activity.getSupportActionBar().setTitle("Annulation Ticket");
}
}
}

View File

@@ -1,27 +1,9 @@
package com.example.quiz; package com.example.quiz;
import com.example.quiz.data.BetsBank; import android.app.Application;
import com.example.quiz.data.repository.BetRepository;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import dagger.hilt.InstallIn;
import dagger.hilt.components.SingletonComponent;
@Module public class AppModule extends Application{
@InstallIn(SingletonComponent.class)
public class AppModule {
@Provides
@Singleton
public BetsBank provideApiService(){
return BetsBank.getInstance();
}
@Provides
@Singleton
public BetRepository provideBetRepository() {
return new BetRepository();
}
} }

View File

@@ -1,32 +1,74 @@
package com.example.quiz; package com.example.quiz;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.content.Intent;
import android.content.pm.PackageManager;
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.activity.result.ActivityResultLauncher;
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.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.Observer;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log; import android.util.Log;
import android.view.Gravity; import android.view.Gravity;
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.GridLayout; import android.widget.GridLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.example.quiz.data.model.Horse; import com.anggastudio.printama.Printama;
import com.example.quiz.data.model.Course;
import com.example.quiz.data.model.Pari;
import com.example.quiz.data.model.PariMise;
import com.example.quiz.data.model.ParisResponse;
import com.example.quiz.data.model.Reunion;
import com.example.quiz.data.model.TypeOfBet;
import com.example.quiz.data.model.dtos.PariCourseDto;
import com.example.quiz.data.model.enums.PariStatut;
import com.example.quiz.databinding.FragmentBetValidationBinding; import com.example.quiz.databinding.FragmentBetValidationBinding;
import com.example.quiz.utils.BluetoothUtils;
import com.example.quiz.utils.HPRTPrinterUtil; import com.example.quiz.utils.LoaderDialog;
import com.example.quiz.viewModel.BetViewModel; 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.example.quiz.viewModel.SharedViewModel; import com.example.quiz.viewModel.SharedViewModel;
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.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
/** /**
@@ -42,20 +84,38 @@ public class BetValidation extends Fragment {
SharedViewModel shared; SharedViewModel shared;
BetViewModel viewModel; private AlertDialog dialog;
private int nombreX;
LogsViewModel logsViewModel;
private HPRTPrinterUtil printer; private boolean order;
private Integer id; private long mise;
private String typeOfBet;
private List<Horse> selectedHorses = new ArrayList<Horse>(); private TypeOfBet typeOfBet;
private List<Horse> totalHorses; SharedPrefsHelper prefsHelper;
private Course course;
LoaderDialog loader;
private int coeff;
private ActivityResultLauncher<Intent> enableBluetoothLauncher;
private final MutableLiveData<List<String>> selectedHorses = new MutableLiveData<>(List.of());
PariViewModel pariViewModel;
public BetValidation() { public BetValidation() {
@@ -72,91 +132,519 @@ public class BetValidation extends Fragment {
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
prefsHelper = SharedPrefsHelper.getInstance(getContext());
} }
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
binding = FragmentBetValidationBinding.inflate(inflater, container, false); binding = FragmentBetValidationBinding.inflate(inflater, container, false);
binding.combination.setText(getString(R.string.combination,"")); loader = new LoaderDialog(getContext());
logsViewModel = new ViewModelProvider(this).get(LogsViewModel.class);
binding.coeff.setText(String.valueOf(1));
coeff = Integer.parseInt(binding.coeff.getText().toString());
binding.coeff.addTextChangedListener(new TextWatcher(){
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if(charSequence.toString().isEmpty()){
binding.coeff.setError("Le coefficient est obligatoire");
return;
}
if(Integer.parseInt(charSequence.toString())<1){
binding.coeff.setError("Le coefficient doit être supérieur ou égal à 1 ");
return;
}
coeff = Integer.parseInt(charSequence.toString());
calculateMise(selectedHorses.getValue());
}
@Override
public void afterTextChanged(Editable editable) {
}
});
return binding.getRoot(); return binding.getRoot();
} }
private void setupNumberGrid(GridLayout grid) { private void setupNumberGrid(GridLayout grid) {
binding.gridNumbers.removeAllViews();
int columns = 4; int columns = 8;
grid.setColumnCount(columns); grid.setColumnCount(columns);
if(shared.selectedCourse.getValue() != null){
if(totalHorses != null){ for (int i = 1; i <= shared.selectedCourse.getValue().getNombrePartants(); i++) {
totalHorses.stream() createNumberItem(String.valueOf(i));
.map(this::createNumberItem) grid.addView(createNumberItem(String.valueOf(i)));
.forEach(grid::addView);
} }
} }
createNumberItem("X");
grid.addView(createNumberItem("X"));
}
private TextView createNumberItem(Horse horse) { private TextView createNumberItem(String horse) {
TextView textView = new TextView(requireContext()); TextView textView = new TextView(requireContext());
textView.setText(String.valueOf(horse.getNumber())); textView.setText(horse);
textView.setTextColor(getResources().getColor(R.color.white)); textView.setTextColor(getResources().getColor(R.color.white));
GridLayout.LayoutParams params = new GridLayout.LayoutParams(); GridLayout.LayoutParams params = new GridLayout.LayoutParams();
params.setMargins(10, 10, 10, 10); params.setMargins(1, 1, 1, 1);
textView.setLayoutParams(params); textView.setLayoutParams(params);
textView.setTextSize(21); textView.setTextSize(10);
textView.setWidth(130); textView.setWidth(80);
textView.setHeight(130); textView.setHeight(100);
textView.setGravity(Gravity.CENTER); textView.setGravity(Gravity.CENTER);
textView.setBackgroundResource(R.drawable.number_background); textView.setBackgroundResource(Objects.equals(horse, "X") ?R.drawable.x_background: R.drawable.number_background);
if(!Objects.equals(horse, "X") && shared.selectedCourse.getValue().getNonPartants() != null && shared.selectedCourse.getValue().getNonPartants().contains(Integer.valueOf(horse))){
textView.setTextColor(getResources().getColor(R.color.white));
textView.setBackgroundResource(R.drawable.number_background_grey);
}
textView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); textView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
textView.setOnClickListener(v -> { textView.setOnClickListener(v -> {
if (selectedHorses.contains(horse)) { if(!Objects.equals(horse, "X") && shared.selectedCourse.getValue().getNonPartants() != null && shared.selectedCourse.getValue().getNonPartants().contains(Integer.valueOf(horse))){
selectedHorses.remove(horse); Toast.makeText(getContext(), "Ce cheval n'est pas partant", Toast.LENGTH_SHORT).show();
return;
}
final List<String> horses = new ArrayList<>(selectedHorses.getValue());
if(horses.size() >= shared.typeOfBet.getValue().getNumberOfHorse() && horse.equals("X")){
Toast.makeText(getContext(), "Vous ne pouvez plus sélectionner X", Toast.LENGTH_SHORT).show();
return;
}
if(_notClickable(horses) && horse.equals("X")){
Toast.makeText(getContext(), "Vous ne pouvez plus sélectionner X", Toast.LENGTH_SHORT).show();
return;
}
if (horses.contains(horse) && !horse.equals("X")) {
horses.remove(horse);
selectedHorses.setValue(horses);
v.setSelected(false); v.setSelected(false);
v.setBackgroundResource(R.drawable.number_background); v.setBackgroundResource(R.drawable.number_background);
} else { } else {
selectedHorses.add(horse); horses.add(horse);
selectedHorses.setValue(horses);
v.setSelected(true); v.setSelected(true);
v.setBackgroundResource(R.drawable.number_selected_background); v.setBackgroundResource(R.drawable.number_selected_background);
} }
String combinationText = selectedHorses.stream() String combinationText = selectedHorses.getValue().stream()
.map(h -> String.valueOf(h.getNumber())) .map(h -> h)
.collect(Collectors.joining("-")); .collect(Collectors.joining("-"));
binding.combination.setText(getString(R.string.combination, combinationText)); binding.combination.setText( combinationText);
}); });
return textView; return textView;
} }
boolean _notClickable(List<String> selectedHorses){
int numberOfElement = 0;
if(shared.typeOfBet.getValue().getNumberOfHorse() < 2){
return true;
}
for(String horse: selectedHorses){
numberOfElement = horse.equals("X")?numberOfElement+1:numberOfElement;
}
nombreX = numberOfElement;
return numberOfElement == shared.typeOfBet.getValue().getNumberOfHorse() - 1;
}
@Override @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);
shared = new ViewModelProvider(requireActivity()).get(SharedViewModel.class); shared = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
viewModel = new ViewModelProvider(this).get(BetViewModel.class); setSelectedTypeOfBetImage();
shared.betId.observe(getViewLifecycleOwner(), id ->{ pariViewModel = new ViewModelProvider(this).get(PariViewModel.class);
this.id = id; typeOfBet = shared.typeOfBet.getValue();
viewModel.getBetNameById(id - 1); course = shared.selectedCourse.getValue();
viewModel.betName.observe(getViewLifecycleOwner(), name ->{ AppCompatActivity activity = (AppCompatActivity) getActivity();
binding.horseName.setText(name); if(activity != null){
}); MaterialToolbar toolbar = activity.findViewById(R.id.toolbar);
viewModel.loadHorses(id - 1); toolbar.setBackgroundColor( getResources().getColor(R.color.primary_green));
viewModel.horses.observe(getViewLifecycleOwner(), h->{ activity.setSupportActionBar(toolbar);
this.totalHorses = h; if(activity.getSupportActionBar() != null){
activity.getSupportActionBar().setTitle("Pari "+shared.selectedCourse.getValue().getNom());
}
}
setupNumberGrid(binding.gridNumbers); setupNumberGrid(binding.gridNumbers);
}); binding.paymentType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
}); @Override
shared.typeOfBet.observe(getViewLifecycleOwner(), type ->{ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
this.typeOfBet = type; String selected = binding.paymentType.getSelectedItem().toString();
});
binding.btnValidate.setOnClickListener(v->{ if (selected.equals("Orange Money")) {
//Toast.makeText(getContext(), "L'id de bet: "+this.id+" et le type est: "+this.typeOfBet, Toast.LENGTH_SHORT).show(); binding.phoneNumber.setVisibility(View.VISIBLE);
printer = new HPRTPrinterUtil(getContext()); } else {
boolean ok = printer.connectBluetooth("02:03:00:00:00:00"); binding.phoneNumber.setVisibility(View.GONE); // ou GONE
if (ok) { binding.phoneNumber.setText(""); // vider le champ si non utilisé
printer.printText("Bonjour"); }
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
binding.phoneNumber.setVisibility(View.INVISIBLE);
} }
}); });
selectedHorses.observe(getViewLifecycleOwner(), new Observer<List<String>>() {
@Override
public void onChanged(List<String> horses) {
calculateMise(horses);
if(shared.typeOfBet.getValue().getNumberOfHorse() > 2 && horses.size() >= shared.typeOfBet.getValue().getNumberOfHorse()){
binding.order.setVisibility(View.VISIBLE);
}else{
binding.order.setVisibility(View.GONE);
} }
if(horses.contains("X")){
if(selectedHorses.getValue().size() == shared.typeOfBet.getValue().getNumberOfHorse()){
binding.elargie.setVisibility(View.VISIBLE);
binding.elargie.setText("Champ total");
return;
}else{
if(selectedHorses.getValue().size() > shared.typeOfBet.getValue().getNumberOfHorse()){
binding.elargie.setVisibility(View.VISIBLE);
binding.elargie.setText("Champ partiel");
return;
}
}
}
if(horses.size() > shared.typeOfBet.getValue().getNumberOfHorse()){
binding.elargie.setVisibility(View.VISIBLE);
binding.elargie.setText("Elargi");
}else{
binding.elargie.setVisibility(View.GONE);
}
}
});
binding.order.setOnCheckedChangeListener((buttonView, isChecked) -> {
order = isChecked;
calculateMise(selectedHorses.getValue());
});
binding.betValidateBtn.setOnClickListener(v->{
if(binding.paymentType.getSelectedItem().toString().equals("Orange Money") && binding.phoneNumber.getText().toString().isEmpty()){
Toast.makeText(getContext(), "Veuillez entrer un numéro de téléphone", Toast.LENGTH_SHORT).show();
return;
}
if(shared.typeOfBet == null || shared.selectedCourse == null){
return;
}
int required = typeOfBet.getNumberOfHorse();
if(selectedHorses.getValue().size() < required){
Toast.makeText(getContext(), "Veuillez sélectionner au moins"+required+" chevaux", Toast.LENGTH_SHORT).show();
return;
}
if(this.mise == 0){
Toast.makeText(getContext(), "Pari non valide", Toast.LENGTH_SHORT).show();
return;
}
Pari pari = new Pari(
OffsetDateTime.now(ZoneOffset.UTC).toString(),
Long.valueOf(course.getId()),
Long.valueOf(prefsHelper.get("id")),
Long.valueOf(prefsHelper.get("terminalId")),
Long.valueOf(prefsHelper.get("pointDeVenteId")),
List.of(new PariMise(String.valueOf(shared.typeOfBet.getValue().getName()), mise)),
_getFormule(),
_getCombinaison(),
coeff,
"XOF",
"Pari"
);
_showPariDialog(pari);
});
binding.backBtn.setOnClickListener(v->{
getActivity().onBackPressed();
});
binding.deleteBtn.setOnClickListener(v->{
_initializeToZero();
});
}
String _getCombinaison(){
if(selectedHorses.getValue().contains("X") && shared.typeOfBet.getValue().getNumberOfHorse() < selectedHorses.getValue().size()){
String first = selectedHorses.getValue().subList(0, shared.typeOfBet.getValue().getNumberOfHorse()).stream()
.map(String::valueOf)
.collect(Collectors.joining(","));
String last = selectedHorses.getValue().subList(shared.typeOfBet.getValue().getNumberOfHorse(), selectedHorses.getValue().size()).stream()
.map(String::valueOf)
.collect(Collectors.joining(","));
return first+",R,"+last;
}else{
return selectedHorses.getValue().stream().map(String::valueOf).collect(Collectors.joining(","));
}
}
List<String> _getFormule(){
List<String> combinaison = selectedHorses.getValue();
if(!combinaison.contains("X")){
if(!order){
return List.of("UNITAIRE");
}else{
return List.of("FORMULE_COMPLETE");
}
}else{
if(shared.typeOfBet.getValue().getNumberOfHorse() < selectedHorses.getValue().size()){
if(order){
return List.of("CHAMP_X", "FORMULE_COMPLETE");
}else{
return List.of("CHAMP_X");
}
}else{
if(order){
return List.of("CHAMP_TOTAL", "FORMULE_COMPLETE");
}else{
return List.of("CHAMP_TOTAL");
}
}
}
}
private void setSelectedTypeOfBetImage(){
switch (shared.typeOfBet.getValue().getName()){
case COUPLE_PLACE:
binding.icTypeOfBet.setImageResource(R.drawable.ic_couple_place);
break;
case COUPLE_GAGNANT:
binding.icTypeOfBet.setImageResource(R.drawable.ic_couple_gagnant);
break;
case TIERCE:
binding.icTypeOfBet.setImageResource(R.drawable.ic_tierce);
break;
case QUARTE:
binding.icTypeOfBet.setImageResource(R.drawable.ic_quarte);
break;
case QUINTE:
binding.icTypeOfBet.setImageResource(R.drawable.ic_quinte_plus);
break;
}
}
public void printPari(ParisResponse pari) throws WriterException {
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(shared.selectedCourse.getValue().getNom()).append("\n");
OffsetDateTime dateTime = OffsetDateTime.parse(shared.selectedCourse.getValue().getHeureDepartPrevue());
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
String formattedDate = dateTime.format(formatter);
tspl.append(formattedDate).append("\n");
tspl.append("Course ").append(String.valueOf(shared.selectedCourse.getValue().getId())).append("\n");
tspl.append(shared.selectedCourse.getValue().getTypesParisOuverts().get(0)).append("\n");
tspl.append(printama.lineSeparator()+"\n");
boolean isElargie = selectedHorses.getValue().size()>shared.typeOfBet.getValue().getNumberOfHorse();
String typePari = pari.getTypesParisMises().get(0).getTypePari();
tspl.append(isElargie?typePari+"/Elargie":typePari).append("\n");
tspl.append(order?"COMBINAISON COMPLETE"+"\n":"");
String combinationText = selectedHorses.getValue().stream()
.map(String::valueOf)
.collect(Collectors.joining("-"));
tspl.append(combinationText).append("\n");
tspl.append("COEF: ").append(String.valueOf(coeff)).append(".0");
tspl.append("\n").append(printama.lineSeparator()).append("\n");
tspl.append("MONTANT: ").append(pari.getTypesParisMises().get(0).getMiseTotale()).append(" XOF");
tspl.append("\n").append(printama.lineSeparator()).append("\n");
tspl.append("AGENT: ").append(prefsHelper.get("code")).append("\n");
tspl.append("DATE: ").append(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss").format(LocalDateTime.now())).append("\n");
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);
_initializeToZero();
} catch (SecurityException e) {
Toast.makeText(requireContext(),
"Permission Bluetooth non accordée", Toast.LENGTH_SHORT).show();
}
}
@SuppressLint({"MissingInflatedId", "SetTextI18n"})
void _showPariDialog(Pari pari){
LayoutInflater inflater = getLayoutInflater();
View view = inflater.inflate(R.layout.pari_confirmation, null);
TextView numero_course = (TextView) view.findViewById(R.id.alert_course_numero);
numero_course.setText("Numero Course: "+String.valueOf(shared.selectedCourse.getValue().getId()));
TextView alert_pari_type = (TextView) view.findViewById(R.id.alert_pari_type);
alert_pari_type.setText(String.valueOf(shared.typeOfBet.getValue().getName()));
TextView is_elargie = (TextView) view.findViewById(R.id.alert_is_elargie);
is_elargie.setText(selectedHorses.getValue().size() > shared.typeOfBet.getValue().getNumberOfHorse()?"CE":"SI");
TextView alert_combinaison = (TextView) view.findViewById(R.id.alert_combinaison);
alert_combinaison.setText(selectedHorses.getValue().stream()
.map(String::valueOf)
.collect(Collectors.joining("-")));
TextView aler_coeff = (TextView) view.findViewById(R.id.alert_coeff);
aler_coeff.setText("Coef:"+String.valueOf(coeff));
TextView alert_montant = (TextView) view.findViewById(R.id.alert_montant);
alert_montant.setText("Montant: "+String.valueOf(pari.getTypesParisMises().get(0).getMiseTotale())+" CFA");
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setView(view);
builder.setCancelable(false);
dialog = builder.create();
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
view.findViewById(R.id.alert_validate).setOnClickListener(v->{
pariViewModel.createPari(pari).observe(getViewLifecycleOwner(),new Observer<Result<ParisResponse>>() {
@Override
public void onChanged(Result<ParisResponse> pariResult) {
switch (pariResult.status){
case ERROR:
loader.dismiss();
dialog.dismiss();
MessageDialog.showError(getContext(), pariResult.message);
break;
case LOADING:
loader.show("Prise de pari");
break;
case SUCCESS:
try {
loader.dismiss();
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();
MessageDialog.showSuccess(getContext(), "Pari créé avec succès");
} catch (WriterException e) {
loader.dismiss();
MessageDialog.showError(getContext(), e.getMessage());
throw new RuntimeException(e);
}
break;
}
}
});
});
view.findViewById(R.id.alert_cancel).setOnClickListener(v -> {
dialog.dismiss();
});
dialog.show();
}
void _initializeToZero(){
selectedHorses.setValue(List.of());
binding.combination.setText("");
binding.order.setChecked(false);
order = false;
binding.order.setVisibility(View.GONE);
binding.coeff.setText("1");
coeff = Integer.parseInt(binding.coeff.getText().toString());
setupNumberGrid(binding.gridNumbers);
}
public void calculateMise(List<String> selectedHorses){
_notClickable(selectedHorses);
int nombreChevauxSelectionnes = selectedHorses.size();
long mise = 0;
int nonPartants = 0;
if(shared.typeOfBet.getValue() == null || shared.selectedCourse.getValue() == null){
return;
}
if(shared.selectedCourse.getValue().getNonPartants() != null){
nonPartants = shared.selectedCourse.getValue().getNonPartants().size();
}
int typeOfBetHorses = shared.typeOfBet.getValue().getNumberOfHorse();
int partants = shared.selectedCourse.getValue().getNombrePartants();
if(shared.typeOfBet.getValue().getNumberOfHorse() > nombreChevauxSelectionnes){
binding.mise.setText(mise+" CFA");
return;
}
if(typeOfBetHorses == 2){
mise = 500;
}else{
if(typeOfBetHorses == 5){
mise = 300;
}else{
mise = 200;
}
}
mise = mise * coeff;
if(nombreX>0){
if(nombreChevauxSelectionnes == typeOfBetHorses){
mise = mise * _calculateArrangement((partants - (typeOfBetHorses-nombreX) - nonPartants), nombreX);
}else{
if(nombreChevauxSelectionnes - typeOfBetHorses < nombreX || nombreChevauxSelectionnes - typeOfBetHorses==1){
mise = 0;
this.mise = mise;
binding.mise.setText(mise+" CFA");
return;
}
mise = mise * _calculateArrangement(nombreChevauxSelectionnes - typeOfBetHorses , nombreX);
}
if(order){
mise = mise * _calculateArrangement(typeOfBetHorses, (typeOfBetHorses-nombreX));
}
this.mise = mise;
binding.mise.setText(mise+" CFA");
return;
}
if(!order){
mise = mise * _calculateCombinaison(nombreChevauxSelectionnes, typeOfBetHorses);
}else{
mise = mise * _calculateArrangement(nombreChevauxSelectionnes, typeOfBetHorses);
}
this.mise = mise;
binding.mise.setText(mise+" CFA");
}
Long _calculateArrangement(int n, int k){
return _calculateFactorial(n) / _calculateFactorial(n-k);
}
Long _calculateCombinaison(int n, int k){
return _calculateFactorial(n) / (_calculateFactorial(k) * _calculateFactorial(n - k));
}
Long _calculateFactorial(int n){
long f = 1;
if(n == 0){
return f;
}
for(int i = 1; i <=n; i++){
f *= i;
}
return f;
}
public Bitmap generateBarcodeBitmap(String contents, int width, int height) throws WriterException {
BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, BarcodeFormat.CODE_128, width, height);
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
bmp.setPixel(x, y, bitMatrix.get(x, y) ? android.graphics.Color.BLACK : android.graphics.Color.WHITE);
}
}
return bmp;
}
public static String generate12Digits() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 8; i++) {
int digit = (int) (Math.random() * 10); // 0 à 9
sb.append(digit);
}
return sb.toString();
}
@Override @Override
public void onDestroyView() { public void onDestroyView() {

View File

@@ -0,0 +1,105 @@
package com.example.quiz;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.quiz.databinding.FragmentCaisseBinding;
import com.example.quiz.utils.AuthNavigator;
import com.example.quiz.utils.SessionManager;
import com.example.quiz.viewModel.LoginViewModel;
import com.example.quiz.viewModel.LogsViewModel;
import com.google.android.material.appbar.MaterialToolbar;
/**
* A simple {@link Fragment} subclass.
* Use the {@link Caisse#newInstance} factory method to
* create an instance of this fragment.
*/
public class Caisse extends Fragment {
FragmentCaisseBinding binding;
AuthNavigator authNavigator;
SessionManager sessionManager;
public Caisse() {
// Required empty public constructor
}
// TODO: Rename and change types and number of parameters
public static Caisse newInstance() {
Caisse fragment = new Caisse();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
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
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentCaisseBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
binding.soldeBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Sold sold = Sold.newInstance();
authNavigator.navigate(sold);
}
});
binding.winTicketBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
WinTicket winTicket = WinTicket.newInstance();
authNavigator.navigate(winTicket);
}
});
binding.cancelTicketBtn.setOnClickListener(v -> {
AnnulationTicket annulationTicket = AnnulationTicket.newInstance();
authNavigator.navigate(annulationTicket);
});
binding.lastBetsBtn.setOnClickListener(v -> {
authNavigator.navigate(DerniersParis.newInstance());
});
}
@Override
public void onResume() {
super.onResume();
AppCompatActivity activity = (AppCompatActivity) getActivity();
if (activity != null){
MaterialToolbar toolbar = activity.findViewById(R.id.toolbar);
activity.setSupportActionBar(toolbar);
if(toolbar != null){
if(activity.getSupportActionBar() != null){
activity.getSupportActionBar().setTitle("Caisse");
toolbar.setBackgroundColor(getResources().getColor(R.color.primary_green));
toolbar.setTitleTextColor(getResources().getColor(R.color.white));
toolbar.setVisibility(View.VISIBLE);
}
}
}
}
}

View File

@@ -0,0 +1,120 @@
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 androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.example.quiz.data.adapter.LastBetsAdapter;
import com.example.quiz.data.model.Pari;
import com.example.quiz.data.model.ParisResponse;
import com.example.quiz.databinding.FragmentDerniersParisBinding;
import com.example.quiz.utils.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 java.util.List;
import dagger.hilt.android.AndroidEntryPoint;
/**
* A simple {@link Fragment} subclass.
* Use the {@link DerniersParis#newInstance} factory method to
* create an instance of this fragment.
*/
@AndroidEntryPoint
public class DerniersParis extends Fragment {
FragmentDerniersParisBinding binding;
LogsViewModel logsViewModel;
LoaderDialog loader;
SharedPrefsHelper prefsHelper;
PariViewModel viewModel;
LastBetsAdapter adapter;
public DerniersParis() {
// Required empty public constructor
}
// TODO: Rename and change types and number of parameters
public static DerniersParis newInstance() {
DerniersParis fragment = new DerniersParis();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
prefsHelper = SharedPrefsHelper.getInstance(getContext());
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentDerniersParisBinding.inflate(inflater, container, false);
loader = 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);
binding.lastBetsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
viewModel = new ViewModelProvider(this).get(PariViewModel.class);
viewModel.getDerniersParis(prefsHelper.get("id")).observe(getViewLifecycleOwner(), new Observer<Result<List<ParisResponse>>>() {
@Override
public void onChanged(Result<List<ParisResponse>> listResult) {
switch (listResult.status){
case LOADING:
loader.show("Chargement des derniers paris");
break;
case ERROR:
loader.dismiss();
MessageDialog.showError(getContext(), listResult.message);
break;
case SUCCESS:
loader.dismiss();
adapter = new LastBetsAdapter(listResult.data, pari -> {
});
logsViewModel.insertLog(prefsHelper.get("id"), "LAST BETS", "Affichage derniers paris", System.currentTimeMillis());
binding.lastBetsRecyclerView.setAdapter(adapter);
break;
}
}
});
}
@Override
public void onResume() {
super.onResume();
AppCompatActivity activity = (AppCompatActivity) getActivity();
if(activity!=null){
MaterialToolbar toolbar = activity.findViewById(R.id.toolbar);
activity.setSupportActionBar(toolbar);
toolbar.setTitle("Derniers Paris");
}
}
}

View File

@@ -6,31 +6,47 @@ 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.appcompat.widget.Toolbar; import androidx.core.view.MenuHost;
import androidx.core.app.ActivityCompat; import androidx.core.view.MenuProvider;
import androidx.core.app.ComponentActivity;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import android.service.controls.Control;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.LayoutAnimationController;
import android.widget.Toast; import android.widget.Toast;
import com.anggastudio.printama.Pref;
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.PagedModel;
import com.example.quiz.databinding.FragmentListOFBettingBinding; import com.example.quiz.databinding.FragmentListOFBettingBinding;
import com.example.quiz.viewModel.BetViewModel; import com.example.quiz.utils.AuthNavigator;
import com.example.quiz.utils.BluetoothUtils;
import com.example.quiz.utils.LoaderDialog;
import com.example.quiz.utils.MessageDialog;
import com.example.quiz.utils.Result;
import com.example.quiz.utils.SessionManager;
import com.example.quiz.viewModel.CourseViewModel;
import com.example.quiz.viewModel.LoginViewModel;
import com.example.quiz.viewModel.LogsViewModel;
import com.example.quiz.viewModel.SharedViewModel; import com.example.quiz.viewModel.SharedViewModel;
import com.google.android.material.appbar.MaterialToolbar;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
import dagger.hilt.android.internal.lifecycle.HiltViewModelFactory;
/** /**
* A simple {@link Fragment} subclass. * A simple {@link Fragment} subclass.
@@ -40,25 +56,26 @@ import dagger.hilt.android.internal.lifecycle.HiltViewModelFactory;
@AndroidEntryPoint @AndroidEntryPoint
public class ListOFBets extends Fragment { public class ListOFBets extends Fragment {
private String username;
private View view;
FragmentListOFBettingBinding binding; FragmentListOFBettingBinding binding;
private BetViewModel viewModel; LoaderDialog loader;
private SharedViewModel shared; private SharedViewModel shared;
private CourseViewModel viewModel;
AuthNavigator authNavigator;
private BetsAdapter adapter; private BetsAdapter adapter;
private AppCompatActivity activity; private AppCompatActivity activity;
public static ListOFBets newInstance(String username) { public static ListOFBets newInstance() {
ListOFBets fragment = new ListOFBets(); ListOFBets fragment = new ListOFBets();
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putString("ARG_USERNAME", username);
fragment.setArguments(args); fragment.setArguments(args);
return fragment; return fragment;
} }
@@ -66,8 +83,31 @@ public class ListOFBets extends Fragment {
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if(getArguments() != null){ SessionManager sessionManager = SessionManager.newInstance(getContext());
username = getArguments().getString("ARG_USERNAME"); 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();
} }
} }
@@ -75,24 +115,16 @@ public class ListOFBets extends Fragment {
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
binding = FragmentListOFBettingBinding.inflate(inflater, container, false); binding = FragmentListOFBettingBinding.inflate(inflater, container, false);
activity = (AppCompatActivity) getActivity(); loader = new LoaderDialog(getContext());
if(activity != null){ shared = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
if(activity.getSupportActionBar() != null){
activity.getSupportActionBar().show();
activity.getSupportActionBar().setTitle("Liste des paris");
}
}
adapter = new BetsAdapter(); adapter = new BetsAdapter();
binding.recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2)); binding.recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
binding.recyclerView.setAdapter(adapter); binding.recyclerView.setAdapter(adapter);
LayoutAnimationController controller = AnimationUtils.loadLayoutAnimation(getContext(), R.anim.layout_fad_in); viewModel = new ViewModelProvider(this).get(CourseViewModel.class);
binding.recyclerView.setLayoutAnimation(controller);
viewModel = new ViewModelProvider(this).get(BetViewModel.class);
shared = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
viewModel.bets.observe(getViewLifecycleOwner(), bets -> { /*viewModel.bets.observe(getViewLifecycleOwner(), bets -> {
adapter.setBets(bets); adapter.setBets(bets);
adapter.setOnItemClickListener(position -> { adapter.setOnItemClickListener(position -> {
shared.setBetId(position); shared.setBetId(position);
@@ -103,24 +135,90 @@ public class ListOFBets extends Fragment {
.addToBackStack(null) .addToBackStack(null)
.commit(); .commit();
}); });
}); });*/
viewModel.loadBets(); //viewModel.loadBets();
observe();
return binding.getRoot(); return binding.getRoot();
} }
public void observe(){
String day = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(LocalDateTime.now());
viewModel.getCourses(day).observe(getViewLifecycleOwner(), new Observer<Result<PagedModel<Course>>>() {
@Override
public void onChanged(Result<PagedModel<Course>> result) {
switch (result.status){
case LOADING:
loader.show("Chargement des courses");
break;
case SUCCESS:
loader.dismiss();
adapter.setBets(result.data.getContent());
adapter.setOnItemClickListener(course ->{
shared.setSelectedCourse(course);
ListOfTypeOfBets typeOfBets = ListOfTypeOfBets.newInstance();
authNavigator.navigate(typeOfBets);
});
break;
case ERROR:
loader.dismiss();
MessageDialog.showError(getContext(), result.message);
break;
}
}
});
}
@SuppressLint("UseSupportActionBar") @SuppressLint("UseSupportActionBar")
@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();
// 🔹 On enlève d'abord les anciens menu providers si ce fragment est recréé
menuHost.invalidateMenu();
// 🔹 Ajout du menu spécifique à ce fragment
menuHost.addMenuProvider(new MenuProvider() {
@Override
public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {
// Nettoyer avant d'inflater
menu.clear();
menuInflater.inflate(R.menu.menu_main, menu);
}
@Override
public boolean onMenuItemSelected(@NonNull MenuItem menuItem) {
int id = menuItem.getItemId();
if (id == R.id.action_sold) {
FragmentManager fragmentManager = getParentFragmentManager();
Sold sold = Sold.newInstance();
fragmentManager.beginTransaction()
.replace(R.id.nav_host_fragment_content_main, sold)
.addToBackStack(null)
.commit();
return true;
}
return false;
}
}, getViewLifecycleOwner(), Lifecycle.State.RESUMED);
}
@Override
public void onResume() {
super.onResume();
AppCompatActivity activity = (AppCompatActivity) getActivity(); AppCompatActivity activity = (AppCompatActivity) getActivity();
if(activity != null){ if(activity != null){
Toolbar toolbar = activity.findViewById(R.id.toolbar); MaterialToolbar toolbar = activity.findViewById(R.id.toolbar);
activity.getSupportActionBar().show();
toolbar.setBackgroundColor( getResources().getColor(R.color.primary_green)); toolbar.setBackgroundColor( getResources().getColor(R.color.primary_green));
toolbar.setTitleTextColor(getResources().getColor(R.color.text_light_grey)); toolbar.setTitleTextColor(getResources().getColor(R.color.white, null));
activity.setSupportActionBar(toolbar); activity.setSupportActionBar(toolbar);
if(activity.getSupportActionBar() != null){ if(activity.getSupportActionBar() != null){
activity.getSupportActionBar().setTitle(username); activity.getSupportActionBar().setTitle("Liste des courses");
} }
} }
} }

View File

@@ -0,0 +1,177 @@
package com.example.quiz;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.view.MenuHost;
import androidx.core.view.MenuProvider;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import android.widget.Toolbar;
import com.anggastudio.printama.Pref;
import com.anggastudio.printama.Printama;
import com.example.quiz.data.adapter.ReunionAdapter;
import com.example.quiz.data.model.Reunion;
import com.example.quiz.databinding.FragmentListOfReunionsBinding;
import com.example.quiz.utils.BluetoothUtils;
import com.example.quiz.utils.LoaderDialog;
import com.example.quiz.utils.MessageDialog;
import com.example.quiz.utils.Result;
import com.example.quiz.viewModel.ReunionViewModel;
import com.example.quiz.viewModel.SharedViewModel;
import com.google.android.material.appbar.MaterialToolbar;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import dagger.hilt.android.AndroidEntryPoint;
/**
* A simple {@link Fragment} subclass.
* Use the {@link ListOfReunions#newInstance} factory method to
* create an instance of this fragment.
*/
@AndroidEntryPoint
public class ListOfReunions extends Fragment {
FragmentListOfReunionsBinding binding;
SharedViewModel shared;
ReunionAdapter adapter;
LoaderDialog loader;
ReunionViewModel reunionViewModel;
public ListOfReunions() {
// Required empty public constructor
}
public static ListOfReunions newInstance(String username) {
ListOfReunions fragment = new ListOfReunions();
Bundle args = new Bundle();
args.putString("USER_NAME", username);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestPermission();
}
private void requestPermission(){
Pref.init(getContext());
if (BluetoothUtils.needsBluetoothPermissions()) {
if (!BluetoothUtils.hasBluetoothPermission(getContext())) {
// Demande la permission si non accordée
BluetoothUtils.requestBluetoothPermission(getActivity());
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(), getActivity());
}
} catch (SecurityException e) {
Toast.makeText(getContext(),
"Permission Bluetooth non accordée", Toast.LENGTH_SHORT).show();
}
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentListOfReunionsBinding.inflate(inflater, container, false);
loader = new LoaderDialog(getContext());
adapter = new ReunionAdapter();
binding.reunionsList.setLayoutManager(new LinearLayoutManager(getContext()));
binding.reunionsList.setAdapter(adapter);
reunionViewModel = new ViewModelProvider(this).get(ReunionViewModel.class);
shared = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
binding.reunionsLayout.setOnRefreshListener(()->{
observeReunions();
binding.reunionsLayout.setRefreshing(false);
});
observeReunions();
return binding.getRoot();
}
public void observeReunions(){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
String formattedDate = sdf.format(new Date());
reunionViewModel.getReunions(formattedDate).observe(getViewLifecycleOwner(), new Observer<Result<List<Reunion>>>() {
@Override
public void onChanged(Result<List<Reunion>> result) {
switch (result.status) {
case LOADING:
loader.show("Chargement des réunions");
break;
case SUCCESS:
loader.dismiss();
adapter.setReunions(result.data);
adapter.setOnItemClickListener(reunion->{
shared.setSelectedReunion(reunion);
FragmentManager fragmentManager = getParentFragmentManager();
ListOFBets bets = ListOFBets.newInstance();
fragmentManager.beginTransaction()
.replace(R.id.nav_host_fragment_content_main, bets)
.addToBackStack(null)
.commit();
});
break;
case ERROR:
loader.dismiss();
MessageDialog.showError(getContext(), result.message);
break;
}
}
});
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
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("Liste des réunions");
}
toolbar.setBackgroundColor(getResources().getColor(R.color.primary_green, null));
toolbar.setTitleTextColor(getResources().getColor(R.color.white, null));
}
}
}

View File

@@ -4,20 +4,32 @@ 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.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
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.Button;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast; import android.widget.Toast;
import com.example.quiz.data.adapter.TypeOfBetAdapter;
import com.example.quiz.data.model.Course;
import com.example.quiz.data.model.TypeOfBet;
import com.example.quiz.databinding.FragmentListOfTypeOfBetsBinding; import com.example.quiz.databinding.FragmentListOfTypeOfBetsBinding;
import com.example.quiz.utils.AuthNavigator;
import com.example.quiz.utils.SessionManager;
import com.example.quiz.viewModel.LoginViewModel;
import com.example.quiz.viewModel.LogsViewModel;
import com.example.quiz.viewModel.SharedViewModel; import com.example.quiz.viewModel.SharedViewModel;
import com.google.android.material.appbar.MaterialToolbar;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
@@ -32,8 +44,13 @@ public class ListOfTypeOfBets extends Fragment {
private FragmentListOfTypeOfBetsBinding binding; private FragmentListOfTypeOfBetsBinding binding;
SessionManager sessionManager;
AuthNavigator authNavigator;
private SharedViewModel shared; private SharedViewModel shared;
private TypeOfBetAdapter adapter;
public ListOfTypeOfBets() { public ListOfTypeOfBets() {
// Required empty public constructor // Required empty public constructor
} }
@@ -47,6 +64,8 @@ public class ListOfTypeOfBets extends Fragment {
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
sessionManager = SessionManager.newInstance(getContext());
authNavigator = new AuthNavigator(getContext(), getParentFragmentManager(), sessionManager, new ViewModelProvider(this).get(LoginViewModel.class), new ViewModelProvider(this).get(LogsViewModel.class));
} }
@Override @Override
@@ -59,26 +78,59 @@ public class ListOfTypeOfBets 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);
binding.typeOfBetRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
// ViewModels
//viewModel = new ViewModelProvider(this).get(BetViewModel.class);
shared = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
RadioGroup types = binding.optionsGroup; // ⚡ Initialiser ladapter UNE SEULE FOIS
adapter = new TypeOfBetAdapter(new ArrayList<>());
binding.typeOfBetRecyclerView.setAdapter(adapter);
List<Course.TypeParis> betType = shared.selectedCourse.getValue().getTypesParisOuverts();
List<TypeOfBet> useList = new ArrayList<>();
if(betType.contains(Course.TypeParis.QUINTE)){
useList.add(new TypeOfBet("Quinte", Course.TypeParis.QUINTE, 5));
}
if(betType.contains(Course.TypeParis.QUARTE)){
useList.add(new TypeOfBet("Quarte", Course.TypeParis.QUARTE, 4));
}
if(betType.contains(Course.TypeParis.TIERCE)){
useList.add(new TypeOfBet("Tierce", Course.TypeParis.TIERCE, 3));
}
if(betType.contains(Course.TypeParis.COUPLE_PLACE)){
useList.add(new TypeOfBet("Couple Place", Course.TypeParis.COUPLE_PLACE, 2));
}
if(betType.contains(Course.TypeParis.COUPLE_PLACE)){
useList.add(new TypeOfBet("Couple Gagnant", Course.TypeParis.COUPLE_GAGNANT, 2));
}
AtomicReference<TypeOfBet> typeOfBet = new AtomicReference<>();
adapter.setTypes(useList);
adapter.setOnItemClickListener(type -> {
typeOfBet.set(type);
});
Button btnValidate = binding.btnValidate; Button btnValidate = binding.btnValidate;
btnValidate.setOnClickListener(v->{ btnValidate.setOnClickListener(v->{
int selectedId = types.getCheckedRadioButtonId(); if(typeOfBet.get() == null){
if (selectedId == -1) { Toast.makeText(getContext(),"Aucun Type de jeu choisi", Toast.LENGTH_SHORT).show();
Toast.makeText(requireContext(), "Veuillez sélectionner une option", Toast.LENGTH_SHORT).show();
return; return;
} }
RadioButton selectedButton = view.findViewById(selectedId); shared.setTypeOfBet(typeOfBet.get());
String selectedType = selectedButton.getText().toString();
shared = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
shared.setTypeOfBet(selectedType);
FragmentManager fragmentManager = getParentFragmentManager();
BetValidation betValidation = BetValidation.newInstance();
fragmentManager.beginTransaction()
.replace(R.id.nav_host_fragment_content_main, betValidation)
.addToBackStack(null)
.commit();
});
BetValidation betValidation = BetValidation.newInstance();
authNavigator.navigate(betValidation);
});
}
@Override
public void onResume() {
super.onResume();
AppCompatActivity activity = (AppCompatActivity) getActivity();
if(activity != null){
MaterialToolbar toolbar = activity.findViewById(R.id.toolbar);
activity.setSupportActionBar(toolbar);
if(activity.getSupportActionBar() != null){
activity.getSupportActionBar().setTitle("Liste des jeux");
}
}
} }
} }

View File

@@ -1,26 +1,23 @@
package com.example.quiz; package com.example.quiz;
import android.app.Activity;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction; import androidx.lifecycle.ViewModelProvider;
import android.text.Editable;
import android.text.TextWatcher;
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.Toast;
import android.widget.Toolbar;
import com.example.quiz.databinding.FragmentLoginBinding; import com.example.quiz.databinding.FragmentLoginBinding;
import com.example.quiz.utils.AuthNavigator;
import com.example.quiz.utils.SessionManager;
import com.example.quiz.utils.SharedPrefsHelper; import com.example.quiz.utils.SharedPrefsHelper;
import com.example.quiz.viewModel.LoginViewModel;
import com.example.quiz.viewModel.LogsViewModel;
/** /**
* A simple {@link Fragment} subclass. * A simple {@link Fragment} subclass.
@@ -29,6 +26,8 @@ import com.example.quiz.utils.SharedPrefsHelper;
*/ */
public class Login extends Fragment { public class Login extends Fragment {
private AuthNavigator authNavigator;
private SharedPrefsHelper prefsHelper; private SharedPrefsHelper prefsHelper;
public Login() { public Login() {
// Required empty public constructor // Required empty public constructor
@@ -59,6 +58,9 @@ public class Login extends Fragment {
@Override @Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
SessionManager 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));
AppCompatActivity activity = (AppCompatActivity) getActivity(); AppCompatActivity activity = (AppCompatActivity) getActivity();
if(activity != null){ if(activity != null){
activity.getSupportActionBar().hide(); activity.getSupportActionBar().hide();
@@ -71,27 +73,28 @@ public class Login extends Fragment {
binding.loginButton.setOnClickListener(new View.OnClickListener() { binding.loginButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if(binding.userNameInput.getText().toString().isEmpty()){
binding.userNameInput.setError("Le nom d'utilisateur est obligatoire");
binding.userNameInput.requestFocus();
return;
}
if(binding.passwordInput.getText().toString().isEmpty()){
binding.passwordInput.setError("Le mot de passe est obligatoire");
binding.passwordInput.requestFocus();
return;
}
if(binding.passwordInput.getText().toString().length() < 6){
binding.passwordInput.setError("Le mot de passe doit contenir au moins 6 caractères ");
binding.passwordInput.requestFocus();
return;
}
prefsHelper = SharedPrefsHelper.getInstance(getContext()); prefsHelper = SharedPrefsHelper.getInstance(getContext());
prefsHelper.save("username", binding.userNameInput.getText().toString()); ListOFBets bets = ListOFBets.newInstance();
authNavigator.navigate(bets);
}
});
binding.caisse.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Caisse caisse = Caisse.newInstance();
authNavigator.navigate(caisse);
}
});
binding.settings.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Settings settings = Settings.newInstance();
FragmentManager fragmentManager = getParentFragmentManager(); FragmentManager fragmentManager = getParentFragmentManager();
ListOFBets bets = ListOFBets.newInstance(binding.userNameInput.getText().toString());
fragmentManager.beginTransaction() fragmentManager.beginTransaction()
.replace(R.id.nav_host_fragment_content_main, bets) .replace(R.id.nav_host_fragment_content_main, settings)
.addToBackStack(null)
.commit(); .commit();
} }
}); });

View File

@@ -0,0 +1,118 @@
package com.example.quiz;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
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 android.widget.Toast;
import com.example.quiz.data.adapter.LogsAdapter;
import com.example.quiz.data.dao.UserActivityLogDao;
import com.example.quiz.data.database.DatabaseClient;
import com.example.quiz.databinding.FragmentLogsBinding;
import com.example.quiz.utils.LoaderDialog;
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 Logs#newInstance} factory method to
* create an instance of this fragment.
*/
@AndroidEntryPoint
public class Logs extends Fragment {
FragmentLogsBinding binding;
LogsViewModel viewModel;
private LogsAdapter adapter;
LoaderDialog loader;
public Logs() {
// Required empty public constructor
}
// TODO: Rename and change types and number of parameters
public static Logs newInstance() {
Logs fragment = new Logs();
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) {
binding = FragmentLogsBinding.inflate(inflater, container, false);
loader = new LoaderDialog(getContext());
adapter = new LogsAdapter();
binding.recyclerLogs.setLayoutManager(new LinearLayoutManager(getContext()));
binding.recyclerLogs.setAdapter(adapter);
// Inflate the layout for this fragment
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
AppCompatActivity activity = (AppCompatActivity) getActivity();
if (activity != null) {
if(activity.getSupportActionBar() != null){
activity.getSupportActionBar().setTitle("Historique");
MaterialToolbar toolbar = activity.findViewById(R.id.toolbar);
toolbar.setBackgroundColor(getResources().getColor(R.color.primary_green));
toolbar.setTitleTextColor(getResources().getColor(R.color.white));
}
}
viewModel = new ViewModelProvider(this).get(LogsViewModel.class);
// Observer les logs
viewModel.getTodayLogs().observe(getViewLifecycleOwner(), new Observer<List<com.example.quiz.data.entity.Logs>>() {
@Override
public void onChanged(List<com.example.quiz.data.entity.Logs> logs) {
loader.show("Chargement des logs");
if(logs != null){
loader.dismiss();
adapter.setLogs(logs);
}else{
loader.dismiss();
showErrorAlert("Erreur", "Impossible de charger les logs");
}
}
});
}
private void showErrorAlert(String title, String message) {
new AlertDialog.Builder(requireContext())
.setTitle(title)
.setMessage(message)
.setCancelable(true)
.setPositiveButton("OK", (dialog, which) -> dialog.dismiss())
.show();
}
}

View File

@@ -1,16 +1,18 @@
package com.example.quiz; package com.example.quiz;
import android.graphics.Color; import android.graphics.Color;
import android.os.Bundle; import android.os.Bundle;
import com.example.quiz.utils.AuthNavigator;
import com.example.quiz.utils.SessionManager;
import com.example.quiz.utils.SharedPrefsHelper; import com.example.quiz.utils.SharedPrefsHelper;
import com.google.android.material.snackbar.Snackbar; import com.example.quiz.viewModel.LoginViewModel;
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.Looper;
import android.view.View; import androidx.lifecycle.ViewModelProvider;
import androidx.fragment.app.FragmentManager;
import androidx.navigation.NavController; import androidx.navigation.NavController;
import androidx.navigation.Navigation; import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.AppBarConfiguration;
@@ -23,43 +25,122 @@ import dagger.hilt.android.AndroidEntryPoint;
@AndroidEntryPoint @AndroidEntryPoint
public class PageQuiz extends AppCompatActivity { public class PageQuiz extends AppCompatActivity {
LoginViewModel viewModel;
LogsViewModel logsViewModel;
AuthNavigator authNavigator;
private SessionManager sessionManager;
private Handler handler = new Handler();
private Runnable checkRunnable;
private AppBarConfiguration appBarConfiguration; private AppBarConfiguration appBarConfiguration;
private ActivityPageQuizBinding binding; private ActivityPageQuizBinding binding;
private SharedPrefsHelper prefsHelper;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
sessionManager = SessionManager.newInstance(this);
binding = ActivityPageQuizBinding.inflate(getLayoutInflater()); binding = ActivityPageQuizBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
logsViewModel = new ViewModelProvider(this).get(LogsViewModel.class);
viewModel = new ViewModelProvider(this).get(LoginViewModel.class);
authNavigator = new AuthNavigator(this, getSupportFragmentManager(), sessionManager, viewModel, logsViewModel);
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
binding.toolbar.setBackgroundColor(Color.parseColor("#501C5A29")); binding.toolbar.setBackgroundColor(Color.parseColor("#501C5A29"));
binding.fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAnchorView(R.id.fab)
.setAction("Action", null).show();
} }
});
prefsHelper = SharedPrefsHelper.getInstance(getApplicationContext());
FragmentManager fragmentManager = getSupportFragmentManager();
if(prefsHelper.get("username") != null){ // void _showPinDialog(){
ListOFBets bets = ListOFBets.newInstance(prefsHelper.get("username")); // LayoutInflater inflater = getLayoutInflater();
fragmentManager // View view = inflater.inflate(R.layout.pin_view, null);
.beginTransaction() //
.replace(R.id.nav_host_fragment_content_main, bets) // AlertDialog.Builder builder = new AlertDialog.Builder(this);
.commit(); // builder.setView(view);
}else{ // builder.setCancelable(true);
Login login = Login.newInstance(); //
fragmentManager // dialog = builder.create();
.beginTransaction() // dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
.replace(R.id.nav_host_fragment_content_main, login) //
.commit(); // view.findViewById(R.id.cancel).setOnClickListener(v -> dialog.dismiss());
//// Bouton OK
// view.findViewById(R.id.pin_validate).setOnClickListener(v -> {
// TextView pin = view.findViewById(R.id.pin);
// if(pin.getText().toString().isEmpty()){
// pin.setError("Entrez le pin!");
// return;
// }
// if(pin.getText().toString().length() < 4){
// pin.setError("Le pin doit contenir minimum quatre chiffres!");
// return;
// }
// LoginPayload loginPayload = new LoginPayload(
// pin.getText().toString()
// );
// viewModel.login(loginPayload).observe(this, new Observer<Result<LoginResponse>>() {
// @Override
// public void onChanged(Result<LoginResponse> loginResponseResult) {
// switch (loginResponseResult.status){
// case LOADING:
// view.findViewById(R.id.pin_validate).setEnabled(false);
// view.findViewById(R.id.pin_validate).setAlpha(0.5f);
// break;
// case ERROR:
// Toast.makeText(getApplicationContext(), loginResponseResult.message, Toast.LENGTH_SHORT).show();
// view.findViewById(R.id.pin_validate).setEnabled(true);
// view.findViewById(R.id.pin_validate).setAlpha(1f);
// break;
// case SUCCESS:
// loginSuccess(loginResponseResult.data);
// logsViewModel.insertLog(prefsHelper.get("id"), "LOGIN","Authentification sur l'appareil", System.currentTimeMillis());
// sessionManager.updateLastExpiredDate();
// dialog.dismiss();
// view.findViewById(R.id.pin_validate).setEnabled(true);
// view.findViewById(R.id.pin_validate).setAlpha(1f);
// break;
// }
// }
// });
// });
//
// dialog.show();
// }
//
//
// private void loginSuccess(LoginResponse loginResponse){
// prefsHelper.save("id", String.valueOf(loginResponse.getId()));
// prefsHelper.save("firstName", loginResponse.getPrenom());
// prefsHelper.save("lastName", loginResponse.getNom());
// prefsHelper.save("profile", loginResponse.getProfile());
// prefsHelper.save("limit", String.valueOf(loginResponse.getLimiteSuperieure()));
// prefsHelper.save("code", loginResponse.getCode());
// }
@Override
protected void onResume() {
super.onResume();
handler = new Handler(Looper.getMainLooper());
checkRunnable = new Runnable() {
@Override
public void run() {
if (sessionManager.isExpired()) {
if (!authNavigator.dialogIsShowing()) {
authNavigator.showPinDialog(() -> {
sessionManager.updateLastExpiredDate();
handler.postDelayed(checkRunnable, 1);
});
} }
} else {
// Replanifie le check toutes les secondes
handler.postDelayed(this, 1);
}
}
};
handler.postDelayed(checkRunnable, 1);
} }
@Override @Override

View File

@@ -1,80 +0,0 @@
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.Question;
import com.example.quiz.databinding.FragmentQuestionsBinding;
import com.example.quiz.injection.QuestionViewModelFactory;
import com.example.quiz.viewModel.QuestionViewModel;
public class Questions extends Fragment {
private FragmentQuestionsBinding binding;
private QuestionViewModel viewModel;
public Questions() {
// Required empty public constructor
}
public static Questions newInstance(String username) {
Questions fragment = new Questions();
Bundle args = new Bundle();
args.putString("ARG_USERNAME", username);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this, QuestionViewModelFactory.getInstance()).get(QuestionViewModel.class);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentQuestionsBinding.inflate(inflater, container, false);
// Inflate the layout for this fragment
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
viewModel.startQuiz();
viewModel.currentQuestion.observe(getViewLifecycleOwner(), new Observer<Question>() {
@Override
public void onChanged(Question question) {
binding.questionText.setText(question.getQuestion());
binding.answer1.setText(question.getAnswers().get(0));
binding.answer2.setText(question.getAnswers().get(1));
binding.answer3.setText(question.getAnswers().get(2));
binding.answer4.setText(question.getAnswers().get(3));
}
});
}
@Override
public void onStart() {
super.onStart();
binding.nextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
viewModel.nextQuestion();
}
});
}
}

View File

@@ -0,0 +1,137 @@
package com.example.quiz;
import android.annotation.SuppressLint;
import android.content.pm.PackageManager;
import android.media.Image;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.Manifest;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.DialogFragment;
import com.example.quiz.databinding.FragmentScanBinding;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.mlkit.vision.barcode.BarcodeScanner;
import com.google.mlkit.vision.barcode.BarcodeScanning;
import com.google.mlkit.vision.barcode.common.Barcode;
import com.google.mlkit.vision.common.InputImage;
public class ScannerDialog extends DialogFragment {
private PreviewView previewView;
private ProcessCameraProvider cameraProvider;
public interface OnBarcodeScannedListener {
void onBarcodeScanned(String code);
}
private OnBarcodeScannedListener listener;
private FragmentScanBinding binding;
public void setOnBarcodeScannedListener(OnBarcodeScannedListener listener){
this.listener = listener;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentScanBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
previewView = view.findViewById(R.id.cameraPreview);
if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.CAMERA}, 1001);
} else {
startCamera();
}
view.findViewById(R.id.closeScanDialog).setOnClickListener(v -> dismiss());
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1001 && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startCamera();
} else {
Toast.makeText(getContext(), "Permission caméra refusée", Toast.LENGTH_SHORT).show();
dismiss();
}
}
private void startCamera() {
final ListenableFuture<ProcessCameraProvider> cameraProviderFuture =
ProcessCameraProvider.getInstance(getContext());
cameraProviderFuture.addListener(() -> {
try {
cameraProvider = cameraProviderFuture.get();
bindPreviewAndScanner();
} catch (Exception e) {
e.printStackTrace();
}
}, ContextCompat.getMainExecutor(getContext()));
}
private void bindPreviewAndScanner() {
cameraProvider.unbindAll();
Preview preview = new Preview.Builder().build();
CameraSelector selector = CameraSelector.DEFAULT_BACK_CAMERA;
preview.setSurfaceProvider(previewView.getSurfaceProvider());
ImageAnalysis analysis = new ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build();
analysis.setAnalyzer(ContextCompat.getMainExecutor(getContext()), imageProxy -> {
@SuppressLint("UnsafeOptInUsageError")
Image mediaImage = imageProxy.getImage();
if (mediaImage != null) {
InputImage inputImage = InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees());
BarcodeScanner scanner = BarcodeScanning.getClient();
scanner.process(inputImage)
.addOnSuccessListener(barcodes -> {
for (Barcode b : barcodes) {
String value = b.getRawValue();
if (value != null && listener != null) {
listener.onBarcodeScanned(value);
dismiss(); // Fermer le dialog après scan
scanner.close();
imageProxy.close();
cameraProvider.unbindAll();
return;
}
}
})
.addOnFailureListener(Throwable::printStackTrace)
.addOnCompleteListener(task -> imageProxy.close());
}
});
cameraProvider.bindToLifecycle(this, selector, preview, analysis);
}
}

View File

@@ -1,44 +0,0 @@
package com.example.quiz;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.navigation.fragment.NavHostFragment;
import com.example.quiz.databinding.FragmentSecond2Binding;
public class Second2Fragment extends Fragment {
private FragmentSecond2Binding binding;
@Override
public View onCreateView(
@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState
) {
binding = FragmentSecond2Binding.inflate(inflater, container, false);
return binding.getRoot();
}
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.buttonSecond.setOnClickListener(v ->
NavHostFragment.findNavController(Second2Fragment.this)
.navigate(R.id.action_Second2Fragment_to_First2Fragment)
);
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}

View File

@@ -1,44 +0,0 @@
package com.example.quiz;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.navigation.fragment.NavHostFragment;
import com.example.quiz.databinding.FragmentSecondBinding;
public class SecondFragment extends Fragment {
private FragmentSecondBinding binding;
@Override
public View onCreateView(
@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState
) {
binding = FragmentSecondBinding.inflate(inflater, container, false);
return binding.getRoot();
}
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.buttonSecond.setOnClickListener(v ->
NavHostFragment.findNavController(SecondFragment.this)
.navigate(R.id.action_SecondFragment_to_FirstFragment)
);
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}

View File

@@ -0,0 +1,275 @@
package com.example.quiz;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
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 androidx.recyclerview.widget.RecyclerView;
import android.provider.Settings;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.quiz.data.adapter.PointDeVenteAdapter;
import com.example.quiz.data.model.PagedModel;
import com.example.quiz.data.model.PointDeVente;
import com.example.quiz.data.model.Tpe;
import com.example.quiz.data.model.TpeResponse;
import com.example.quiz.databinding.FragmentServerConfigBinding;
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.PointDeVenteViewModel;
import com.example.quiz.viewModel.TpeViewModel;
import com.google.android.material.appbar.MaterialToolbar;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import dagger.hilt.android.AndroidEntryPoint;
/**
* A simple {@link Fragment} subclass.
* Use the {@link ServerConfig#newInstance} factory method to
* create an instance of this fragment.
*/
@AndroidEntryPoint
public class ServerConfig extends Fragment {
FragmentServerConfigBinding binding;
private TpeViewModel viewModel;
private SharedPrefsHelper sharedPrefsHelper;
private boolean isUserTap = true;
PointDeVenteAdapter pointDeVenteAdapter;
PointDeVente pdv = null;
private PointDeVenteViewModel pointDeVenteViewModel;
LoaderDialog loader;
public ServerConfig() {
// Required empty public constructor
}
// TODO: Rename and change types and number of parameters
public static ServerConfig newInstance() {
ServerConfig fragment = new ServerConfig();
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sharedPrefsHelper = SharedPrefsHelper.getInstance(getContext());
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentServerConfigBinding.inflate(inflater, container, false);
viewModel = new ViewModelProvider(this).get(TpeViewModel.class);
pointDeVenteViewModel = new ViewModelProvider(this).get(PointDeVenteViewModel.class);
loader = new LoaderDialog(getContext());
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
AppCompatActivity activity = (AppCompatActivity) getActivity();
if(activity != null){
MaterialToolbar toolbar = activity.findViewById(R.id.toolbar);
toolbar.setTitle("Configuration du terminal");
toolbar.setBackgroundColor( getResources().getColor(R.color.primary_green));
}
pointDeVenteAdapter = new PointDeVenteAdapter();
RecyclerView recyclerView = binding.pointRecyclerView;
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
binding.pointDeVente.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if(!isUserTap) return;
if(charSequence.toString().isEmpty() || charSequence.toString().length()==0){
recyclerView.setVisibility(View.GONE);
binding.autocompleteContainer.setVisibility(View.GONE);
return;
}
pointDeVenteViewModel.setPointsDeVente(charSequence.toString()).observe(getViewLifecycleOwner(), new Observer<Result<PagedModel<PointDeVente>>>() {
@Override
public void onChanged(Result<PagedModel<PointDeVente>> pagedModelResult) {
switch (pagedModelResult.status){
case LOADING:
binding.autocompleteContainer.setVisibility(View.VISIBLE);
binding.loader.setVisibility(View.VISIBLE);
break;
case SUCCESS:
binding.autocompleteContainer.setVisibility(View.VISIBLE);
binding.loader.setVisibility(View.GONE);
List<PointDeVente> pointsDeVente = pagedModelResult.data.getContent();
recyclerView.setVisibility(View.VISIBLE);
pointDeVenteAdapter.setPoints(pointsDeVente);
recyclerView.setAdapter(pointDeVenteAdapter);
pointDeVenteAdapter.setOnItemClickListener((PointDeVente pointDeVente)->{
pdv = pointDeVente;
isUserTap = false;
binding.pointDeVente.setText(pointDeVente.getNom());
isUserTap = true;
binding.autocompleteContainer.setVisibility(View.GONE);
binding.loader.setVisibility(View.GONE);
});
break;
case ERROR:
binding.autocompleteContainer.setVisibility(View.GONE);
binding.loader.setVisibility(View.GONE);
MessageDialog.showError(getContext(), "Erreur lors du chargement des points de vente");
break;
}
}
});
}
@Override
public void afterTextChanged(Editable editable) {
}
});
super.onViewCreated(view, savedInstanceState);
}
@Override
public void onResume() {
String serialNumber = Settings.Secure.getString(getContext().getContentResolver(), Settings.Secure.ANDROID_ID);
binding.numeroSerie.setText(serialNumber);
binding.brand.setText(Build.BRAND);
binding.model.setText(Build.MODEL);
binding.type.setText("POS");
binding.validate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(pdv == null){
MessageDialog.showError(getContext(), "Veuillez sélectionner un point de vente");
return;
}
Tpe tpe = new Tpe();
tpe.setNumeroSerie(serialNumber);
tpe.setVersionOs(Build.VERSION.RELEASE);
tpe.setVersionLogicielle(getAppVersion(getContext()));
tpe.setPlateforme("PMU_BET");
tpe.setSystemeExploitation("Android");
tpe.setPointDeVenteId(pdv.getId());
tpe.setAdresseIp(getLocalIpAddress());
tpe.setModeleAppareil(Build.MODEL);
tpe.setTypeTerminal("POS/"+Build.BRAND);
tpe.setStatut("ACTIF");
viewModel.createTpe(tpe).observe(getViewLifecycleOwner(), new Observer<Result<TpeResponse>>() {
@Override
public void onChanged(Result<TpeResponse> tpeResult) {
switch (tpeResult.status){
case LOADING:
loader.show("Création du TPE");
break;
case SUCCESS:
loader.dismiss();
MessageDialog.showSuccess(getContext(), "TPE configuré avec succès!");
sharedPrefsHelper.save("terminalId", String.valueOf(tpeResult.data.getId()));
sharedPrefsHelper.save("pointDeVenteId", String.valueOf(tpeResult.data.getPointDeVenteId()));
break;
case ERROR:
loader.dismiss();
MessageDialog.showError(getContext(), "Erreur lors de la configuration du TPE");
break;
}
}
});
}
});
binding.cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (getActivity() != null) {
getActivity().getSupportFragmentManager().popBackStack();
}
}
});
super.onResume();
}
public String getLocalIpAddress() {
try {
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) {
return inetAddress.getHostAddress();
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
return "0.0.0.0";
}
public static String getAppVersion(Context context) {
try {
return context.getPackageManager()
.getPackageInfo(context.getPackageName(), 0)
.versionName;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return "Version inconnue";
}
}
public static String getMacAddress() {
try {
List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : interfaces) {
if (!nif.getName().equalsIgnoreCase("wlan0")) continue;
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) return "N/A";
StringBuilder res1 = new StringBuilder();
for (byte b : macBytes) {
res1.append(String.format("%02X:", b));
}
if (res1.length() > 0) res1.deleteCharAt(res1.length() - 1);
return res1.toString();
}
} catch (Exception ex) {
ex.printStackTrace();
}
return "N/A";
}
}

View File

@@ -0,0 +1,98 @@
package com.example.quiz;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.quiz.databinding.FragmentSettingsBinding;
import com.google.android.material.appbar.MaterialToolbar;
/**
* A simple {@link Fragment} subclass.
* Use the {@link Settings#newInstance} factory method to
* create an instance of this fragment.
*/
public class Settings 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
FragmentSettingsBinding binding;
public Settings() {
// Required empty public constructor
}
// TODO: Rename and change types and number of parameters
public static Settings newInstance() {
Settings fragment = new Settings();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentSettingsBinding.inflate(inflater, container, false);
// Inflate the layout for this fragment
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, 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("Choisissez une action à effectuer");
}
toolbar.setBackgroundColor(getResources().getColor(R.color.primary_green, null));
toolbar.setTitleTextColor(getResources().getColor(R.color.white, null));
}
binding.logs.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Logs logs = Logs.newInstance();
FragmentManager fragmentManager = getParentFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.nav_host_fragment_content_main, logs)
.addToBackStack(null)
.commit();
}
});
binding.serverConfig.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ServerConfig serverConfig = ServerConfig.newInstance();
FragmentManager fragmentManager = getParentFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.nav_host_fragment_content_main, serverConfig)
.addToBackStack(null)
.commit();
}
});
}
}

View File

@@ -0,0 +1,158 @@
package com.example.quiz;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.example.quiz.databinding.FragmentSoldBinding;
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 java.util.Calendar;
import dagger.hilt.android.AndroidEntryPoint;
/**
* A simple {@link Fragment} subclass.
* Use the {@link Sold#newInstance} factory method to
* create an instance of this fragment.
*/
@AndroidEntryPoint
public class Sold extends Fragment {
FragmentSoldBinding binding;
LoaderDialog dialog;
PariViewModel pariViewModel;
SharedPrefsHelper prefsHelper;
LogsViewModel logsViewModel;
public Sold() {
// Required empty public constructor
}
// TODO: Rename and change types and number of parameters
public static Sold newInstance() {
Sold fragment = new Sold();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
prefsHelper = SharedPrefsHelper.getInstance(getContext());
AppCompatActivity activity = (AppCompatActivity) getActivity();
if(activity != null){
MaterialToolbar toolbar = activity.findViewById(R.id.toolbar);
activity.setSupportActionBar(toolbar);
if(activity.getSupportActionBar() != null){
activity.getSupportActionBar().setTitle("Soldes");
}
}
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
binding = FragmentSoldBinding.inflate(inflater, container, false);
dialog = 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);
pariViewModel = new ViewModelProvider(this).get(PariViewModel.class);
binding.btnByCourse.setOnClickListener(v -> {
FragmentManager fragmentManager = getParentFragmentManager();
SoldByCourse soldByCourse = SoldByCourse.newInstance();
fragmentManager.beginTransaction()
.replace(R.id.nav_host_fragment_content_main, soldByCourse)
.addToBackStack(null)
.commit();
});
binding.btnByDay.setOnClickListener(v->{
_showCalendar();
});
}
String _reformatDateForDate(int num){
if(num<10){
return "0"+num;
}
return String.valueOf(num);
}
void _showCalendar(){
Calendar calendar = Calendar.getInstance();
DatePickerDialog datePickerDialog = new DatePickerDialog(
getContext(),
(view, year, month, dayOfMonth) -> {
String date = year + "-" + _reformatDateForDate(month + 1) + "-" + _reformatDateForDate(dayOfMonth);
pariViewModel.getSoldeByDay(prefsHelper.get("id"), date).observe(getViewLifecycleOwner(), new Observer<Result<Double>>() {
@Override
public void onChanged(Result<Double> doubleResult) {
switch (doubleResult.status){
case LOADING:{
dialog.show("Solde du jour");
break;
}
case ERROR:{
dialog.dismiss();
MessageDialog.showError(getContext(), doubleResult.message);
break;
}
case SUCCESS:{
dialog.dismiss();
_showSold(doubleResult.data);
logsViewModel.insertLog(prefsHelper.get("id"), "SOLDE JOUR", "Solde du "+date, System.currentTimeMillis());
break;
}
}
}
});
},
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH)
);
datePickerDialog.show();
}
void _showSold(double solde){
new AlertDialog.Builder(getContext())
.setTitle("Solde")
.setMessage("Solde la course "+solde)
.setPositiveButton("Ok", (dialog, which)->{
dialog.dismiss();
}).show();
}
}

View File

@@ -0,0 +1,116 @@
package com.example.quiz;
import android.app.AlertDialog;
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 android.widget.Toast;
import com.example.quiz.databinding.FragmentSoldByCourseBinding;
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 dagger.hilt.android.AndroidEntryPoint;
/**
* A simple {@link Fragment} subclass.
* Use the {@link SoldByCourse#newInstance} factory method to
* create an instance of this fragment.
*/
@AndroidEntryPoint
public class SoldByCourse extends Fragment {
FragmentSoldByCourseBinding binding;
LoaderDialog loader;
LogsViewModel logsViewModel;
PariViewModel pariViewModel;
SharedPrefsHelper prefsHelper;
public static SoldByCourse newInstance() {
SoldByCourse fragment = new SoldByCourse();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
prefsHelper = SharedPrefsHelper.getInstance(getContext());
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
binding = FragmentSoldByCourseBinding.inflate(inflater, container, false);
logsViewModel = new ViewModelProvider(this).get(LogsViewModel.class);
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
loader = new LoaderDialog(getContext());
pariViewModel = new ViewModelProvider(this).get(PariViewModel.class);
binding.btnCheckBalance.setOnClickListener(v -> {
if(binding.etRaceNumber.getText().toString().isEmpty()){
binding.etRaceNumber.setError("Veuillez entrer un numéro de course");
return;
}
pariViewModel.getSoldeByCourse(prefsHelper.get("id"), binding.etRaceNumber.getText().toString()).observe(getViewLifecycleOwner(), new Observer<Result<Double>>() {
@Override
public void onChanged(Result<Double> doubleResult) {
switch (doubleResult.status){
case LOADING:{
loader.show("Chargement du solde");
break;
}
case ERROR:{
loader.dismiss();
MessageDialog.showError(getContext(), doubleResult.message);
break;
}
case SUCCESS:{
loader.dismiss();
_showPariDialog(doubleResult.data);
logsViewModel.insertLog(prefsHelper.get("id"), "SOLDE COURSE", "Solde de la course "+binding.etRaceNumber.getText(), System.currentTimeMillis());
break;
}
}
}
});
});
}
void _showPariDialog(double solde){
new AlertDialog.Builder(getContext())
.setTitle("Solde")
.setMessage("Solde la course "+solde)
.setPositiveButton("Ok", (dialog, which) -> {
binding.etRaceNumber.setText("");
dialog.dismiss();
})
.show();
}
}

View File

@@ -0,0 +1,76 @@
package com.example.quiz;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.quiz.databinding.FragmentWinTicketBinding;
import com.google.android.material.appbar.MaterialToolbar;
/**
* A simple {@link Fragment} subclass.
* Use the {@link WinTicket#newInstance} factory method to
* create an instance of this fragment.
*/
public class WinTicket extends Fragment {
FragmentWinTicketBinding binding;
public WinTicket() {
// Required empty public constructor
}
public static WinTicket newInstance() {
WinTicket fragment = new WinTicket();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentWinTicketBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.winTicketBtnBack.setOnClickListener(v -> {
getActivity().onBackPressed();
});
binding.scanTicketBtn.setOnClickListener(v ->{
ScannerDialog scannerDialog = new ScannerDialog();
scannerDialog.setOnBarcodeScannedListener(code -> {
binding.referenceTicket.setText(code);
});
scannerDialog.show(getParentFragmentManager(), "scanner");
});
}
@Override
public void onResume() {
super.onResume();
AppCompatActivity activity = (AppCompatActivity) getActivity();
if (activity != null) {
activity.getSupportActionBar();
MaterialToolbar toolbar = activity.findViewById(R.id.toolbar);
if(toolbar!= null){
toolbar.setTitle("Ticket Gagnant");
}
}
}
}

View File

@@ -0,0 +1,28 @@
package com.example.quiz.data;
import android.os.AsyncTask;
import com.example.quiz.data.dao.UserActivityLogDao;
import com.example.quiz.data.entity.Logs;
import java.util.concurrent.Executors;
public class ActivityLogger {
public static UserActivityLogDao dao;
public static void init(UserActivityLogDao iDao){
dao = iDao;
}
public static void log(String userId, String activity, String description, Long timestamp){
Executors.newSingleThreadExecutor().execute(()->{
Logs userLog = new Logs();
userLog.setUserId(userId);
userLog.setAction(activity);
userLog.setDescription(description);
userLog.setTimestamp(timestamp);
dao.insertLog(userLog);
});
}
}

View File

@@ -1,72 +0,0 @@
package com.example.quiz.data;
import com.example.quiz.data.model.Bet;
import com.example.quiz.data.model.Horse;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
public class BetsBank {
public List<Bet> getBet() {
return Arrays.asList(
new Bet(
1,
"Course de chevaux",
LocalDate.of(2025,10,1),
Arrays.asList(
new Horse(1),
new Horse(2),
new Horse(3),
new Horse(4)
)
),
new Bet(
2,
"Course de chevaux 2",
LocalDate.of(2025,10,1),
Arrays.asList(
new Horse(4),
new Horse(5),
new Horse(7),
new Horse(6)
)
),
new Bet(
3,
"Course de chevaux 3",
LocalDate.of(2025,10,1),
Arrays.asList(
new Horse(10),
new Horse(90),
new Horse(45),
new Horse(9)
)
),
new Bet(
4,
"Course de chevaux 4",
LocalDate.of(2025,10,1),
Arrays.asList(
new Horse(5),
new Horse(33),
new Horse(26),
new Horse(99),
new Horse(100),
new Horse(101),
new Horse(102),
new Horse(103),
new Horse(104),
new Horse(105)
)
)
);
}
public static BetsBank instance;
public static BetsBank getInstance(){
if(instance == null){
instance = new BetsBank();
}
return instance;
}
}

View File

@@ -1,69 +0,0 @@
package com.example.quiz.data;
import com.example.quiz.data.model.Question;
import java.util.Arrays;
import java.util.List;
public class QuestionsBank {
public List<Question> getQuestions() {
return Arrays.asList(
new Question(
"Who is the creator of Android?",
Arrays.asList(
"Andy Rubin",
"Steve Wozniak",
"Jake Wharton",
"Paul Smith"
),
0
),
new Question(
"When did the first man land on the moon?",
Arrays.asList(
"1958",
"1962",
"1967",
"1969"
),
3
),
new Question(
"What is the house number of The Simpsons?",
Arrays.asList(
"42",
"101",
"666",
"742"
),
3
),
new Question(
"Who painteddid the Mona Lisa paint?",
Arrays.asList(
"Michelangelo",
"Leonardo Da Vinci",
"Raphael",
"Caravaggio"
),
1
),
new Question(
"What is the country top-level domain of Belgium?",
Arrays.asList(
".bg",
".bm",
".bl",
".be"
),
3
)
);
}
private static QuestionsBank instance;
public static QuestionsBank getInstance() {
if (instance == null) {
instance = new QuestionsBank();
}
return instance;
}
}

View File

@@ -10,27 +10,33 @@ import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.example.quiz.R; import com.example.quiz.R;
import com.example.quiz.data.model.Bet; import com.example.quiz.data.model.Course;
import com.example.quiz.viewModel.SharedViewModel;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class BetsAdapter extends RecyclerView.Adapter<BetsAdapter.BetViewHolder> { public class BetsAdapter extends RecyclerView.Adapter<BetsAdapter.BetViewHolder> {
private List<Bet> bets = new ArrayList<>();
private List<Course> bets = new ArrayList<>();
private onItemClickListener listener; private onItemClickListener listener;
public BetsAdapter() {
}
public interface onItemClickListener { public interface onItemClickListener {
void onItemClick(int position); void onItemClick(Course bet);
} }
public void setOnItemClickListener(onItemClickListener listener){ public void setOnItemClickListener(onItemClickListener listener){
this.listener = listener; this.listener = listener;
} }
public void setBets(List<Course> bets){
public void setBets(List<Bet> bets){
this.bets = bets; this.bets = bets;
notifyDataSetChanged(); notifyDataSetChanged();
} }
@@ -38,38 +44,41 @@ public class BetsAdapter extends RecyclerView.Adapter<BetsAdapter.BetViewHolder>
@NonNull @NonNull
@Override @Override
public BetViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public BetViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_bet, parent, false); View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_bet, parent, false);
return new BetViewHolder(view); return new BetViewHolder(view);
} }
@Override @Override
public void onBindViewHolder(@NonNull BetViewHolder holder, int position) { public void onBindViewHolder(@NonNull BetViewHolder holder, int position) {
Bet bet = bets.get(position); Course bet = bets.get(position);
holder.tvDate.setText(String.valueOf(bet.getDate())); OffsetDateTime dateTime = OffsetDateTime.parse(bet.getHeureDepartPrevue());
holder.tvName.setText(String.valueOf(bet.getName())); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
String formattedDate = dateTime.format(formatter);
holder.tvDate.setText(formattedDate);
holder.tvName.setText(bet.getNom());
String reunion = "Reunion "+bet.getReunionNumero();
holder.tvReunion.setText(reunion);
holder.itemView.setOnClickListener(v -> { holder.itemView.setOnClickListener(v -> {
if(listener != null){ if (listener != null) listener.onItemClick(bet);
listener.onItemClick(bet.getId());
}
}); });
} }
@Override @Override
public int getItemCount(){ public int getItemCount(){
return bets.size(); return bets.size();
} }
static class BetViewHolder extends RecyclerView.ViewHolder{ static class BetViewHolder extends RecyclerView.ViewHolder{
TextView tvName, tvDate; TextView tvName, tvDate, tvReunion;
public BetViewHolder(@NonNull View itemView) { public BetViewHolder(@NonNull View itemView) {
super(itemView); super(itemView);
tvName = itemView.findViewById(R.id.tvName); tvName = itemView.findViewById(R.id.tvName);
tvDate = itemView.findViewById(R.id.tvDate); tvDate = itemView.findViewById(R.id.tvDate);
tvReunion = itemView.findViewById(R.id.tvReunion);
} }
} }
} }

View File

@@ -0,0 +1,105 @@
package com.example.quiz.data.adapter;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.quiz.R;
import com.example.quiz.data.model.Pari;
import com.example.quiz.data.model.ParisResponse;
import java.util.ArrayList;
import java.util.List;
public class LastBetsAdapter extends RecyclerView.Adapter<LastBetsAdapter.LastBetsViewHolder> {
private List<ParisResponse> listeParis = new ArrayList<>(); // évite les NPE
private OnBetClick onBetClick; // peut être null si tu veux
public interface OnBetClick {
void onItemClick(ParisResponse pari);
}
public LastBetsAdapter(List<ParisResponse> listeParis, OnBetClick onBetClick) {
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) {
if (newList == null) {
this.listeParis = new ArrayList<>();
} else {
this.listeParis = newList;
}
notifyDataSetChanged();
}
public void addItem(ParisResponse pari) {
if (pari == null) return;
this.listeParis.add(pari);
notifyItemInserted(listeParis.size() - 1);
}
public void clear() {
this.listeParis.clear();
notifyDataSetChanged();
}
@NonNull
@Override
public LastBetsViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.last_bets_item, parent, false);
return new LastBetsViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull LastBetsViewHolder holder, int position) {
ParisResponse pari = listeParis.get(position);
if (pari == null) return;
holder.typeOfCourse.setText(pari.getCourseNom());
holder.refenceTicket.setText(pari.getNumeroTicket());
holder.betType.setText(pari.getFormules().size()>0 ? pari.getFormules().get(0):"Pas de formule");
holder.course.setText("Course: " + pari.getCourseId());
// Chevaux : join en toute sécurité (gère Integer list ou String list)
holder.horses.setText(pari.getCombinaison());
// Mise (défensif)
holder.mise.setText("Mise: " + pari.getMiseTotale() + " CFA");
// click listener défensif
holder.itemView.setOnClickListener(v -> {
if (onBetClick != null) onBetClick.onItemClick(pari);
});
}
@Override
public int getItemCount() {
return (listeParis != null) ? listeParis.size() : 0;
}
public static class LastBetsViewHolder extends RecyclerView.ViewHolder {
TextView betType, horses, course, mise, refenceTicket, typeOfCourse;
public LastBetsViewHolder(@NonNull View itemView) {
super(itemView);
betType = itemView.findViewById(R.id.last_bet_type);
course = itemView.findViewById(R.id.last_bet_course);
horses = itemView.findViewById(R.id.last_bet_horses);
mise = itemView.findViewById(R.id.last_bet_mise);
refenceTicket = itemView.findViewById(R.id.reference_ticket);
typeOfCourse = itemView.findViewById(R.id.type_of_course);
}
}
}

View File

@@ -0,0 +1,63 @@
package com.example.quiz.data.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.example.quiz.R;
import com.example.quiz.data.entity.Logs;
import org.jspecify.annotations.NonNull;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
public class LogsAdapter extends RecyclerView.Adapter<LogsAdapter.LogViewHolder> {
private List<Logs> logs = new ArrayList<>();
public void setLogs(List<Logs> logs) {
this.logs = logs;
notifyDataSetChanged();
}
@NonNull
@Override
public LogViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_log, parent, false);
return new LogViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull LogViewHolder holder, int position) {
Logs log = logs.get(position);
holder.textAction.setText(log.getAction());
holder.textDescription.setText(log.getDescription());
SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy • HH:mm", Locale.getDefault());
holder.textDate.setText(sdf.format(new Date(log.getTimestamp())));
}
@Override
public int getItemCount() {
return logs.size();
}
static class LogViewHolder extends RecyclerView.ViewHolder {
TextView textAction, textDescription, textDate;
public LogViewHolder(@NonNull View itemView) {
super(itemView);
textAction = itemView.findViewById(R.id.textAction);
textDescription = itemView.findViewById(R.id.textDescription);
textDate = itemView.findViewById(R.id.textDate);
}
}
}

View File

@@ -0,0 +1,78 @@
package com.example.quiz.data.adapter;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.quiz.R;
import com.example.quiz.data.model.PointDeVente;
import java.util.ArrayList;
import java.util.List;
public class PointDeVenteAdapter extends RecyclerView.Adapter<PointDeVenteAdapter.ViewHolder> {
private List<PointDeVente> points = new ArrayList<>();
private OnItemClickListener listener;
public PointDeVenteAdapter(){
}
// Interface pour gérer le clic sur un item
public interface OnItemClickListener {
void onItemClick(PointDeVente point);
}
public void setOnItemClickListener(OnItemClickListener listener) {
this.listener = listener;
}
public void setPoints(List<PointDeVente> points) {
this.points = points;
notifyDataSetChanged();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_point_de_vente, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
PointDeVente point = points.get(position);
holder.txtNom.setText(point.getNom());
String adresse = point.getVille()+", "+point.getAdresse();
holder.txtAdresse.setText(adresse);
holder.itemView.setOnClickListener(v -> {
if (listener != null) listener.onItemClick(point);
});
}
@Override
public int getItemCount() {
return points.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView txtNom;
TextView txtAdresse;
public ViewHolder(@NonNull View itemView) {
super(itemView);
txtNom = itemView.findViewById(R.id.txtNom);
txtAdresse = itemView.findViewById(R.id.txtAdresse);
}
}
}

View File

@@ -0,0 +1,72 @@
package com.example.quiz.data.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.quiz.R;
import com.example.quiz.data.model.Reunion;
import java.util.ArrayList;
import java.util.List;
public class ReunionAdapter extends RecyclerView.Adapter<ReunionAdapter.ReunionViewHolder> {
private List<Reunion> reunions = List.of();
private onItemClickListener listener;
public interface onItemClickListener {
void onItemClick(Reunion reunion);
}
public void setOnItemClickListener(onItemClickListener listener){
this.listener = listener;
}
public void setReunions(List<Reunion> reunions){
this.reunions = reunions;
notifyDataSetChanged();
}
@NonNull
@Override
public ReunionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_reunion, parent, false);
return new ReunionViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ReunionViewHolder holder, int position){
Reunion reunion = reunions.get(position);
holder.reunionName.setText(String.valueOf(reunion.getNom()));
holder.reunionDate.setText(String.valueOf(reunion.getDate()));
holder.itemView.setOnClickListener(v -> {
if(listener != null){
listener.onItemClick(reunion);
}
});
}
@Override
public int getItemCount() {
return reunions.size();
}
static class ReunionViewHolder extends RecyclerView.ViewHolder {
TextView reunionName, reunionDate;
public ReunionViewHolder(View itemView){
super(itemView);
reunionName = itemView.findViewById(R.id.reunionName);
reunionDate = itemView.findViewById(R.id.reunionDate);
}
}
}

View File

@@ -0,0 +1,80 @@
package com.example.quiz.data.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.quiz.R;
import com.example.quiz.data.model.TypeOfBet;
import java.util.List;
public class TypeOfBetAdapter extends RecyclerView.Adapter<TypeOfBetAdapter.TypeOfBetViewHolder> {
private List<TypeOfBet> types;
private onItemClickListener listener;
private int selectedPosition = -1;
public interface onItemClickListener{
void onItemClick(TypeOfBet type);
}
public void setOnItemClickListener(TypeOfBetAdapter.onItemClickListener listener){
this.listener = listener;
}
public TypeOfBetAdapter(List<TypeOfBet> types){
this.types = types;
}
public void setTypes(List<TypeOfBet> types){
this.types = types;
notifyDataSetChanged();
}
@NonNull
@Override
public TypeOfBetViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.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());
if(selectedPosition != position){
holder.itemView.setBackgroundResource(R.drawable.item_gradient_bg);
}else{
holder.itemView.setBackgroundResource(R.drawable.item_gradient_bg_selected);
}
holder.itemView.setOnClickListener(v->{
if(listener != null){
listener.onItemClick(type);
}
int previousPosition = selectedPosition;
selectedPosition = holder.getAdapterPosition();
notifyItemChanged(previousPosition);
notifyItemChanged(selectedPosition);
});
}
@Override
public int getItemCount() {
return types.size();
}
public class TypeOfBetViewHolder extends RecyclerView.ViewHolder {
TextView type_of_bet_name;
public TypeOfBetViewHolder(@NonNull View itemView) {
super(itemView);
type_of_bet_name = itemView.findViewById(R.id.type_of_bet_name);
}
}
}

View File

@@ -0,0 +1,20 @@
package com.example.quiz.data.dao;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import com.example.quiz.data.entity.Logs;
import java.util.List;
@Dao
public interface UserActivityLogDao {
@Insert
void insertLog(Logs log);
@Query(" SELECT * FROM user_activity_logs WHERE date(timestamp / 1000, 'unixepoch', 'localtime') = date('now', 'localtime') ORDER BY timestamp DESC ")
LiveData<List<Logs>> getTodayLogs();
}

View File

@@ -0,0 +1,30 @@
package com.example.quiz.data.database;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import com.example.quiz.data.dao.UserActivityLogDao;
import com.example.quiz.data.entity.Logs;
@Database(entities = {Logs.class}, version = 3, exportSchema = false)
public abstract class AppDataBase extends RoomDatabase {
private static AppDataBase instance;
public abstract UserActivityLogDao logsDao();
public static synchronized AppDataBase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(
context.getApplicationContext(),
AppDataBase.class,
"app-db"
).fallbackToDestructiveMigration() // optionnel
.build();
}
return instance;
}
}

View File

@@ -0,0 +1,34 @@
package com.example.quiz.data.database;
import android.content.Context;
import androidx.room.Room;
public class DatabaseClient {
private static DatabaseClient instance;
private AppDataBase appDatabase;
private Context context;
private DatabaseClient(Context context) {
this.context = context;
appDatabase = Room.databaseBuilder(context,
AppDataBase.class, "QuizDB")
.fallbackToDestructiveMigration()
.build();
}
public static synchronized DatabaseClient getInstance(Context context) {
if (instance == null) {
instance = new DatabaseClient(context);
}
return instance;
}
public AppDataBase getAppDatabase() {
return appDatabase;
}
}

View File

@@ -0,0 +1,33 @@
package com.example.quiz.data.database;
import android.content.Context;
import androidx.room.Room;
import com.example.quiz.data.dao.UserActivityLogDao;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import dagger.hilt.InstallIn;
import dagger.hilt.android.qualifiers.ApplicationContext;
import dagger.hilt.components.SingletonComponent;
@Module
@InstallIn(SingletonComponent.class)
public class DatabaseModule {
@Provides
@Singleton
public static AppDataBase provideDatabase(@ApplicationContext Context context) {
return Room.databaseBuilder(context, AppDataBase.class, "QuizDB")
.fallbackToDestructiveMigration()
.build();
}
@Provides
public static UserActivityLogDao provideUserActivityLogDao(AppDataBase db) {
return db.logsDao(); // ton DAO
}
}

View File

@@ -0,0 +1,59 @@
package com.example.quiz.data.entity;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity(tableName = "user_activity_logs")
public class Logs {
@PrimaryKey(autoGenerate = true)
private int id;
private String userId;
private String action;
private String description;
private Long timestamp;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public Long getTimestamp() {
return timestamp;
}
public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}
}

View File

@@ -1,52 +0,0 @@
package com.example.quiz.data.model;
import java.time.LocalDate;
import java.util.List;
public class Bet {
private int id;
private String name;
private LocalDate date;
private List<Horse> horses;
public List<Horse> getHorses() {
return horses;
}
public void setHorses(List<Horse> horses) {
this.horses = horses;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public LocalDate getDate() {
return date;
}
public void setDate(LocalDate date) {
this.date = date;
}
public Bet(int id, String name, LocalDate date, List<Horse> horses) {
this.id = id;
this.name = name;
this.date = date;
this.horses = horses;
}
}

View File

@@ -0,0 +1,57 @@
package com.example.quiz.data.model;
public class Cheval {
private int number;
private String nom;
private String numero;
private boolean nonPartant;
private boolean nonEcurie;
public Cheval(int number, String nom, String numero, boolean nonPartant, boolean nonEcurie) {
this.number = number;
this.nom = nom;
this.numero = numero;
this.nonPartant = nonPartant;
this.nonEcurie = nonEcurie;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public String getNumero() {
return numero;
}
public void setNumero(String numero) {
this.numero = numero;
}
public boolean isNonPartant() {
return nonPartant;
}
public void setNonPartant(boolean nonPartant) {
this.nonPartant = nonPartant;
}
public boolean isNonEcurie() {
return nonEcurie;
}
public void setNonEcurie(boolean nonEcurie) {
this.nonEcurie = nonEcurie;
}
}

View File

@@ -0,0 +1,82 @@
package com.example.quiz.data.model;
import com.example.quiz.data.model.enums.CourseType;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
public class Course {
private long id;
private long hippodromeId;
private long reunionNumero;
private String reunionDate;
private String nom;
private int numero;
private String heureDepartPrevue;
private boolean bettingOpen;
private String bettingClosedAt;
private String discipline;
private int distanceMetres;
private String categorie;
private int nombrePartants;
private Statut statut;
private boolean annulee;
private boolean reporteeMemeJour;
private boolean reporteeAutreJour;
private boolean incidentTechnique;
private List<Integer> nonPartants;
private List<TypeParis> typesParisOuverts;
// ===== ENUMS =====
public enum Statut {
BROUILLON,
VALIDE,
OUVERT,
FERME,
RESULTAT_PROVISOIRE,
RESULTAT_OFFICIEL,
REGLEE,
ANNULEE
}
public enum TypeParis {
COUPLE_GAGNANT,
COUPLE_PLACE,
TIERCE,
QUARTE,
QUINTE
}
// ===== GETTERS & SETTERS =====
public long getId() { return id; }
public long getHippodromeId() { return hippodromeId; }
public long getReunionNumero() { return reunionNumero; }
public String getReunionDate() { return reunionDate; }
public String getNom() { return nom; }
public int getNumero() { return numero; }
public String getHeureDepartPrevue() { return heureDepartPrevue; }
public boolean isBettingOpen() { return bettingOpen; }
public String getBettingClosedAt() { return bettingClosedAt; }
public String getDiscipline() { return discipline; }
public int getDistanceMetres() { return distanceMetres; }
public String getCategorie() { return categorie; }
public int getNombrePartants() { return nombrePartants; }
public Statut getStatut() { return statut; }
public boolean isAnnulee() { return annulee; }
public boolean isReporteeMemeJour() { return reporteeMemeJour; }
public boolean isReporteeAutreJour() { return reporteeAutreJour; }
public boolean isIncidentTechnique() { return incidentTechnique; }
public List<Integer> getNonPartants() { return nonPartants; }
public List<TypeParis> getTypesParisOuverts() { return typesParisOuverts; }
}

View File

@@ -0,0 +1,67 @@
package com.example.quiz.data.model;
public class Hippodrome {
private int id;
private String nom;
private String ville;
private String pays;
private int capacite;
private String description;
public Hippodrome(int id, String nom, String ville, String pays, int capacite, String description) {
this.id = id;
this.nom = nom;
this.ville = ville;
this.pays = pays;
this.capacite = capacite;
this.description = description;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public String getVille() {
return ville;
}
public void setVille(String ville) {
this.ville = ville;
}
public String getPays() {
return pays;
}
public void setPays(String pays) {
this.pays = pays;
}
public int getCapacite() {
return capacite;
}
public void setCapacite(int capacite) {
this.capacite = capacite;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

View File

@@ -1,18 +0,0 @@
package com.example.quiz.data.model;
public class Horse {
private Integer number;
public Horse(Integer number) {
this.number = number;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
}

View File

@@ -0,0 +1,62 @@
package com.example.quiz.data.model;
import com.example.quiz.data.model.enums.Pageable;
import java.util.List;
public class PagedModel<T> {
private Long totalPages;
private Long totalElements;
private Pageable pageable;
private Long numberOfElements;
private Long size;
private List<T> content;
public Long getTotalPages() {
return totalPages;
}
public void setTotalPages(Long totalPages) {
this.totalPages = totalPages;
}
public Long getTotalElements() {
return totalElements;
}
public void setTotalElements(Long totalElements) {
this.totalElements = totalElements;
}
public Pageable getPageable() {
return pageable;
}
public void setPageable(Pageable pageable) {
this.pageable = pageable;
}
public Long getNumberOfElements() {
return numberOfElements;
}
public void setNumberOfElements(Long numberOfElements) {
this.numberOfElements = numberOfElements;
}
public Long getSize() {
return size;
}
public void setSize(Long size) {
this.size = size;
}
public List<T> getContent() {
return content;
}
public void setContent(List<T> content) {
this.content = content;
}
}

View File

@@ -0,0 +1,54 @@
package com.example.quiz.data.model;
import com.example.quiz.data.model.dtos.PariCourseDto;
import com.example.quiz.data.model.enums.PariStatut;
import java.util.List;
import java.util.Map;
import java.util.List;
import java.util.List;
public class Pari {
private String dateHeurePrise; // ISO String
private long courseId;
private long agentId;
private long terminalId;
private long pointDeVenteId;
private List<PariMise> typesParisMises;
private List<String> formules;
private String combinaison;
private double coefficient;
private String devise;
private String notes;
public Pari(String dateHeurePrise, long courseId, long agentId, long terminalId, long pointDeVenteId, List<PariMise> typesParisMises, List<String> formules, String combinaison, double coefficient, String devise, String notes) {
this.dateHeurePrise = dateHeurePrise;
this.courseId = courseId;
this.agentId = agentId;
this.terminalId = terminalId;
this.pointDeVenteId = pointDeVenteId;
this.typesParisMises = typesParisMises;
this.formules = formules;
this.combinaison = combinaison;
this.coefficient = coefficient;
this.devise = devise;
this.notes = notes;
}
public String getDateHeurePrise() { return dateHeurePrise; }
public long getCourseId() { return courseId; }
public long getAgentId() { return agentId; }
public long getTerminalId() { return terminalId; }
public long getPointDeVenteId() { return pointDeVenteId; }
public List<PariMise> getTypesParisMises() { return typesParisMises; }
public List<String> getFormules() { return formules; }
public String getCombinaison() { return combinaison; }
public double getCoefficient() { return coefficient; }
public String getDevise() { return devise; }
public String getNotes() { return notes; }
// ===== INNER CLASS =====
}

View File

@@ -0,0 +1,19 @@
package com.example.quiz.data.model;
public class PariMise {
private String typePari;
private long miseTotale;
public PariMise(String typePari, long miseTotale) {
this.typePari = typePari;
this.miseTotale = miseTotale;
}
public String getTypePari() {
return typePari;
}
public long getMiseTotale() {
return miseTotale;
}
}

View File

@@ -0,0 +1,100 @@
package com.example.quiz.data.model;
import java.io.Serializable;
import java.util.List;
public class ParisResponse implements Serializable {
private long id;
private String numeroTicket;
private String dateHeurePrise;
private String qrPayload;
private long courseId;
private String courseNom;
private int courseNumero;
private String hippodromeNom;
private long reunionNumero;
private String reunionDate;
private String heureDepartPrevue;
private int nombrePartants;
private List<Integer> nonPartants;
private long agentId;
private String agentCode;
private String agentNom;
private long terminalId;
private String terminalNumeroSerie;
private long pointDeVenteId;
private String pointDeVenteCode;
private String pointDeVenteNom;
private List<PariMise> typesParisMises;
private long miseTotale;
private List<String> formules;
private double coefficient;
private String combinaison;
private StatutPari statutPari;
private long gainCalcule;
private String devise;
private String dateReglement;
private String datePaiement;
private String notes;
// ================= ENUM =================
public enum StatutPari {
ENREGISTRE,
VALIDE,
PAYE,
ANNULE,
PERDANT,
GAGNANT
}
// ================= INNER CLASS =================
// ================= GETTERS =================
public long getId() { return id; }
public String getNumeroTicket() { return numeroTicket; }
public String getDateHeurePrise() { return dateHeurePrise; }
public String getQrPayload() { return qrPayload; }
public long getCourseId() { return courseId; }
public String getCourseNom() { return courseNom; }
public int getCourseNumero() { return courseNumero; }
public String getHippodromeNom() { return hippodromeNom; }
public long getReunionNumero() { return reunionNumero; }
public String getReunionDate() { return reunionDate; }
public String getHeureDepartPrevue() { return heureDepartPrevue; }
public int getNombrePartants() { return nombrePartants; }
public List<Integer> getNonPartants() { return nonPartants; }
public long getAgentId() { return agentId; }
public String getAgentCode() { return agentCode; }
public String getAgentNom() { return agentNom; }
public long getTerminalId() { return terminalId; }
public String getTerminalNumeroSerie() { return terminalNumeroSerie; }
public long getPointDeVenteId() { return pointDeVenteId; }
public String getPointDeVenteCode() { return pointDeVenteCode; }
public String getPointDeVenteNom() { return pointDeVenteNom; }
public List<PariMise> getTypesParisMises() { return typesParisMises; }
public long getMiseTotale() { return miseTotale; }
public List<String> getFormules() { return formules; }
public double getCoefficient() { return coefficient; }
public String getCombinaison() { return combinaison; }
public StatutPari getStatutPari() { return statutPari; }
public long getGainCalcule() { return gainCalcule; }
public String getDevise() { return devise; }
public String getDateReglement() { return dateReglement; }
public String getDatePaiement() { return datePaiement; }
public String getNotes() { return notes; }
}

View File

@@ -0,0 +1,92 @@
package com.example.quiz.data.model;
public class PointDeVente {
private Long id;
private String code;
private String nom;
private String adresse;
private String ville;
private String latitude;
private String longitude;
private PointStatus statut;
public PointDeVente(Long id, String code, String nom, String adresse, String ville, String latitude, String longitude, PointStatus statut) {
this.id = id;
this.code = code;
this.nom = nom;
this.adresse = adresse;
this.ville = ville;
this.latitude = latitude;
this.longitude = longitude;
this.statut = statut;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public String getAdresse() {
return adresse;
}
public void setAdresse(String adresse) {
this.adresse = adresse;
}
public String getVille() {
return ville;
}
public void setVille(String ville) {
this.ville = ville;
}
public String getLatitude() {
return latitude;
}
public void setLatitude(String latitude) {
this.latitude = latitude;
}
public String getLongitude() {
return longitude;
}
public void setLongitude(String longitude) {
this.longitude = longitude;
}
public PointStatus getStatut() {
return statut;
}
public void setStatut(PointStatus statut) {
this.statut = statut;
}
public enum PointStatus {
ACTIF,
INACTIF
}
}

View File

@@ -0,0 +1,79 @@
package com.example.quiz.data.model;
import java.time.LocalDate;
public class Reunion {
private int id;
private String code;
private String nom;
private String date;
private int numero;
private Hippodrome hippodrome;
private int totalCourses;
public Reunion(int id, String code, String nom, String date, int numero, Hippodrome hippodrome, int totalCourses) {
this.id = id;
this.code = code;
this.nom = nom;
this.date = date;
this.numero = numero;
this.hippodrome = hippodrome;
this.totalCourses = totalCourses;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public int getNumero() {
return numero;
}
public void setNumero(int numero) {
this.numero = numero;
}
public Hippodrome getHippodrome() {
return hippodrome;
}
public void setHippodrome(Hippodrome hippodrome) {
this.hippodrome = hippodrome;
}
public int getTotalCourses() {
return totalCourses;
}
public void setTotalCourses(int totalCourses) {
this.totalCourses = totalCourses;
}
}

View File

@@ -0,0 +1,114 @@
package com.example.quiz.data.model;
public class Tpe {
private String numeroSerie;
private long pointDeVenteId;
private String statut;
private String versionLogicielle;
private String typeTerminal;
private String plateforme;
private String modeleAppareil;
private String systemeExploitation;
private String versionOs;
private String adresseIp;
private String adresseMac;
private String journalSession;
public Tpe() {}
public String getNumeroSerie() {
return numeroSerie;
}
public void setNumeroSerie(String numeroSerie) {
this.numeroSerie = numeroSerie;
}
public long getPointDeVenteId() {
return pointDeVenteId;
}
public void setPointDeVenteId(long pointDeVenteId) {
this.pointDeVenteId = pointDeVenteId;
}
public String getStatut() {
return statut;
}
public void setStatut(String statut) {
this.statut = statut;
}
public String getVersionLogicielle() {
return versionLogicielle;
}
public void setVersionLogicielle(String versionLogicielle) {
this.versionLogicielle = versionLogicielle;
}
public String getTypeTerminal() {
return typeTerminal;
}
public void setTypeTerminal(String typeTerminal) {
this.typeTerminal = typeTerminal;
}
public String getPlateforme() {
return plateforme;
}
public void setPlateforme(String plateforme) {
this.plateforme = plateforme;
}
public String getModeleAppareil() {
return modeleAppareil;
}
public void setModeleAppareil(String modeleAppareil) {
this.modeleAppareil = modeleAppareil;
}
public String getSystemeExploitation() {
return systemeExploitation;
}
public void setSystemeExploitation(String systemeExploitation) {
this.systemeExploitation = systemeExploitation;
}
public String getVersionOs() {
return versionOs;
}
public void setVersionOs(String versionOs) {
this.versionOs = versionOs;
}
public String getAdresseIp() {
return adresseIp;
}
public void setAdresseIp(String adresseIp) {
this.adresseIp = adresseIp;
}
public String getAdresseMac() {
return adresseMac;
}
public void setAdresseMac(String adresseMac) {
this.adresseMac = adresseMac;
}
public String getJournalSession() {
return journalSession;
}
public void setJournalSession(String journalSession) {
this.journalSession = journalSession;
}
}

View File

@@ -0,0 +1,51 @@
package com.example.quiz.data.model;
public class TpeResponse extends Tpe {
private long id;
private String derniereConnexion;
private String derniereConnexionAgent;
private String derniereDeconnexionAgent;
private boolean assigned;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getDerniereConnexion() {
return derniereConnexion;
}
public void setDerniereConnexion(String derniereConnexion) {
this.derniereConnexion = derniereConnexion;
}
public String getDerniereConnexionAgent() {
return derniereConnexionAgent;
}
public void setDerniereConnexionAgent(String derniereConnexionAgent) {
this.derniereConnexionAgent = derniereConnexionAgent;
}
public String getDerniereDeconnexionAgent() {
return derniereDeconnexionAgent;
}
public void setDerniereDeconnexionAgent(String derniereDeconnexionAgent) {
this.derniereDeconnexionAgent = derniereDeconnexionAgent;
}
public boolean isAssigned() {
return assigned;
}
public void setAssigned(boolean assigned) {
this.assigned = assigned;
}
}

View File

@@ -0,0 +1,38 @@
package com.example.quiz.data.model;
public class TypeOfBet {
private String label;
private Course.TypeParis name;
private int numberOfHorse;
public TypeOfBet(String label, Course.TypeParis name, int numberOfHorse) {
this.label = label;
this.name = name;
this.numberOfHorse = numberOfHorse;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public Course.TypeParis getName() {
return name;
}
public void setName(Course.TypeParis name) {
this.name = name;
}
public int getNumberOfHorse() {
return numberOfHorse;
}
public void setNumberOfHorse(int numberOfHorse) {
this.numberOfHorse = numberOfHorse;
}
}

View File

@@ -0,0 +1,50 @@
package com.example.quiz.data.model.dtos;
public class PariCourseDto {
private int id;
private String nom;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
private String type;
public PariCourseDto(int id) {
this.id = id;
}
public PariCourseDto(String nom) {
this.nom = nom;
}
public PariCourseDto(int id, String nom, String type) {
this.id = id;
this.nom = nom;
this.type = type;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}

View File

@@ -0,0 +1,68 @@
package com.example.quiz.data.model.dtos.auth;
public class Agent {
private Long id;
private String code;
private String nom;
private String prenom;
private String phone;
private String zone;
private String fonction;
public Agent(){}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public String getPrenom() {
return prenom;
}
public void setPrenom(String prenom) {
this.prenom = prenom;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getZone() {
return zone;
}
public void setZone(String zone) {
this.zone = zone;
}
public String getFonction() {
return fonction;
}
public void setFonction(String fonction) {
this.fonction = fonction;
}
}

View File

@@ -0,0 +1,38 @@
package com.example.quiz.data.model.dtos.auth;
public class LoginPayload {
private String code;
private String pin;
private Long terminalId;
public LoginPayload(String code, String pin, Long terminalId) {
this.code = code;
this.pin = pin;
this.terminalId = terminalId;
}
public Long getTerminalId() {
return terminalId;
}
public void setTerminalId(Long terminalId) {
this.terminalId = terminalId;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getPin() {
return pin;
}
public void setPin(String pin) {
this.pin = pin;
}
}

View File

@@ -0,0 +1,27 @@
package com.example.quiz.data.model.dtos.auth;
public class LoginResponse {
private String token;
private Agent agent;
public LoginResponse(String token, Agent agent) {
this.token = token;
this.agent = agent;
}
public Agent getUser() {
return agent;
}
public void setUser(Agent agent) {
this.agent = agent;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}

View File

@@ -0,0 +1,237 @@
package com.example.quiz.data.model.dtos.auth;
public class User {
private int id;
private String code;
private String profile;
private String principalCode;
private String caisseProfile;
private String statut;
private String zone;
private String kiosk;
private String fonction;
private String dateEmbauche;
private String derniereConnexion;
private String nom;
private String prenom;
private String autresNom;
private String dateNaissance;
private String lieuNaissance;
private String ville;
private String adresse;
private String cni;
private double limiteInferieure;
private double limiteSuperieure;
private double limiteParTransaction;
private String autreAdresse1;
public User(int id, String code, String profile, String principalCode, String caisseProfile, String statut, String zone, String kiosk, String fonction, String dateEmbauche, String derniereConnexion, String nom, String prenom, String autresNom, String dateNaissance, String lieuNaissance, String ville, String adresse, String cni, double limiteInferieure, double limiteSuperieure, double limiteParTransaction, String autreAdresse1){
this.id = id;
this.code = code;
this.profile = profile;
this.principalCode = principalCode;
this.caisseProfile = caisseProfile;
this.statut = statut;
this.zone = zone;
this.kiosk = kiosk;
this.fonction = fonction;
this.dateEmbauche = dateEmbauche;
this.derniereConnexion = derniereConnexion;
this.nom = nom;
this.prenom = prenom;
this.autresNom = autresNom;
this.dateNaissance = dateNaissance;
this.lieuNaissance = lieuNaissance;
this.ville = ville;
this.adresse = adresse;
this.cni = cni;
this.limiteInferieure = limiteInferieure;
this.limiteSuperieure = limiteSuperieure;
this.limiteParTransaction = limiteParTransaction;
this.autreAdresse1 = autreAdresse1;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getProfile() {
return profile;
}
public void setProfile(String profile) {
this.profile = profile;
}
public String getPrincipalCode() {
return principalCode;
}
public void setPrincipalCode(String principalCode) {
this.principalCode = principalCode;
}
public String getCaisseProfile() {
return caisseProfile;
}
public void setCaisseProfile(String caisseProfile) {
this.caisseProfile = caisseProfile;
}
public String getStatut() {
return statut;
}
public void setStatut(String statut) {
this.statut = statut;
}
public String getZone() {
return zone;
}
public void setZone(String zone) {
this.zone = zone;
}
public String getKiosk() {
return kiosk;
}
public void setKiosk(String kiosk) {
this.kiosk = kiosk;
}
public String getFonction() {
return fonction;
}
public void setFonction(String fonction) {
this.fonction = fonction;
}
public String getDateEmbauche() {
return dateEmbauche;
}
public void setDateEmbauche(String dateEmbauche) {
this.dateEmbauche = dateEmbauche;
}
public String getDerniereConnexion() {
return derniereConnexion;
}
public void setDerniereConnexion(String derniereConnexion) {
this.derniereConnexion = derniereConnexion;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public String getPrenom() {
return prenom;
}
public void setPrenom(String prenom) {
this.prenom = prenom;
}
public String getAutresNom() {
return autresNom;
}
public void setAutresNom(String autresNom) {
this.autresNom = autresNom;
}
public String getDateNaissance() {
return dateNaissance;
}
public void setDateNaissance(String dateNaissance) {
this.dateNaissance = dateNaissance;
}
public String getLieuNaissance() {
return lieuNaissance;
}
public void setLieuNaissance(String lieuNaissance) {
this.lieuNaissance = lieuNaissance;
}
public String getVille() {
return ville;
}
public void setVille(String ville) {
this.ville = ville;
}
public String getAdresse() {
return adresse;
}
public void setAdresse(String adresse) {
this.adresse = adresse;
}
public String getCni() {
return cni;
}
public void setCni(String cni) {
this.cni = cni;
}
public double getLimiteInferieure() {
return limiteInferieure;
}
public void setLimiteInferieure(double limiteInferieure) {
this.limiteInferieure = limiteInferieure;
}
public double getLimiteSuperieure() {
return limiteSuperieure;
}
public void setLimiteSuperieure(double limiteSuperieure) {
this.limiteSuperieure = limiteSuperieure;
}
public double getLimiteParTransaction() {
return limiteParTransaction;
}
public void setLimiteParTransaction(double limiteParTransaction) {
this.limiteParTransaction = limiteParTransaction;
}
public String getAutreAdresse1() {
return autreAdresse1;
}
public void setAutreAdresse1(String autreAdresse1) {
this.autreAdresse1 = autreAdresse1;
}
}

View File

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

View File

@@ -0,0 +1,38 @@
package com.example.quiz.data.model.enums;
import java.io.Serializable;
public class Pageable implements Serializable {
private boolean unpaged;
private boolean paged;
private int pageNumber;
private int pageSize;
private long offset; // ⚠️ long obligatoire
private Sort sort;
public boolean isUnpaged() {
return unpaged;
}
public boolean isPaged() {
return paged;
}
public int getPageNumber() {
return pageNumber;
}
public int getPageSize() {
return pageSize;
}
public long getOffset() {
return offset;
}
public Sort getSort() {
return sort;
}
}

View File

@@ -0,0 +1,7 @@
package com.example.quiz.data.model.enums;
public enum PariStatut {
EN_ATTENTE,
PERDANT,
VALIDEE
}

View File

@@ -0,0 +1,23 @@
package com.example.quiz.data.model.enums;
import java.io.Serializable;
public class Sort implements Serializable {
private boolean unsorted;
private boolean sorted;
private boolean empty;
public boolean isUnsorted() {
return unsorted;
}
public boolean isSorted() {
return sorted;
}
public boolean isEmpty() {
return empty;
}
}

View File

@@ -0,0 +1,71 @@
package com.example.quiz.data.remote;
import android.util.Log;
import androidx.annotation.NonNull;
import java.util.concurrent.TimeUnit;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import dagger.hilt.InstallIn;
import dagger.hilt.components.SingletonComponent;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
@Module
@InstallIn(SingletonComponent.class)
public class ApiClient {
private static final String BASE_URL = "https://boxer-adapting-bluegill.ngrok-free.app/api/";
@Provides
@Singleton
public AuthInterceptor provideInterceptor(TokenManager tokenManager){
return new AuthInterceptor(tokenManager);
}
@Provides
@Singleton
public HttpLoggingInterceptor provideLoggingInterceptor(){
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(@NonNull String s) {
Log.d("OkHttp", "log: "+s);
}
});
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
return loggingInterceptor;
}
@Provides
@Singleton
public OkHttpClient provideOkHttpClient(HttpLoggingInterceptor loggingInterceptor, AuthInterceptor authInterceptor){
return new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.addInterceptor(authInterceptor)
.connectTimeout(240, TimeUnit.SECONDS)
.readTimeout(240, TimeUnit.SECONDS)
.build();
}
@Provides
@Singleton
public Retrofit provideRetrofit(OkHttpClient okHttpClient){
return new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
@Provides
@Singleton
public ApiService provideApiService(Retrofit retrofit){
return retrofit.create(ApiService.class);
}
}

View File

@@ -0,0 +1,54 @@
package com.example.quiz.data.remote;
import com.example.quiz.data.model.Course;
import com.example.quiz.data.model.PagedModel;
import com.example.quiz.data.model.Pari;
import com.example.quiz.data.model.ParisResponse;
import com.example.quiz.data.model.PointDeVente;
import com.example.quiz.data.model.Reunion;
import com.example.quiz.data.model.Tpe;
import com.example.quiz.data.model.dtos.auth.LoginPayload;
import com.example.quiz.data.model.dtos.auth.LoginResponse;
import com.example.quiz.data.model.TpeResponse;
import java.util.List;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Path;
import retrofit2.http.Query;
public interface ApiService {
@GET("reunions/date/{date}")
Call<List<Reunion>> getReunions(@Path("date") String date);
@GET("courses")
Call<PagedModel<Course>> getCourses(@Query("reunionDate") String reunionDate);
@POST("paris")
Call<ParisResponse> createPari(@Body Pari pari);
@POST("auth/agent/login")
Call<LoginResponse> login(@Body LoginPayload loginPayload);
@GET("paris/agent/{agentId}/today")
Call<List<ParisResponse>> derniersParis(@Path("agentId") String agentId);
@PUT("pari/annuler/{numeroTicket}")
Call<ParisResponse> annulerPari(@Path("numeroTicket") String numeroTicket);
@GET("paris/agent/{agentId}/solde/course/{courseId}")
Call<Double> getSoldeByCourse(@Path("agentId") String createdBy, @Path("courseId") String courseId);
@GET("paris/agent/{agentId}/solde")
Call<Double> getSoldeByDay(@Path("agentId") String agentId, @Query("date") String day);
@POST("terminaux")
Call<TpeResponse> createTpe(@Body Tpe tpe);
@GET("points-de-vente")
Call<PagedModel<PointDeVente>> getPointsDeVente(@Query("nom") String nom);
}

View File

@@ -0,0 +1,35 @@
package com.example.quiz.data.remote;
import androidx.annotation.NonNull;
import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
public class AuthInterceptor implements Interceptor {
private final TokenManager tokenManager;
public AuthInterceptor(TokenManager tokenManager) {
this.tokenManager = tokenManager;
}
@NonNull
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
String token = tokenManager.getToken();
if (token != null) {
Request newRequest = originalRequest.newBuilder()
.addHeader("Authorization", "Bearer " + token)
.build();
return chain.proceed(newRequest);
}
return chain.proceed(originalRequest);
}
}

View File

@@ -0,0 +1,85 @@
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,33 @@
package com.example.quiz.data.remote;
import android.content.Context;
import android.content.SharedPreferences;
import javax.inject.Inject;
import dagger.hilt.android.qualifiers.ApplicationContext;
public class TokenManager {
private static final String PREF_NAME = "auth_pref";
private static final String KEY_TOKEN = "auth_token";
SocketManager socketManager;
private SharedPreferences sharedPreferences;
@Inject
public TokenManager(@ApplicationContext Context context, SocketManager socketManager) {
this.sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
this.socketManager = socketManager;
}
public void saveToken(String token){
sharedPreferences.edit().putString(KEY_TOKEN, token).apply();
socketManager.reconnect();
}
public String getToken(){
return sharedPreferences.getString(KEY_TOKEN, null);
}
public void clearToken(){
sharedPreferences.edit().remove(KEY_TOKEN).apply();
}
}

View File

@@ -1,46 +0,0 @@
package com.example.quiz.data.repository;
import com.example.quiz.data.BetsBank;
import com.example.quiz.data.model.Bet;
import com.example.quiz.data.model.Horse;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class BetRepository {
private BetsBank betsBank;
@Inject
public BetRepository(BetsBank betsBank){
this.betsBank = betsBank;
}
public BetRepository(){
this.betsBank = BetsBank.getInstance();
}
public List<Bet> getAllBets(){
return betsBank.getBet();
}
public Bet getBetById(int id){
return betsBank.getBet().get(id);
}
public List<Horse> getHorsesById(int id){
return getBetById(id).getHorses();
}
public BetRepository getInstance(){
return new BetRepository();
}
public String getBetNameById(int id){
return getBetById(id).getName();
}
}

View File

@@ -0,0 +1,49 @@
package com.example.quiz.data.repository;
import android.util.Log;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.quiz.data.model.Course;
import com.example.quiz.data.model.PagedModel;
import com.example.quiz.data.remote.ApiService;
import com.example.quiz.utils.Result;
import java.util.List;
import javax.inject.Inject;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class CourseRepository {
private ApiService apiService;
@Inject
public CourseRepository(ApiService apiService) {
this.apiService = apiService;
}
public LiveData<Result<PagedModel<Course>>> getCourses(String reunionDate) {
MutableLiveData<Result<PagedModel<Course>>> liveCourses = new MutableLiveData<Result<PagedModel<Course>>>();
liveCourses.setValue(Result.loading());
apiService.getCourses(reunionDate).enqueue(new Callback<PagedModel<Course>>() {
@Override
public void onResponse(Call<PagedModel<Course>> call, Response<PagedModel<Course>> response) {
if(response.isSuccessful()){
liveCourses.postValue(Result.success(response.body()));
}else{
liveCourses.postValue(Result.error(response.message()));
}
}
@Override
public void onFailure(Call<PagedModel<Course>> call, Throwable throwable) {
liveCourses.postValue(Result.error(throwable.getMessage()));
}
});
return liveCourses;
}
}

View File

@@ -0,0 +1,51 @@
package com.example.quiz.data.repository;
import android.util.Log;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.quiz.data.model.dtos.auth.LoginPayload;
import com.example.quiz.data.model.dtos.auth.LoginResponse;
import com.example.quiz.data.remote.ApiService;
import com.example.quiz.data.remote.TokenManager;
import com.example.quiz.utils.Result;
import javax.inject.Inject;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class LoginRepository {
private ApiService apiService;
private TokenManager tokenManager;
@Inject
public LoginRepository(ApiService apiService, TokenManager tokenManager) {
this.tokenManager = tokenManager;
this.apiService = apiService;
}
public LiveData<Result<LoginResponse>> login(LoginPayload loginPayload){
MutableLiveData<Result<LoginResponse>> liveLogin = new MutableLiveData<Result<LoginResponse>>();
liveLogin.setValue(Result.loading());
apiService.login(loginPayload).enqueue(new Callback<LoginResponse>() {
@Override
public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
if(response.isSuccessful()){
liveLogin.postValue(Result.success(response.body()));
Log.d("TOKEN", response.body().getToken());
tokenManager.saveToken(response.body().getToken());
}else{
liveLogin.postValue(Result.error(response.message()));
}
}
@Override
public void onFailure(Call<LoginResponse> call, Throwable throwable) {
liveLogin.postValue(Result.error(throwable.getMessage()));
}
});
return liveLogin;
}
}

View File

@@ -0,0 +1,174 @@
package com.example.quiz.data.repository;
import android.util.Log;
import android.widget.Toast;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.quiz.data.model.Pari;
import com.example.quiz.data.model.ParisResponse;
import com.example.quiz.data.remote.ApiService;
import com.example.quiz.utils.Result;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class PariRepository {
private ApiService apiService;
@Inject
public PariRepository(ApiService apiService) {
this.apiService = apiService;
}
public LiveData<Result<ParisResponse>> createPari(Pari pari) {
MutableLiveData<Result<ParisResponse>> pariResponse = new MutableLiveData<>();
pariResponse.setValue(Result.loading());
apiService.createPari(pari).enqueue(new Callback<ParisResponse>() {
@Override
public void onResponse(Call<ParisResponse> call, Response<ParisResponse> response) {
if (response.isSuccessful() && response.body() != null) {
pariResponse.postValue(Result.success(response.body()));
} else {
// On récupère l'erreur exacte envoyée par le backend
try {
Map<String, String> errorBody = Collections.emptyMap();
if(response.errorBody() != null){
errorBody = (Map<String, String>) response.errorBody();
}
Log.d("PariRepository", response.errorBody().toString());
pariResponse.postValue(Result.error(errorBody.get("message")));
} catch (Exception e) {
pariResponse.postValue(Result.error("Erreur serveur"));
}
}
}
@Override
public void onFailure(Call<ParisResponse> call, Throwable t) {
pariResponse.postValue(Result.error(t.getMessage()));
}
});
return pariResponse;
}
public LiveData<Result<List<ParisResponse>>> derniersParis(String agentId){
MutableLiveData<Result<List<ParisResponse>>> derniersParis = new MutableLiveData<>();
derniersParis.setValue(Result.loading());
apiService.derniersParis(agentId).enqueue(new Callback<List<ParisResponse>>() {
@Override
public void onResponse(Call<List<ParisResponse>> call, Response<List<ParisResponse>> response) {
if(response.isSuccessful() && response.body() != null){
derniersParis.postValue(Result.success(response.body()));
}else{
try{
String errorBody = response.errorBody() != null ?
response.errorBody().string() : "Erreur inconnue";
derniersParis.postValue(Result.error(errorBody));
}catch (Exception e){
derniersParis.postValue(Result.error(e.getMessage()));
}
}
}
@Override
public void onFailure(Call<List<ParisResponse>> call, Throwable throwable) {
derniersParis.postValue(Result.error(throwable.getMessage()));
}
});
return derniersParis;
}
public LiveData<Result<ParisResponse>> annulerPari(String numeroTicket){
MutableLiveData<Result<ParisResponse>> pariResponse = new MutableLiveData<>();
pariResponse.setValue(Result.loading());
apiService.annulerPari(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 errorBody = response.errorBody() != null ?
response.errorBody().string() : "Erreur inconnue";
pariResponse.postValue(Result.error(errorBody));
}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<Double>> getSoldeByCourse(String agentId, String courseId){
MutableLiveData<Result<Double>> solde = new MutableLiveData<>();
solde.setValue(Result.loading());
apiService.getSoldeByCourse(agentId, courseId).enqueue(new Callback<Double>() {
@Override
public void onResponse(Call<Double> call, Response<Double> response) {
if(response.isSuccessful()){
solde.postValue(Result.success(response.body()));
}else{
try{
solde.postValue(Result.error(response.errorBody().string()));
}catch (Exception e){
solde.postValue(Result.error(e.getMessage()));
}
}
}
@Override
public void onFailure(Call<Double> call, Throwable throwable) {
solde.postValue(Result.error(throwable.getMessage()));
}
});
return solde;
}
public LiveData<Result<Double>> getSoldeByDay(String agentId, String day){
MutableLiveData<Result<Double>> solde = new MutableLiveData<>();
solde.setValue(Result.loading());
apiService.getSoldeByDay(agentId, day).enqueue(new Callback<Double>() {
@Override
public void onResponse(Call<Double> call, Response<Double> response) {
if(response.isSuccessful()){
solde.postValue(Result.success(response.body()));
}else{
try {
solde.postValue(Result.error(response.errorBody().string()));
}catch (Exception e){
solde.postValue(Result.error(e.getMessage()));
}
}
}
@Override
public void onFailure(Call<Double> call, Throwable throwable) {
solde.postValue(Result.error(throwable.getMessage()));
}
});
return solde;
}
}

View File

@@ -0,0 +1,65 @@
package com.example.quiz.data.repository;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.quiz.data.model.PagedModel;
import com.example.quiz.data.model.PointDeVente;
import com.example.quiz.data.remote.ApiService;
import com.example.quiz.utils.Result;
import javax.inject.Inject;
import retrofit2.*;
public class PointDeVenteRepository {
private ApiService apiService;
@Inject
public PointDeVenteRepository(ApiService apiService) {
this.apiService = apiService;
}
public LiveData<Result<PagedModel<PointDeVente>>> getPointsDeVente(String nom) {
MutableLiveData<Result<PagedModel<PointDeVente>>> result = new MutableLiveData<>();
result.setValue(Result.loading());
apiService.getPointsDeVente(nom)
.clone()
.enqueue(new Callback<PagedModel<PointDeVente>>() {
@Override
public void onResponse(Call<PagedModel<PointDeVente>> call,
Response<PagedModel<PointDeVente>> response) {
if (response.isSuccessful() && response.body() != null) {
result.postValue(Result.success(response.body()));
} else {
String errorMessage = "Erreur inconnue";
try {
if (response.errorBody() != null) {
errorMessage = response.errorBody().string();
}
} catch (Exception e) {
errorMessage = e.getMessage();
}
result.postValue(Result.error(errorMessage));
}
}
@Override
public void onFailure(Call<PagedModel<PointDeVente>> call, Throwable t) {
result.postValue(Result.error(
t.getMessage() != null ? t.getMessage() : "Erreur réseau"
));
}
});
return result;
}
}

View File

@@ -1,31 +0,0 @@
package com.example.quiz.data.repository;
import com.example.quiz.data.QuestionsBank;
import com.example.quiz.data.model.Question;
import java.util.List;
import java.util.Map;
public class QuestionRepository {
private QuestionsBank questionsBank;
public QuestionRepository(){
this.questionsBank = QuestionsBank.getInstance();
}
public List<Question> getQuestions(){
return questionsBank.getQuestions();
}
public Object updateQuestionById(int id, Question question){
Question myQuestion = questionsBank.getQuestions().get(id);
if(question == null) return Map.of("error", "Cette question n'existe pas!");
myQuestion.setQuestion(question.getQuestion()!=null?question.getQuestion():myQuestion.getQuestion());
myQuestion.setAnswers(!question.getAnswers().isEmpty()?question.getAnswers():myQuestion.getAnswers());
myQuestion.setIndexAnswer(question.getIndexAnswer() > 3?question.getIndexAnswer():myQuestion.getIndexAnswer());
return Map.of("success", true);
}
public QuestionRepository getInstance(){
return new QuestionRepository();
}
}

View File

@@ -0,0 +1,49 @@
package com.example.quiz.data.repository;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.quiz.data.model.Reunion;
import com.example.quiz.data.remote.ApiService;
import com.example.quiz.utils.Result;
import java.util.List;
import javax.inject.Inject;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class ReunionRepository {
private ApiService apiService;
@Inject
public ReunionRepository(ApiService apiService) {
this.apiService = apiService;
}
public LiveData<Result<List<Reunion>>> getReunions(String date){
MutableLiveData<Result<List<Reunion>>> liveReunionsData = new MutableLiveData<>();
liveReunionsData.setValue(Result.loading());
apiService.getReunions(date).enqueue(new Callback<List<Reunion>>() {
@Override
public void onResponse(Call<List<Reunion>> call, Response<List<Reunion>> response) {
if(response.isSuccessful()){
liveReunionsData.postValue(Result.success(response.body()));
}else{
liveReunionsData.postValue(Result.error(response.message()));
}
}
@Override
public void onFailure(Call<List<Reunion>> call, Throwable throwable) {
liveReunionsData.postValue(Result.error(throwable.getMessage()));
}
});
return liveReunionsData;
}
}

View File

@@ -0,0 +1,47 @@
package com.example.quiz.data.repository;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.quiz.data.model.Tpe;
import com.example.quiz.data.model.TpeResponse;
import com.example.quiz.data.remote.ApiService;
import com.example.quiz.utils.Result;
import javax.inject.Inject;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class TpeRepository {
private ApiService apiService;
@Inject
public TpeRepository(ApiService apiService) {
this.apiService = apiService;
}
public LiveData<Result<TpeResponse>> createTpe(Tpe tpe) {
MutableLiveData<Result<TpeResponse>> tpeLiveData = new MutableLiveData<>();
tpeLiveData.setValue(Result.loading());
apiService.createTpe(tpe).enqueue(new Callback<TpeResponse>() {
@Override
public void onResponse(Call<TpeResponse> call, Response<TpeResponse> response) {
if (response.isSuccessful()) {
tpeLiveData.postValue(Result.success(response.body()));
} else {
tpeLiveData.postValue(Result.error(response.message()));
}
}
@Override
public void onFailure(Call<TpeResponse> call, Throwable throwable) {
tpeLiveData.postValue(Result.error(throwable.getMessage()));
}
});
return tpeLiveData;
}
}

View File

@@ -0,0 +1,29 @@
package com.example.quiz.data.repository.module;
import com.example.quiz.data.remote.ApiService;
import com.example.quiz.data.repository.CourseRepository;
import com.example.quiz.data.repository.ReunionRepository;
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 RepositoryModule {
@Provides
@Singleton
public ReunionRepository provideReunionRepository(ApiService apiService){
return new ReunionRepository(apiService);
}
@Provides
@Singleton
public CourseRepository provideCourseRepository(ApiService apiService){
return new CourseRepository(apiService);
}
}

View File

@@ -1,38 +0,0 @@
package com.example.quiz.injection;
import androidx.annotation.NonNull;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import com.example.quiz.data.repository.QuestionRepository;
import com.example.quiz.viewModel.QuestionViewModel;
public class QuestionViewModelFactory implements ViewModelProvider.Factory {
private QuestionRepository questionRepository;
private static QuestionViewModelFactory factory;
public static QuestionViewModelFactory getInstance(){
if(factory == null){
synchronized (QuestionViewModel.class){
if(factory == null){
factory = new QuestionViewModelFactory();
}
}
}
return factory;
}
private QuestionViewModelFactory(){
this.questionRepository = new QuestionRepository();
}
@Override
@NonNull
public <T extends ViewModel> T create(@NonNull Class<T> modelClass){
if(modelClass.isAssignableFrom(QuestionViewModel.class)){
return (T) new QuestionViewModel(questionRepository);
}
throw new IllegalArgumentException("Unknown ViewModel class");
}
}

View File

@@ -0,0 +1,147 @@
package com.example.quiz.utils;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner;
import com.example.quiz.R;
import com.example.quiz.data.model.dtos.auth.LoginPayload;
import com.example.quiz.data.model.dtos.auth.LoginResponse;
import com.example.quiz.data.remote.TokenManager;
import com.example.quiz.viewModel.LoginViewModel;
import com.example.quiz.viewModel.LogsViewModel;
import javax.inject.Inject;
public class AuthNavigator {
private final Context context;
private FragmentManager fragmentManager;
private TokenManager tokenManager;
private final SessionManager sessionManager;
LoginViewModel viewModel;
LogsViewModel logsViewModel;
private Dialog dialog;
SharedPrefsHelper prefsHelper;
public AuthNavigator(Context context,
FragmentManager fragmentManager,
SessionManager sessionManager,
LoginViewModel viewModel,
LogsViewModel logsViewModel) {
this.viewModel = viewModel;
this.logsViewModel = logsViewModel;
this.prefsHelper = SharedPrefsHelper.getInstance(context);
this.fragmentManager = fragmentManager;
this.context = context;
dialog = new Dialog(context);
this.sessionManager = sessionManager;
}
public void navigate(Fragment destinationFragment) {
if(destinationFragment== null){
return;
}
if (sessionManager.isExpired()) {
showPinDialog(() -> {
sessionManager.updateLastExpiredDate();
fragmentManager.beginTransaction()
.replace(R.id.nav_host_fragment_content_main, destinationFragment)
.addToBackStack(null)
.commit();
});
} else {
fragmentManager.beginTransaction()
.replace(R.id.nav_host_fragment_content_main, destinationFragment)
.addToBackStack(null)
.commit();
}
}
public void showPinDialog(Runnable onSuccess) {
View view = LayoutInflater.from(context).inflate(R.layout.pin_view, null);
dialog.setContentView(view);
dialog.setCancelable(true);
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
view.findViewById(R.id.cancel).setOnClickListener(v -> dialog.dismiss());
view.findViewById(R.id.pin_validate)
.setOnClickListener(v -> {
TextView code = view.findViewById(R.id.code);
TextView pin = view.findViewById(R.id.pin);
if(code.getText().toString().isEmpty()){
pin.setError("Entrez le code!");
return;
}
if(pin.getText().toString().isEmpty()){
pin.setError("Entrez le pin!");
return;
}
if(pin.getText().toString().length() < 4){
pin.setError("Le pin doit contenir minimum quatre chiffres!");
return;
}
LoginPayload loginPayload = new LoginPayload(
code.getText().toString(),
pin.getText().toString(),
Long.valueOf(prefsHelper.get("terminalId"))
);
viewModel.login(loginPayload).observe((LifecycleOwner) context, new Observer<Result<LoginResponse>>() {
@Override
public void onChanged(Result<LoginResponse> loginResponseResult) {
switch (loginResponseResult.status){
case LOADING:
view.findViewById(R.id.pin_validate).setEnabled(false);
view.findViewById(R.id.pin_validate).setAlpha(0.5f);
break;
case ERROR:
MessageDialog.showError(context, loginResponseResult.message);
view.findViewById(R.id.pin_validate).setEnabled(true);
view.findViewById(R.id.pin_validate).setAlpha(1f);
break;
case SUCCESS:
loginSuccess(loginResponseResult.data);
logsViewModel.insertLog(prefsHelper.get("id"), "LOGIN","Authentification sur l'appareil", System.currentTimeMillis());
sessionManager.updateLastExpiredDate();
dialog.dismiss();
view.findViewById(R.id.pin_validate).setEnabled(true);
view.findViewById(R.id.pin_validate).setAlpha(1f);
onSuccess.run();
break;
}
}
});
});
dialog.show();
}
public boolean dialogIsShowing(){
return dialog.isShowing();
}
private void loginSuccess(LoginResponse loginResponse){
prefsHelper.save("id", String.valueOf(loginResponse.getUser().getId()));
prefsHelper.save("firstName", loginResponse.getUser().getPrenom());
prefsHelper.save("lastName", loginResponse.getUser().getNom());
prefsHelper.save("profile", loginResponse.getUser().getFonction());
prefsHelper.save("code", loginResponse.getUser().getCode());
}
}

View File

@@ -0,0 +1,77 @@
package com.example.quiz.utils;
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.widget.Toast;
import androidx.annotation.RequiresPermission;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentActivity;
import com.anggastudio.printama.Printama;
import com.anggastudio.printama.PrintamaUI;
public class BluetoothUtils {
public static final int PERMISSION_REQUEST_BLUETOOTH_CONNECT = 432;
public static boolean needsBluetoothPermissions() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
}
public static boolean hasBluetoothPermission(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
return ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT)
== PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_SCAN)
== PackageManager.PERMISSION_GRANTED;
} else {
return ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED;
}
}
public static void requestBluetoothPermission(FragmentActivity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
ActivityCompat.requestPermissions(activity,
new String[]{
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_CONNECT
},
PERMISSION_REQUEST_BLUETOOTH_CONNECT);
} else {
ActivityCompat.requestPermissions(activity,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
PERMISSION_REQUEST_BLUETOOTH_CONNECT);
}
}
/** Fournit lintent pour activer Bluetooth */
public static Intent getEnableBluetoothIntent() {
return new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
}
/** Affiche la liste des imprimantes */
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public static void showPrinterList(Context context, FragmentActivity activity) {
PrintamaUI.showPrinterList(activity, selectedDevice -> {
if (selectedDevice != null) {
String name = Printama.getSavedPrinterName(context);
showToast("Connected to " + name, context);
} else {
showToast("Failed to choose printer", context);
}
});
}
private static void showToast(String msg, Context c) {
Toast.makeText(c, msg, Toast.LENGTH_SHORT).show();
}
}

View File

@@ -0,0 +1,8 @@
package com.example.quiz.utils;
import android.bluetooth.BluetoothSocket;
public class EscCosPrinterUtil {
private BluetoothSocket btSocket = null;
}

View File

@@ -1,91 +1,268 @@
package com.example.quiz.utils; //package com.example.quiz.utils;
//
import android.content.Context; //import android.annotation.SuppressLint;
import android.util.Log; //import android.bluetooth.BluetoothAdapter;
import android.widget.Toast; //import android.bluetooth.BluetoothDevice;
//import android.content.Context;
import tspl.HPRTPrinterHelper; //import android.graphics.Bitmap;
//import android.graphics.BitmapFactory;
public class HPRTPrinterUtil { //import android.graphics.Color;
//import android.os.Environment;
private static final String TAG = "HPRTPrinterUtil"; //import android.util.Log;
private Context context; //import android.widget.Toast;
//
public HPRTPrinterUtil(Context context) { //import java.io.ByteArrayOutputStream;
this.context = context; //import java.io.File;
} //import java.io.FileOutputStream;
//import java.io.IOException;
/** //import java.io.InputStream;
* Connecte une imprimante Bluetooth //import java.util.Set;
* @param btAddress adresse MAC Bluetooth de l'imprimante //
* @return true si connecté, false sinon //import tspl.HPRTPrinterHelper;
*/ //
public boolean connectBluetooth(String btAddress) { //public class HPRTPrinterUtil {
try { //
HPRTPrinterHelper.PortClose(); // ferme toute connexion existante // private static final String TAG = "HPRTPrinterUtil";
int result = HPRTPrinterHelper.PortOpen("Bluetooth," + btAddress); // private Context context;
if (result == 0) { //
Log.d(TAG, "Connexion réussie"); // BluetoothAdapter mBluetoothAdapter;
Toast.makeText(context, "Imprimante connectée", Toast.LENGTH_SHORT).show(); //
return true; // public HPRTPrinterUtil(Context context) {
} else { // this.context = context;
Log.e(TAG, "Erreur connexion: " + result); // }
Toast.makeText(context, "Erreur connexion imprimante: " + result, Toast.LENGTH_SHORT).show(); //
return false; //
} //
} catch (Exception e) { // @SuppressLint("MissingPermission")
Log.e(TAG, "Erreur connexion: " + e.getMessage()); // public boolean autoConnectBluetoothByName() {
return false; // try {
} // BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
} // if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
// Toast.makeText(context, "Bluetooth désactivé ou non disponible", Toast.LENGTH_LONG).show();
public void printText(String text) { // return false;
try { // }
if (!HPRTPrinterHelper.IsOpened()) { //
Toast.makeText(context, "Imprimante non connectée", Toast.LENGTH_SHORT).show(); // Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
return; // if (pairedDevices != null) {
} // for (BluetoothDevice device : pairedDevices) {
// if (device.getName() != null && device.getName().contains("MP4P Printer")) {
// TSPL pur pour texte simple // String btAddress = device.getAddress();
String tspl = ""+ text + "\r\n"; //
// // Connexion via ton SDK HPRT
// Envoi à l'imprimante // int result = HPRTPrinterHelper.PortOpen("Bluetooth," + btAddress);
HPRTPrinterHelper.PrintData(tspl); // if (result == 0) {
// Toast.makeText(context, "Imprimante connectée : " + device.getName(), Toast.LENGTH_SHORT).show();
Log.d(TAG, "Texte imprimé sur ticket"); // on log seulement le succès // Log.d(TAG, "Connexion réussie : " + device.getName() + " - " + btAddress);
// return true;
} catch (Exception e) { // } else {
Log.e(TAG, "Erreur impression TSPL: " + e.getMessage()); // Toast.makeText(context, "Erreur connexion imprimante: " + result, Toast.LENGTH_SHORT).show();
Toast.makeText(context, "Erreur impression TSPL", Toast.LENGTH_SHORT).show(); // Log.e(TAG, "Erreur connexion imprimante: " + result);
} // return false;
} // }
// }
// }
// }
public void printTSPLTemplate(String tsplTemplate) { //
try { // Toast.makeText(context, "Imprimante non trouvée. Veuillez appairer d'abord.", Toast.LENGTH_LONG).show();
if (!HPRTPrinterHelper.IsOpened()) { // return false;
Toast.makeText(context, "Imprimante non connectée", Toast.LENGTH_SHORT).show(); //
return; // } catch (Exception e) {
} // e.printStackTrace();
// Toast.makeText(context, "Erreur auto-connexion : " + e.getMessage(), Toast.LENGTH_LONG).show();
HPRTPrinterHelper.PrintData(tsplTemplate); // return false;
Log.d(TAG, "Template imprimé"); // }
} catch (Exception e) { // }
Log.e(TAG, "Erreur impression TSPL: " + e.getMessage()); //
Toast.makeText(context, "Erreur impression TSPL", Toast.LENGTH_SHORT).show(); //
} // public boolean connectBluetooth(String btAddress) {
} // try {
// HPRTPrinterHelper.PortClose(); // ferme toute connexion existante
/** // int result = HPRTPrinterHelper.PortOpen("Bluetooth," + btAddress);
* Déconnecte l'imprimante // if (result == 0) {
*/ // Log.d(TAG, "Connexion réussie");
public void disconnect() { // Toast.makeText(context, "Imprimante connectée", Toast.LENGTH_SHORT).show();
try { // return true;
HPRTPrinterHelper.PortClose(); // } else {
Toast.makeText(context, "Imprimante déconnectée", Toast.LENGTH_SHORT).show(); // Log.e(TAG, "Erreur connexion: " + result);
} catch (Exception e) { // Toast.makeText(context, "Erreur connexion imprimante: " + result, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Erreur déconnexion: " + e.getMessage()); // return false;
} // }
} // } catch (Exception e) {
} // Log.e(TAG, "Erreur connexion: " + e.getMessage());
// return false;
// }
// }
//
// public void printText(Bitmap logo, StringBuilder text) {
// try {
// if (!HPRTPrinterHelper.IsOpened()) {
// Toast.makeText(context, "Imprimante non connectée", Toast.LENGTH_SHORT).show();
// return;
// }
//
//
// Bitmap resized = resizeForPrinter(logo, 384);
//
// HPRTPrinterHelper.printImage("0", "0", resized, false);
//
// String tspl = ""+ text + "\r\n";
//
//
//
//
// // Envoi à l'imprimante
// //HPRTPrinterHelper.PrintData(tspl);
//
// Log.d(TAG, "Texte imprimé sur ticket"); // on log seulement le succès
//
// } catch (Exception e) {
// Log.e(TAG, "Erreur impression TSPL: " + e.getMessage());
// Toast.makeText(context, "Erreur impression TSPL", Toast.LENGTH_SHORT).show();
// }
// }
//
//
//
//
// public void printTSPLTemplate(String tsplTemplate) {
// try {
// if (!HPRTPrinterHelper.IsOpened()) {
// Toast.makeText(context, "Imprimante non connectée", Toast.LENGTH_SHORT).show();
// return;
// }
//
// HPRTPrinterHelper.PrintData(tsplTemplate);
// Log.d(TAG, "Template imprimé");
// } catch (Exception e) {
// Log.e(TAG, "Erreur impression TSPL: " + e.getMessage());
// Toast.makeText(context, "Erreur impression TSPL", Toast.LENGTH_SHORT).show();
// }
// }
//
// public static Bitmap toMonochrome(Bitmap src) {
// int width = src.getWidth();
// int height = src.getHeight();
//
// // Bitmap de sortie en ARGB_8888
// Bitmap bw = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
//
// for (int y = 0; y < height; y++) {
// for (int x = 0; x < width; x++) {
// int pixel = src.getPixel(x, y);
//
// // Calcul de la luminance (grayscale)
// int gray = (Color.red(pixel) + Color.green(pixel) + Color.blue(pixel)) / 3;
//
// // Seuil pour décider noir ou blanc
// if (gray < 128) {
// bw.setPixel(x, y, Color.BLACK);
// } else {
// bw.setPixel(x, y, Color.WHITE);
// }
// }
// }
//
// return bw;
// }
//
//
// public static byte[] bitmapToEscPos(Bitmap bitmap) throws IOException {
// bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false);
//
// int width = bitmap.getWidth();
// int height = bitmap.getHeight();
//
// int bytesPerRow = (width + 7) / 8;
// byte[] imageBytes = new byte[bytesPerRow * height];
//
// int index = 0;
//
// for (int y = 0; y < height; y++) {
// int bitIndex = 0;
// byte currentByte = 0;
//
// for (int x = 0; x < width; x++) {
// int color = bitmap.getPixel(x, y);
// int gray = (Color.red(color) + Color.green(color) + Color.blue(color)) / 3;
//
// currentByte <<= 1;
// if (gray < 128) currentByte |= 1;
//
// bitIndex++;
//
// if (bitIndex == 8) {
// imageBytes[index++] = currentByte;
// currentByte = 0;
// bitIndex = 0;
// }
// }
//
// if (bitIndex > 0) {
// currentByte <<= (8 - bitIndex);
// imageBytes[index++] = currentByte;
// }
// }
//
// // Préfixe ESC/POS GS v 0
// ByteArrayOutputStream baos = new ByteArrayOutputStream();
// baos.write(0x1D);
// baos.write('v');
// baos.write('0');
// baos.write(0); // Normal mode
// baos.write(bytesPerRow & 0xFF);
// baos.write((bytesPerRow >> 8) & 0xFF);
// baos.write(height & 0xFF);
// baos.write((height >> 8) & 0xFF);
// baos.write(imageBytes);
//
// return baos.toByteArray();
// }
//
//
// public static Bitmap resizeForPrinter(Bitmap bmp, int maxWidth){
// if(bmp.getWidth() <= maxWidth) return bmp;
// int newHeight = bmp.getHeight() * maxWidth / bmp.getWidth();
// return Bitmap.createScaledBitmap(bmp, maxWidth, newHeight, false);
// }
//
// // 2. Convertir le bitmap monochrome en flux binaire TSPL
// public static byte[] bitmapToTsplBinary(Bitmap bmp){
// int width = bmp.getWidth();
// int height = bmp.getHeight();
// int bytesPerRow = (width + 7) / 8;
// byte[] data = new byte[bytesPerRow*height];
// int index = 0;
// for(int y=0;y<height;y++){
// int bitIndex = 0;
// byte currentByte = 0;
// for(int x=0;x<width;x++){
// int pixel = bmp.getPixel(x, y);
// currentByte <<= 1;
// if(pixel == Color.BLACK) currentByte |= 1;
// bitIndex++;
// if(bitIndex==8){
// data[index++] = currentByte;
// currentByte = 0;
// bitIndex = 0;
// }
// }
// if(bitIndex != 0){
// currentByte <<= (8 - bitIndex);
// data[index++] = currentByte;
// }
// }
// return data;
// }
//
// /**
// * Déconnecte l'imprimante
// */
// public void disconnect() {
// try {
// HPRTPrinterHelper.PortClose();
// Toast.makeText(context, "Imprimante déconnectée", Toast.LENGTH_SHORT).show();
// } catch (Exception e) {
// Log.e(TAG, "Erreur déconnexion: " + e.getMessage());
// }
// }
//}

View File

@@ -0,0 +1,36 @@
package com.example.quiz.utils;
import android.app.Dialog;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import com.example.quiz.R;
public class LoaderDialog {
private Dialog dialog;
private TextView loaderText;
public LoaderDialog(Context context) {
dialog = new Dialog(context);
View view = LayoutInflater.from(context).inflate(R.layout.dowload_loader, null);
loaderText = view.findViewById(R.id.loaderText);
dialog.setContentView(view);
dialog.setCancelable(false); // Empêche fermeture en cliquant dehors
dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
}
public void show(String message) {
loaderText.setText(message);
dialog.show();
}
public void dismiss() {
if (dialog.isShowing()) {
dialog.dismiss();
}
}
}

View File

@@ -0,0 +1,53 @@
package com.example.quiz.utils;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.lifecycle.Observer;
import com.example.quiz.R;
import com.example.quiz.data.model.dtos.auth.LoginResponse;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
public class MessageDialog {
public static void showSuccess(Context context, String message) {
Dialog dialog = new Dialog(context);
View view = LayoutInflater.from(context).inflate(R.layout.dialog_success, null);
TextView txtMessage = view.findViewById(R.id.txtMessage);
Button btnOk = view.findViewById(R.id.btnOk);
txtMessage.setText(message);
btnOk.setOnClickListener(v -> dialog.dismiss());
dialog.setContentView(view);
dialog.setCancelable(true);
dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
dialog.show();
}
public static void showError(Context context, String message) {
Dialog dialog = new Dialog(context);
View view = LayoutInflater.from(context).inflate(R.layout.dialog_error, null);
TextView txtMessage = view.findViewById(R.id.txtMessage);
Button btnOk = view.findViewById(R.id.btnOk);
txtMessage.setText(message);
btnOk.setOnClickListener(v -> dialog.dismiss());
dialog.setContentView(view);
dialog.setCancelable(true);
dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
dialog.show();
}
}

View File

@@ -0,0 +1,28 @@
package com.example.quiz.utils;
public class Result<T> {
public enum Status { SUCCESS, ERROR, LOADING }
public final Status status;
public final T data;
public final String message;
private Result(Status status, T data, String message) {
this.status = status;
this.data = data;
this.message = message;
}
public static <T> Result<T> success(T data) {
return new Result<>(Status.SUCCESS, data, null);
}
public static <T> Result<T> error(String msg) {
return new Result<>(Status.ERROR, null, msg);
}
public static <T> Result<T> loading() {
return new Result<>(Status.LOADING, null, null);
}
}

View File

@@ -0,0 +1,42 @@
package com.example.quiz.utils;
import android.content.Context;
import android.os.Bundle;
import java.time.Duration;
import java.time.LocalDateTime;
public class SessionManager {
// 10 minutes en millisecondes
private static final long sessionExpiredTime = 10 * 60 * 1000;
private static SessionManager instance;
private LocalDateTime lastExpiredDate;
public SessionManager(Context context) {
if(lastExpiredDate != null){
updateLastExpiredDate();
}
}
public static SessionManager newInstance(Context context) {
if(instance == null){
instance = new SessionManager(context.getApplicationContext());
}
return instance;
}
public void updateLastExpiredDate() {
lastExpiredDate = LocalDateTime.now();
}
public boolean isExpired() {
if (lastExpiredDate == null) {
return true;
}
// Vérifie si le temps écoulé depuis lastExpiredDate > sessionExpiredTime
Duration elapsed = Duration.between(lastExpiredDate, LocalDateTime.now());
return elapsed.toMillis() > sessionExpiredTime;
}
}

View File

@@ -1,46 +0,0 @@
package com.example.quiz.viewModel;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.example.quiz.data.model.Bet;
import com.example.quiz.data.model.Horse;
import com.example.quiz.data.repository.BetRepository;
import java.util.List;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class BetViewModel extends ViewModel {
private BetRepository betRepository;
public MutableLiveData<List<Bet>> bets = new MutableLiveData<List<Bet>>();
public MutableLiveData<String> betName = new MutableLiveData<String>();
public MutableLiveData<List<Horse>> horses = new MutableLiveData<>();
@Inject
public BetViewModel(BetRepository betRepository){
this.betRepository = betRepository;
}
public void loadBets(){
bets.setValue(betRepository.getAllBets());
}
public void loadHorses(int id){
horses.setValue(betRepository.getHorsesById(id));
}
public void getBetNameById(int id){
betName.setValue(betRepository.getBetNameById(id));
}
}

View File

@@ -0,0 +1,32 @@
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.PagedModel;
import com.example.quiz.data.repository.CourseRepository;
import com.example.quiz.utils.Result;
import java.util.List;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class CourseViewModel extends ViewModel {
private final CourseRepository courseRepository;
private LiveData<Result<PagedModel<Course>>> courses;
@Inject
public CourseViewModel(CourseRepository courseRepository){
this.courseRepository = courseRepository;
}
public LiveData<Result<PagedModel<Course>>> getCourses(String reunionDate){
courses = courseRepository.getCourses(reunionDate);
return courses;
}
}

View File

@@ -0,0 +1,27 @@
package com.example.quiz.viewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.example.quiz.data.model.dtos.auth.LoginPayload;
import com.example.quiz.data.model.dtos.auth.LoginResponse;
import com.example.quiz.data.repository.LoginRepository;
import com.example.quiz.utils.Result;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class LoginViewModel extends ViewModel {
LoginRepository loginRepository;
@Inject
public LoginViewModel(LoginRepository loginRepository) {
this.loginRepository = loginRepository;
}
public LiveData<Result<LoginResponse>> login(LoginPayload loginPayload){
return loginRepository.login(loginPayload);
}
}

View File

@@ -0,0 +1,41 @@
package com.example.quiz.viewModel;
import android.util.Log;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.example.quiz.data.ActivityLogger;
import com.example.quiz.data.dao.UserActivityLogDao;
import com.example.quiz.data.database.DatabaseClient;
import com.example.quiz.data.entity.Logs;
import java.util.List;
import java.util.concurrent.Executors;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class LogsViewModel extends ViewModel {
private MutableLiveData<List<Logs>> todayLogs = new MutableLiveData<List<Logs>>(null);
private UserActivityLogDao dao;
@Inject
public LogsViewModel(UserActivityLogDao dao) {
this.dao = dao;
ActivityLogger.init(dao);
}
public LiveData<List<Logs>> getTodayLogs() {
return dao.getTodayLogs();
}
public void insertLog(String userId, String action, String description, Long timestamp){
ActivityLogger.log(userId, action, description, timestamp);
}
}

View File

@@ -0,0 +1,59 @@
package com.example.quiz.viewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.example.quiz.data.model.Pari;
import com.example.quiz.data.model.ParisResponse;
import com.example.quiz.data.repository.PariRepository;
import com.example.quiz.utils.Result;
import java.util.List;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class PariViewModel extends ViewModel {
private final PariRepository pariRepository;
private LiveData<Result<ParisResponse>> pari;
private LiveData<Result<List<ParisResponse>>> derniersParis;
private LiveData<Result<ParisResponse>> pariAnnule;
private LiveData<Result<Double>> solde;
private LiveData<Result<Double>> soldeByDay;
@Inject
public PariViewModel(PariRepository repository){
this.pariRepository = repository;
}
public LiveData<Result<ParisResponse>> createPari(Pari pari){
this.pari = pariRepository.createPari(pari);
return this.pari;
}
public LiveData<Result<List<ParisResponse>>> getDerniersParis(String agentId){
derniersParis = pariRepository.derniersParis(agentId);
return pariRepository.derniersParis(agentId);
}
public LiveData<Result<ParisResponse>> annulerPari(String numeroTicket){
pariAnnule = pariRepository.annulerPari(numeroTicket);
return pariAnnule;
}
public LiveData<Result<Double>> getSoldeByCourse(String agentId, String courseId){
solde = pariRepository.getSoldeByCourse(agentId, courseId);
return solde;
}
public LiveData<Result<Double>> getSoldeByDay(String agentId, String day){
soldeByDay = pariRepository.getSoldeByDay(agentId, day);
return soldeByDay;
}
}

View File

@@ -0,0 +1,29 @@
package com.example.quiz.viewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.example.quiz.data.model.PagedModel;
import com.example.quiz.data.model.PointDeVente;
import com.example.quiz.data.repository.PointDeVenteRepository;
import com.example.quiz.utils.Result;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class PointDeVenteViewModel extends ViewModel {
private final PointDeVenteRepository pointDeVenteRepository;
private LiveData<Result<PagedModel<PointDeVente>>> pointsDeVente;
@Inject
public PointDeVenteViewModel(PointDeVenteRepository pointDeVenteRepository){
this.pointDeVenteRepository = pointDeVenteRepository;
}
public LiveData<Result<PagedModel<PointDeVente>>> setPointsDeVente(String search){
this.pointsDeVente = pointDeVenteRepository.getPointsDeVente(search);
return this.pointsDeVente;
}
}

View File

@@ -1,41 +0,0 @@
package com.example.quiz.viewModel;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.example.quiz.data.model.Question;
import com.example.quiz.data.repository.QuestionRepository;
import java.util.Objects;
public class QuestionViewModel extends ViewModel {
private QuestionRepository questionRepository;
public MutableLiveData<Question> currentQuestion = new MutableLiveData<Question>();
public MutableLiveData<Integer> score = new MutableLiveData<Integer>();
public MutableLiveData<Boolean> isLastQuestion = new MutableLiveData<Boolean>(false);
public QuestionViewModel(QuestionRepository questionRepository){
this.questionRepository = questionRepository;
}
public void startQuiz(){
currentQuestion.setValue(questionRepository.getQuestions().get(0));
score.setValue(0);
}
public Boolean isAnswerValid(int answerIndex){
return answerIndex == Objects.requireNonNull(currentQuestion.getValue()).getIndexAnswer();
}
public void nextQuestion(){
int currentQuestionIndex = questionRepository.getQuestions().indexOf(currentQuestion.getValue());
if(currentQuestionIndex == questionRepository.getQuestions().size()-1){
isLastQuestion.setValue(true);
}else{
currentQuestion.setValue(questionRepository.getQuestions().get(currentQuestionIndex+1));
}
}
}

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.Reunion;
import com.example.quiz.data.repository.ReunionRepository;
import com.example.quiz.utils.Result;
import java.util.List;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class ReunionViewModel extends ViewModel {
private final ReunionRepository reunionRepository;
private LiveData<Result<List<Reunion>>> reunions;
@Inject
public ReunionViewModel(ReunionRepository reunionRepository) {
this.reunionRepository = reunionRepository;
}
public LiveData<Result<List<Reunion>>> getReunions(String date) {
if (reunions == null) {
reunions = reunionRepository.getReunions(date);
}
return reunions;
}
}

Some files were not shown because too many files have changed in this diff Show More