From 3d817d0b8104856a8145d46106a3ee5c07b9c910 Mon Sep 17 00:00:00 2001 From: 230409 <230409@epvc.pt> Date: Mon, 11 May 2026 14:54:38 +0100 Subject: [PATCH] .. --- app/build.gradle.kts | 9 +- app/src/main/AndroidManifest.xml | 42 +- .../pap_teste/AccountCreatedActivity.java | 8 +- .../pap_teste/LocalReservaAdapter.java | 67 +++ .../com/example/pap_teste/MainActivity.java | 6 +- .../pap_teste/MyReservationsFragment.java | 75 ++-- .../pap_teste/NovaReservaActivity.java | 422 ++++++++---------- .../example/pap_teste/OnboardingActivity.java | 3 +- .../example/pap_teste/ProfileFragment.java | 1 + .../com/example/pap_teste/SplashActivity.java | 36 ++ .../pap_teste/TableSelectionAdapter.java | 87 ++++ .../example/pap_teste/data/AppDatabase.java | 24 + .../pap_teste/data/ReservationDao.java | 29 ++ .../pap_teste/models/LocalReservation.java | 51 +++ .../com/example/pap_teste/models/Reserva.java | 39 +- .../viewmodels/ReservationViewModel.java | 53 +++ .../main/res/layout/activity_nova_reserva.xml | 40 ++ app/src/main/res/layout/activity_splash.xml | 47 ++ .../main/res/layout/item_reserva_local.xml | 96 ++++ app/src/main/res/layout/item_table.xml | 48 ++ gradle/libs.versions.toml | 4 + 21 files changed, 875 insertions(+), 312 deletions(-) create mode 100644 app/src/main/java/com/example/pap_teste/LocalReservaAdapter.java create mode 100644 app/src/main/java/com/example/pap_teste/SplashActivity.java create mode 100644 app/src/main/java/com/example/pap_teste/TableSelectionAdapter.java create mode 100644 app/src/main/java/com/example/pap_teste/data/AppDatabase.java create mode 100644 app/src/main/java/com/example/pap_teste/data/ReservationDao.java create mode 100644 app/src/main/java/com/example/pap_teste/models/LocalReservation.java create mode 100644 app/src/main/java/com/example/pap_teste/viewmodels/ReservationViewModel.java create mode 100644 app/src/main/res/layout/activity_splash.xml create mode 100644 app/src/main/res/layout/item_reserva_local.xml create mode 100644 app/src/main/res/layout/item_table.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ad20d54..750a133 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -5,12 +5,12 @@ plugins { android { namespace = "com.example.pap_teste" - compileSdk = 36 + compileSdk = 35 defaultConfig { applicationId = "com.example.pap_teste" minSdk = 24 - targetSdk = 36 + targetSdk = 35 versionCode = 1 versionName = "1.0" @@ -44,6 +44,11 @@ dependencies { implementation(libs.firebase.database) implementation(libs.glide) implementation(libs.firebase.storage) + + // Room + implementation(libs.room.runtime) + annotationProcessor(libs.room.compiler) + testImplementation(libs.junit) androidTestImplementation(libs.ext.junit) androidTestImplementation(libs.espresso.core) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fa7d6df..a768316 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -27,7 +27,7 @@ @@ -35,6 +35,10 @@ + + @@ -50,6 +54,42 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/pap_teste/AccountCreatedActivity.java b/app/src/main/java/com/example/pap_teste/AccountCreatedActivity.java index e23c3c9..65fc420 100644 --- a/app/src/main/java/com/example/pap_teste/AccountCreatedActivity.java +++ b/app/src/main/java/com/example/pap_teste/AccountCreatedActivity.java @@ -1,5 +1,7 @@ package com.example.pap_teste; +import static android.content.Intent.getIntent; + import android.content.Intent; import android.os.Bundle; import android.widget.Button; @@ -50,13 +52,13 @@ public class AccountCreatedActivity extends AppCompatActivity { this, isEstablishment ? EstablishmentDashboardActivity.class : ClientDashboardActivity.class ); - nextScreen.putExtra(MainActivity.EXTRA_ACTION_MODE, MainActivity.AccountAction.CRIAR.name()); + // nextScreen.putExtra(MainActivity.EXTRA_ACTION_MODE, MainActivity.AccountAction.CRIAR.name()); nextScreen.putExtra(MainActivity.EXTRA_DISPLAY_NAME, displayName); nextScreen.putExtra(MainActivity.EXTRA_EMAIL, email); nextScreen.putExtra(MainActivity.EXTRA_ACCOUNT_TYPE, accountType); nextScreen.putExtra(MainActivity.EXTRA_ROLE, role); - startActivity(nextScreen); - finish(); + //startActivity(nextScreen); + //finish(); }); if (btnBack != null) { diff --git a/app/src/main/java/com/example/pap_teste/LocalReservaAdapter.java b/app/src/main/java/com/example/pap_teste/LocalReservaAdapter.java new file mode 100644 index 0000000..b956163 --- /dev/null +++ b/app/src/main/java/com/example/pap_teste/LocalReservaAdapter.java @@ -0,0 +1,67 @@ +package com.example.pap_teste; + +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.pap_teste.models.LocalReservation; +import com.google.android.material.button.MaterialButton; +import com.google.android.material.chip.Chip; +import java.util.List; + +public class LocalReservaAdapter extends RecyclerView.Adapter { + + private final List reservations; + private final OnActionListener listener; + + public interface OnActionListener { + void onCancel(LocalReservation reservation); + void onEdit(LocalReservation reservation); + } + + public LocalReservaAdapter(List reservations, OnActionListener listener) { + this.reservations = reservations; + this.listener = listener; + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_reserva_local, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + LocalReservation res = reservations.get(position); + holder.restaurantName.setText(res.getRestaurantName()); + holder.dateInfo.setText(res.getDate() + " às " + res.getTime()); + holder.tableInfo.setText("Mesa " + res.getTableNumber() + " • " + res.getGuests() + " pessoas"); + holder.statusChip.setText(res.getStatus()); + + holder.btnCancel.setOnClickListener(v -> listener.onCancel(res)); + holder.itemView.setOnClickListener(v -> listener.onEdit(res)); + } + + @Override + public int getItemCount() { + return reservations.size(); + } + + static class ViewHolder extends RecyclerView.ViewHolder { + TextView restaurantName, dateInfo, tableInfo; + Chip statusChip; + MaterialButton btnCancel; + + ViewHolder(View itemView) { + super(itemView); + restaurantName = itemView.findViewById(R.id.txtRestauranteNome); + dateInfo = itemView.findViewById(R.id.txtDataHora); + tableInfo = itemView.findViewById(R.id.txtMesaPessoas); + statusChip = itemView.findViewById(R.id.chipStatus); + btnCancel = itemView.findViewById(R.id.btnCancelar); + } + } +} diff --git a/app/src/main/java/com/example/pap_teste/MainActivity.java b/app/src/main/java/com/example/pap_teste/MainActivity.java index 5573556..c17e7a1 100644 --- a/app/src/main/java/com/example/pap_teste/MainActivity.java +++ b/app/src/main/java/com/example/pap_teste/MainActivity.java @@ -35,7 +35,8 @@ public class MainActivity extends AppCompatActivity { // Check if user is already logged in if (mAuth.getCurrentUser() != null) { - startActivity(new Intent(this, ClientDashboardActivity.class)); + android.app.ActivityOptions options = android.app.ActivityOptions.makeCustomAnimation(this, android.R.anim.fade_in, android.R.anim.fade_out); + startActivity(new Intent(this, ClientDashboardActivity.class), options.toBundle()); finish(); return; } @@ -101,7 +102,8 @@ public class MainActivity extends AppCompatActivity { User newUser = new User(uid, name, email, "CLIENTE"); db.collection("users").document(uid).set(newUser) .addOnSuccessListener(aVoid -> { - startActivity(new Intent(this, ClientDashboardActivity.class)); + android.app.ActivityOptions options = android.app.ActivityOptions.makeCustomAnimation(this, android.R.anim.fade_in, android.R.anim.fade_out); + startActivity(new Intent(this, ClientDashboardActivity.class), options.toBundle()); finish(); }); }) diff --git a/app/src/main/java/com/example/pap_teste/MyReservationsFragment.java b/app/src/main/java/com/example/pap_teste/MyReservationsFragment.java index 8a5c5f2..01dedbb 100644 --- a/app/src/main/java/com/example/pap_teste/MyReservationsFragment.java +++ b/app/src/main/java/com/example/pap_teste/MyReservationsFragment.java @@ -9,23 +9,22 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.example.pap_teste.models.Reserva; -import com.google.firebase.auth.FirebaseAuth; -import com.google.firebase.firestore.FirebaseFirestore; -import com.google.firebase.firestore.Query; -import com.google.firebase.firestore.QueryDocumentSnapshot; +import com.example.pap_teste.models.LocalReservation; +import com.example.pap_teste.viewmodels.ReservationViewModel; import java.util.ArrayList; import java.util.List; public class MyReservationsFragment extends Fragment { private RecyclerView rvReservations; - private ReservaAdapter adapter; - private List reservationList = new ArrayList<>(); + private LocalReservaAdapter adapter; + private List reservationList = new ArrayList<>(); private ProgressBar progressBar; private View emptyState; - private FirebaseFirestore db; + private ReservationViewModel viewModel; @Nullable @Override @@ -36,63 +35,41 @@ public class MyReservationsFragment extends Fragment { progressBar = view.findViewById(R.id.progressBar); emptyState = view.findViewById(R.id.emptyState); - db = FirebaseFirestore.getInstance(); + viewModel = new ViewModelProvider(this).get(ReservationViewModel.class); setupAdapter(); - loadReservations(); + observeReservations(); return view; } private void setupAdapter() { - adapter = new ReservaAdapter(reservationList, new ReservaAdapter.OnReservaActionListener() { + adapter = new LocalReservaAdapter(reservationList, new LocalReservaAdapter.OnActionListener() { @Override - public void onCheckIn(Reserva reserva) { - // Implement check-in logic if needed - Toast.makeText(getContext(), "Check-in disponível em breve.", Toast.LENGTH_SHORT).show(); + public void onCancel(LocalReservation reservation) { + reservation.setStatus("CANCELADA"); + viewModel.update(reservation); + Toast.makeText(getContext(), "Reserva cancelada.", Toast.LENGTH_SHORT).show(); } @Override - public void onCancel(Reserva reserva) { - cancelReservation(reserva); + public void onEdit(LocalReservation reservation) { + // Show details or edit + Toast.makeText(getContext(), "Detalhes: " + reservation.getNotes(), Toast.LENGTH_LONG).show(); } }); + rvReservations.setLayoutManager(new LinearLayoutManager(getContext())); rvReservations.setAdapter(adapter); } - private void loadReservations() { - String userId = FirebaseAuth.getInstance().getUid(); - if (userId == null) return; - + private void observeReservations() { progressBar.setVisibility(View.VISIBLE); - db.collection("reservations") - .whereEqualTo("userId", userId) - .orderBy("date", Query.Direction.DESCENDING) - .get() - .addOnCompleteListener(task -> { - progressBar.setVisibility(View.GONE); - if (task.isSuccessful()) { - reservationList.clear(); - for (QueryDocumentSnapshot document : task.getResult()) { - Reserva r = document.toObject(Reserva.class); - r.setId(document.getId()); - reservationList.add(r); - } - adapter.notifyDataSetChanged(); - emptyState.setVisibility(reservationList.isEmpty() ? View.VISIBLE : View.GONE); - } else { - Toast.makeText(getContext(), "Erro ao carregar reservas.", Toast.LENGTH_SHORT).show(); - } - }); - } - - private void cancelReservation(Reserva reserva) { - db.collection("reservations").document(reserva.getId()) - .update("status", "cancelled") - .addOnSuccessListener(aVoid -> { - Toast.makeText(getContext(), "Reserva cancelada.", Toast.LENGTH_SHORT).show(); - loadReservations(); - }) - .addOnFailureListener(e -> Toast.makeText(getContext(), "Erro ao cancelar.", Toast.LENGTH_SHORT).show()); + viewModel.getAllReservations().observe(getViewLifecycleOwner(), reservations -> { + progressBar.setVisibility(View.GONE); + reservationList.clear(); + reservationList.addAll(reservations); + adapter.notifyDataSetChanged(); + emptyState.setVisibility(reservationList.isEmpty() ? View.VISIBLE : View.GONE); + }); } } diff --git a/app/src/main/java/com/example/pap_teste/NovaReservaActivity.java b/app/src/main/java/com/example/pap_teste/NovaReservaActivity.java index 52edcf7..4b8d5dc 100644 --- a/app/src/main/java/com/example/pap_teste/NovaReservaActivity.java +++ b/app/src/main/java/com/example/pap_teste/NovaReservaActivity.java @@ -1,333 +1,261 @@ package com.example.pap_teste; +import android.app.DatePickerDialog; +import android.app.TimePickerDialog; import android.os.Bundle; -import androidx.recyclerview.widget.RecyclerView; - +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; import androidx.activity.EdgeToEdge; +import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; - -import android.widget.Button; +import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import com.example.pap_teste.models.FoodCategory; +import com.example.pap_teste.models.LocalReservation; +import com.example.pap_teste.models.Mesa; +import com.example.pap_teste.models.Restaurant; +import com.example.pap_teste.viewmodels.ReservationViewModel; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.database.DataSnapshot; +import com.google.firebase.database.DatabaseError; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; +import com.google.firebase.database.ValueEventListener; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.Locale; public class NovaReservaActivity extends AppCompatActivity { - private enum State { - CATEGORIES, RESTAURANTS, DETAILS - } + private enum State { CATEGORIES, RESTAURANTS, DETAILS } private State currentState = State.CATEGORIES; private String selectedCategory = null; - private com.example.pap_teste.models.Restaurant selectedRestaurant = null; + private Restaurant selectedRestaurant = null; + private Mesa selectedMesa = null; + private String selectedDate = null; + private String selectedTime = null; - private androidx.recyclerview.widget.RecyclerView rvCategories, rvRestaurants; - private android.view.View scrollNovaReserva; - private android.widget.TextView txtTitle; - private android.widget.ProgressBar progressBar; + private RecyclerView rvCategories, rvRestaurants, rvTables; + private View scrollNovaReserva; + private TextView txtTitle; + private ProgressBar progressBar; + private ReservationViewModel viewModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_nova_reserva); + + viewModel = new ViewModelProvider(this).get(ReservationViewModel.class); + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.novaReservaRoot), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); + initViews(); + setupCategories(); + updateViewState(); + } + + private void initViews() { rvCategories = findViewById(R.id.rvCategories); rvRestaurants = findViewById(R.id.rvRestaurants); + rvTables = findViewById(R.id.rvTables); scrollNovaReserva = findViewById(R.id.scrollNovaReserva); txtTitle = findViewById(R.id.txtTituloNovaReserva); progressBar = findViewById(R.id.progressBar); - Button back = findViewById(R.id.btnVoltar); - if (back != null) { - back.setOnClickListener(v -> handleBackNavigation()); - } - - setupCategories(); - updateViewState(); + findViewById(R.id.btnVoltar).setOnClickListener(v -> handleBackNavigation()); + findViewById(R.id.btnSelectDate).setOnClickListener(v -> showDatePicker()); + findViewById(R.id.btnSelectTime).setOnClickListener(v -> showTimePicker()); + findViewById(R.id.btnConfirmarReserva).setOnClickListener(v -> validateAndSave()); } private void handleBackNavigation() { if (currentState == State.RESTAURANTS) { currentState = State.CATEGORIES; - updateViewState(); } else if (currentState == State.DETAILS) { currentState = State.RESTAURANTS; - updateViewState(); } else { finish(); + return; } + updateViewState(); } private void updateViewState() { - rvCategories - .setVisibility(currentState == State.CATEGORIES ? android.view.View.VISIBLE : android.view.View.GONE); - rvRestaurants - .setVisibility(currentState == State.RESTAURANTS ? android.view.View.VISIBLE : android.view.View.GONE); - scrollNovaReserva - .setVisibility(currentState == State.DETAILS ? android.view.View.VISIBLE : android.view.View.GONE); + rvCategories.setVisibility(currentState == State.CATEGORIES ? View.VISIBLE : View.GONE); + rvRestaurants.setVisibility(currentState == State.RESTAURANTS ? View.VISIBLE : View.GONE); + scrollNovaReserva.setVisibility(currentState == State.DETAILS ? View.VISIBLE : View.GONE); - if (currentState == State.CATEGORIES) { - txtTitle.setText("Escolha o tema"); - } else if (currentState == State.RESTAURANTS) { - txtTitle.setText("Restaurantes: " + selectedCategory); - } else { - txtTitle.setText("Reserva: " + (selectedRestaurant != null ? selectedRestaurant.getName() : "")); - setupReservationOptions(); + switch (currentState) { + case CATEGORIES: txtTitle.setText("Escolha o tema"); break; + case RESTAURANTS: txtTitle.setText("Restaurantes: " + selectedCategory); break; + case DETAILS: txtTitle.setText("Reserva: " + (selectedRestaurant != null ? selectedRestaurant.getName() : "")); break; } } private void setupCategories() { - java.util.List cats = new java.util.ArrayList<>(); - cats.add(new com.example.pap_teste.models.FoodCategory("Carnes", R.drawable.cat_carnes)); - cats.add(new com.example.pap_teste.models.FoodCategory("Massas", R.drawable.cat_massas)); - cats.add(new com.example.pap_teste.models.FoodCategory("Sushi", R.drawable.cat_sushi)); - cats.add(new com.example.pap_teste.models.FoodCategory("Pizzas", R.drawable.cat_pizzas)); - cats.add(new com.example.pap_teste.models.FoodCategory("Sobremesas", R.drawable.cat_sobremesas)); + List cats = new ArrayList<>(); + cats.add(new FoodCategory("Carnes", R.drawable.cat_carnes)); + cats.add(new FoodCategory("Massas", R.drawable.cat_massas)); + cats.add(new FoodCategory("Sushi", R.drawable.cat_sushi)); + cats.add(new FoodCategory("Pizzas", R.drawable.cat_pizzas)); + cats.add(new FoodCategory("Sobremesas", R.drawable.cat_sobremesas)); - rvCategories.setLayoutManager(new androidx.recyclerview.widget.LinearLayoutManager(this)); + rvCategories.setLayoutManager(new LinearLayoutManager(this)); rvCategories.setAdapter(new FoodCategoryAdapter(cats, category -> { selectedCategory = category.getName(); currentState = State.RESTAURANTS; - setupRestaurants(); + loadRestaurants(); updateViewState(); })); } - private void setupRestaurants() { - java.util.List filteredList = new java.util.ArrayList<>(); - com.google.firebase.database.DatabaseReference usersRef = com.google.firebase.database.FirebaseDatabase - .getInstance().getReference("Restaurantes"); - - if (progressBar != null) - progressBar.setVisibility(android.view.View.VISIBLE); - - usersRef.orderByChild("category").equalTo(selectedCategory) - .addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() { - @Override - public void onDataChange( - @androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) { - if (progressBar != null) - progressBar.setVisibility(android.view.View.GONE); - filteredList.clear(); - for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) { - String role = ds.child("role").getValue(String.class); - String accountType = ds.child("accountType").getValue(String.class); - - if ("ADMIN".equalsIgnoreCase(role) || "ESTABELECIMENTO".equalsIgnoreCase(accountType)) { - String name = ds.child("establishmentName").getValue(String.class); - if (name == null) - name = ds.child("displayName").getValue(String.class); - String email = ds.child("email").getValue(String.class); - String cat = ds.child("category").getValue(String.class); - String logoUrl = ds.child("logoUrl").getValue(String.class); - - if (name != null && email != null) { - filteredList.add(new com.example.pap_teste.models.Restaurant(name, cat, email, - false, logoUrl)); - } - } - } - - rvRestaurants.setLayoutManager( - new androidx.recyclerview.widget.LinearLayoutManager(NovaReservaActivity.this)); - rvRestaurants.setAdapter(new RestaurantAdapter(filteredList, restaurant -> { - selectedRestaurant = restaurant; - currentState = State.DETAILS; - updateViewState(); - })); + private void loadRestaurants() { + progressBar.setVisibility(View.VISIBLE); + DatabaseReference ref = FirebaseDatabase.getInstance().getReference("Restaurantes"); + ref.orderByChild("category").equalTo(selectedCategory).addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(@NonNull DataSnapshot snapshot) { + progressBar.setVisibility(View.GONE); + List list = new ArrayList<>(); + for (DataSnapshot ds : snapshot.getChildren()) { + String name = ds.child("establishmentName").getValue(String.class); + if (name == null) name = ds.child("displayName").getValue(String.class); + String email = ds.child("email").getValue(String.class); + String cat = ds.child("category").getValue(String.class); + String logo = ds.child("logoUrl").getValue(String.class); + if (name != null && email != null) { + list.add(new Restaurant(name, cat, email, false, logo)); } + } + rvRestaurants.setLayoutManager(new LinearLayoutManager(NovaReservaActivity.this)); + rvRestaurants.setAdapter(new RestaurantAdapter(list, restaurant -> { + selectedRestaurant = restaurant; + currentState = State.DETAILS; + loadTables(); + updateViewState(); + })); + } - @Override - public void onCancelled( - @androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) { - if (progressBar != null) - progressBar.setVisibility(android.view.View.GONE); - android.widget.Toast.makeText(NovaReservaActivity.this, "Erro ao carregar restaurantes.", - android.widget.Toast.LENGTH_SHORT).show(); - } - }); + @Override + public void onCancelled(@NonNull DatabaseError error) { + progressBar.setVisibility(View.GONE); + Toast.makeText(NovaReservaActivity.this, "Erro ao carregar.", Toast.LENGTH_SHORT).show(); + } + }); } - private String selectedDate = null; - private String selectedTime = null; + private void loadTables() { + progressBar.setVisibility(View.VISIBLE); + DatabaseReference ref = FirebaseDatabase.getInstance().getReference("Mesas"); + ref.addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(@NonNull DataSnapshot snapshot) { + progressBar.setVisibility(View.GONE); + List tables = new ArrayList<>(); + for (DataSnapshot ds : snapshot.getChildren()) { + Mesa m = ds.getValue(Mesa.class); + if (m != null && selectedRestaurant.getEmail().equalsIgnoreCase(m.getRestauranteEmail())) { + tables.add(m); + } + } + rvTables.setLayoutManager(new GridLayoutManager(NovaReservaActivity.this, 3)); + rvTables.setAdapter(new TableSelectionAdapter(tables, mesa -> selectedMesa = mesa)); + } - private void setupReservationOptions() { - android.widget.Button btnDate = findViewById(R.id.btnSelectDate); - android.widget.Button btnTime = findViewById(R.id.btnSelectTime); - - btnDate.setOnClickListener(v -> { - java.util.Calendar cal = java.util.Calendar.getInstance(); - new android.app.DatePickerDialog(this, (view, year, month, dayOfMonth) -> { - selectedDate = dayOfMonth + "/" + (month + 1) + "/" + year; - btnDate.setText(selectedDate); - }, cal.get(java.util.Calendar.YEAR), cal.get(java.util.Calendar.MONTH), - cal.get(java.util.Calendar.DAY_OF_MONTH)).show(); + @Override + public void onCancelled(@NonNull DatabaseError error) { + progressBar.setVisibility(View.GONE); + } }); - - btnTime.setOnClickListener(v -> { - java.util.Calendar cal = java.util.Calendar.getInstance(); - new android.app.TimePickerDialog(this, (view, hourOfDay, minute) -> { - selectedTime = String.format(java.util.Locale.getDefault(), "%02d:%02d", hourOfDay, minute); - btnTime.setText(selectedTime); - }, cal.get(java.util.Calendar.HOUR_OF_DAY), cal.get(java.util.Calendar.MINUTE), true).show(); - }); - - findViewById(R.id.btnConfirmarReserva).setOnClickListener(v -> saveReservation()); } - private void saveReservation() { - android.widget.EditText etPartySize = findViewById(R.id.etPartySize); - int val = 0; - try { - val = Integer.parseInt(etPartySize.getText().toString()); - } catch (Exception e) { - } - final int partySize = val; + private void showDatePicker() { + Calendar cal = Calendar.getInstance(); + new DatePickerDialog(this, (view, year, month, day) -> { + selectedDate = String.format(Locale.getDefault(), "%02d/%02d/%d", day, month + 1, year); + ((Button)findViewById(R.id.btnSelectDate)).setText(selectedDate); + }, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show(); + } - if (selectedDate == null || selectedTime == null || partySize == 0) { - android.widget.Toast.makeText(this, "Por favor, selecione data, hora e número de pessoas.", - android.widget.Toast.LENGTH_SHORT).show(); + private void showTimePicker() { + Calendar cal = Calendar.getInstance(); + new TimePickerDialog(this, (view, hour, min) -> { + selectedTime = String.format(Locale.getDefault(), "%02d:%02d", hour, min); + ((Button)findViewById(R.id.btnSelectTime)).setText(selectedTime); + }, cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), true).show(); + } + + private void validateAndSave() { + EditText etPartySize = findViewById(R.id.etPartySize); + EditText etNotes = findViewById(R.id.etNotes); + String partySizeStr = etPartySize.getText().toString(); + + if (selectedDate == null || selectedTime == null || selectedMesa == null || partySizeStr.isEmpty()) { + Toast.makeText(this, "Preencha todos os campos e escolha a mesa.", Toast.LENGTH_SHORT).show(); return; } - String restEmail = selectedRestaurant.getEmail(); + int partySize = Integer.parseInt(partySizeStr); + if (partySize > selectedMesa.getCapacidade()) { + Toast.makeText(this, "A mesa escolhida só suporta " + selectedMesa.getCapacidade() + " pessoas.", Toast.LENGTH_SHORT).show(); + return; + } - if (progressBar != null) - progressBar.setVisibility(android.view.View.VISIBLE); - android.widget.Button btnConfirmar = findViewById(R.id.btnConfirmarReserva); - if (btnConfirmar != null) - btnConfirmar.setEnabled(false); - - com.google.firebase.database.DatabaseReference mesasRef = com.google.firebase.database.FirebaseDatabase - .getInstance().getReference("Mesas"); - - mesasRef.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() { - @Override - public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) { - int totalMesas = 0; - for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) { - com.example.pap_teste.models.Mesa m = ds.getValue(com.example.pap_teste.models.Mesa.class); - if (m != null && m.getRestauranteEmail() != null && restEmail.trim().equalsIgnoreCase(m.getRestauranteEmail().trim())) { - totalMesas++; - } + // Conflict check + viewModel.checkConflict(selectedDate, selectedMesa.getNumero(), isAvailable -> { + runOnUiThread(() -> { + if (isAvailable) { + saveToFirebaseAndRoom(partySize, etNotes.getText().toString()); + } else { + Toast.makeText(this, "Esta mesa já está reservada para este dia.", Toast.LENGTH_LONG).show(); } - - if (totalMesas == 0) { - proceedWithReservation(partySize); - return; - } - - checkReservationsAndSave(totalMesas, partySize); - } - - @Override - public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) { - proceedWithReservation(partySize); - } + }); }); } - private void checkReservationsAndSave(int totalMesas, final int partySize) { - String restEmail = selectedRestaurant.getEmail(); - com.google.firebase.database.DatabaseReference reservasRef = com.google.firebase.database.FirebaseDatabase - .getInstance().getReference("reservas"); - - reservasRef.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() { - @Override - public void onDataChange( - @androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) { - int ocupadas = 0; - java.util.Map ocupacaoPorHora = new java.util.HashMap<>(); - - for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) { - com.example.pap_teste.models.Reserva r = ds - .getValue(com.example.pap_teste.models.Reserva.class); - if (r != null && r.getRestauranteEmail() != null && - r.getRestauranteEmail().trim().equalsIgnoreCase(restEmail.trim()) && - selectedDate.equals(r.getData()) && !"Cancelada".equals(r.getEstado()) - && !"Recusada".equals(r.getEstado())) { - int count = ocupacaoPorHora.getOrDefault(r.getHora(), 0) + 1; - ocupacaoPorHora.put(r.getHora(), count); - if (selectedTime.equals(r.getHora())) { - ocupadas++; - } - } - } - - if (ocupadas >= totalMesas) { - String sugestao = ""; - String[] horasComuns = { "12:00", "12:30", "13:00", "13:30", "14:00", "19:00", "19:30", - "20:00", "20:30", "21:00", "21:30", "22:00" }; - for (String h : horasComuns) { - if (ocupacaoPorHora.getOrDefault(h, 0) < totalMesas && !h.equals(selectedTime)) { - sugestao = h; - break; // Encontramos a primeira sugestão livre - } - } - String msg = "Não há mesas disponíveis para as " + selectedTime + "."; - if (!sugestao.isEmpty()) { - msg += " Sugestão: tente reservar para as " + sugestao + "."; - } else { - msg += " Tente para outro dia."; - } - android.widget.Toast - .makeText(NovaReservaActivity.this, msg, android.widget.Toast.LENGTH_LONG).show(); - } else { - proceedWithReservation(partySize); - } - } - - @Override - public void onCancelled( - @androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) { - proceedWithReservation(partySize); - } - }); - } - - private void proceedWithReservation(int partySize) { - String userEmail = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser() != null - ? com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser().getEmail() - : "cliente@teste.com"; - com.google.firebase.database.DatabaseReference ref = com.google.firebase.database.FirebaseDatabase.getInstance() - .getReference("reservas"); + private void saveToFirebaseAndRoom(int guests, String notes) { + progressBar.setVisibility(View.VISIBLE); + DatabaseReference ref = FirebaseDatabase.getInstance().getReference("reservas"); String id = ref.push().getKey(); + String userEmail = FirebaseAuth.getInstance().getCurrentUser() != null ? FirebaseAuth.getInstance().getCurrentUser().getEmail() : "anon@test.com"; - com.example.pap_teste.models.Reserva reserva = new com.example.pap_teste.models.Reserva( - id, - userEmail, - selectedRestaurant.getName(), - selectedRestaurant.getEmail(), - selectedDate, - selectedTime, - partySize, - "Pendente"); + LocalReservation local = new LocalReservation(); + local.setFirebaseId(id); + local.setRestaurantName(selectedRestaurant.getName()); + local.setRestaurantEmail(selectedRestaurant.getEmail()); + local.setDate(selectedDate); + local.setTime(selectedTime); + local.setGuests(guests); + local.setTableNumber(selectedMesa.getNumero()); + local.setNotes(notes); + local.setStatus("PENDENTE"); if (id != null) { - ref.child(id).setValue(reserva).addOnCompleteListener(task -> { - if (progressBar != null) - progressBar.setVisibility(android.view.View.GONE); - android.widget.Button btnConfirmar = findViewById(R.id.btnConfirmarReserva); - if (btnConfirmar != null) - btnConfirmar.setEnabled(true); - + ref.child(id).setValue(local).addOnCompleteListener(task -> { + progressBar.setVisibility(View.GONE); if (task.isSuccessful()) { - android.widget.Toast - .makeText(NovaReservaActivity.this, "Reserva solicitada com sucesso!", - android.widget.Toast.LENGTH_SHORT) - .show(); + viewModel.insert(local); + Toast.makeText(this, "Reserva realizada com sucesso!", Toast.LENGTH_LONG).show(); finish(); } else { - android.widget.Toast - .makeText(NovaReservaActivity.this, "Erro ao salvar reserva.", - android.widget.Toast.LENGTH_SHORT) - .show(); + Toast.makeText(this, "Erro ao guardar no servidor.", Toast.LENGTH_SHORT).show(); } }); } diff --git a/app/src/main/java/com/example/pap_teste/OnboardingActivity.java b/app/src/main/java/com/example/pap_teste/OnboardingActivity.java index 387401c..9ce7229 100644 --- a/app/src/main/java/com/example/pap_teste/OnboardingActivity.java +++ b/app/src/main/java/com/example/pap_teste/OnboardingActivity.java @@ -39,7 +39,8 @@ public class OnboardingActivity extends AppCompatActivity { tagline.startAnimation(fadeIn); btnGetStarted.setOnClickListener(v -> { - startActivity(new Intent(OnboardingActivity.this, MainActivity.class)); + android.app.ActivityOptions options = android.app.ActivityOptions.makeCustomAnimation(this, android.R.anim.fade_in, android.R.anim.fade_out); + startActivity(new Intent(OnboardingActivity.this, MainActivity.class), options.toBundle()); finish(); }); } diff --git a/app/src/main/java/com/example/pap_teste/ProfileFragment.java b/app/src/main/java/com/example/pap_teste/ProfileFragment.java index 7a4ee69..cbd415a 100644 --- a/app/src/main/java/com/example/pap_teste/ProfileFragment.java +++ b/app/src/main/java/com/example/pap_teste/ProfileFragment.java @@ -10,6 +10,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; +import android.widget.Toast; import com.bumptech.glide.Glide; import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseUser; diff --git a/app/src/main/java/com/example/pap_teste/SplashActivity.java b/app/src/main/java/com/example/pap_teste/SplashActivity.java new file mode 100644 index 0000000..4c79594 --- /dev/null +++ b/app/src/main/java/com/example/pap_teste/SplashActivity.java @@ -0,0 +1,36 @@ +package com.example.pap_teste; + +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.ImageView; +import android.widget.TextView; +import androidx.appcompat.app.AppCompatActivity; + +public class SplashActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_splash); + + ImageView logo = findViewById(R.id.splash_logo); + TextView title = findViewById(R.id.splash_title); + TextView subtitle = findViewById(R.id.splash_subtitle); + + Animation fadeIn = AnimationUtils.loadAnimation(this, android.R.anim.fade_in); + fadeIn.setDuration(1500); + + logo.startAnimation(fadeIn); + title.startAnimation(fadeIn); + subtitle.startAnimation(fadeIn); + + new Handler(Looper.getMainLooper()).postDelayed(() -> { + startActivity(new Intent(SplashActivity.this, OnboardingActivity.class)); + finish(); + }, 2500); + } +} diff --git a/app/src/main/java/com/example/pap_teste/TableSelectionAdapter.java b/app/src/main/java/com/example/pap_teste/TableSelectionAdapter.java new file mode 100644 index 0000000..98edb54 --- /dev/null +++ b/app/src/main/java/com/example/pap_teste/TableSelectionAdapter.java @@ -0,0 +1,87 @@ +package com.example.pap_teste; + +import android.graphics.Color; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import com.example.pap_teste.models.Mesa; +import com.google.android.material.card.MaterialCardView; +import java.util.List; + +public class TableSelectionAdapter extends RecyclerView.Adapter { + + private final List tables; + private final OnTableClickListener listener; + private int selectedPosition = -1; + + public interface OnTableClickListener { + void onTableClick(Mesa mesa); + } + + public TableSelectionAdapter(List tables, OnTableClickListener listener) { + this.tables = tables; + this.listener = listener; + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_table, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + Mesa mesa = tables.get(position); + holder.txtNumber.setText("Mesa " + mesa.getNumero()); + + boolean isOccupied = "OCUPADA".equalsIgnoreCase(mesa.getEstado()) || "RESERVADA".equalsIgnoreCase(mesa.getEstado()); + holder.txtStatus.setText(mesa.getEstado()); + + if (isOccupied) { + holder.card.setCardBackgroundColor(Color.parseColor("#F5F5F5")); + holder.card.setStrokeColor(Color.TRANSPARENT); + holder.txtStatus.setTextColor(Color.GRAY); + holder.itemView.setEnabled(false); + holder.imgStatus.setAlpha(0.3f); + } else { + boolean isSelected = selectedPosition == position; + holder.card.setCardBackgroundColor(isSelected ? holder.itemView.getContext().getColor(R.color.colorChipConfirmed) : Color.WHITE); + holder.card.setStrokeColor(isSelected ? holder.itemView.getContext().getColor(R.color.colorSuccess) : holder.itemView.getContext().getColor(R.color.colorBorder)); + holder.txtStatus.setTextColor(isSelected ? holder.itemView.getContext().getColor(R.color.colorSuccess) : holder.itemView.getContext().getColor(R.color.colorTextSecondary)); + holder.itemView.setEnabled(true); + holder.imgStatus.setAlpha(1.0f); + } + + holder.itemView.setOnClickListener(v -> { + int previousSelected = selectedPosition; + selectedPosition = holder.getAdapterPosition(); + if (previousSelected != -1) notifyItemChanged(previousSelected); + notifyItemChanged(selectedPosition); + listener.onTableClick(mesa); + }); + } + + @Override + public int getItemCount() { + return tables.size(); + } + + static class ViewHolder extends RecyclerView.ViewHolder { + MaterialCardView card; + TextView txtNumber, txtStatus; + ImageView imgStatus; + + ViewHolder(View itemView) { + super(itemView); + card = itemView.findViewById(R.id.cardTable); + txtNumber = itemView.findViewById(R.id.txtTableNumber); + txtStatus = itemView.findViewById(R.id.txtTableStatus); + imgStatus = itemView.findViewById(R.id.imgTableStatus); + } + } +} diff --git a/app/src/main/java/com/example/pap_teste/data/AppDatabase.java b/app/src/main/java/com/example/pap_teste/data/AppDatabase.java new file mode 100644 index 0000000..ed12ef8 --- /dev/null +++ b/app/src/main/java/com/example/pap_teste/data/AppDatabase.java @@ -0,0 +1,24 @@ +package com.example.pap_teste.data; + +import android.content.Context; +import androidx.room.Database; +import androidx.room.Room; +import androidx.room.RoomDatabase; +import com.example.pap_teste.models.LocalReservation; + +@Database(entities = {LocalReservation.class}, version = 1, exportSchema = false) +public abstract class AppDatabase extends RoomDatabase { + private static AppDatabase instance; + + public abstract ReservationDao reservationDao(); + + public static synchronized AppDatabase getInstance(Context context) { + if (instance == null) { + instance = Room.databaseBuilder(context.getApplicationContext(), + AppDatabase.class, "namesa_database") + .fallbackToDestructiveMigration() + .build(); + } + return instance; + } +} diff --git a/app/src/main/java/com/example/pap_teste/data/ReservationDao.java b/app/src/main/java/com/example/pap_teste/data/ReservationDao.java new file mode 100644 index 0000000..bfed1b2 --- /dev/null +++ b/app/src/main/java/com/example/pap_teste/data/ReservationDao.java @@ -0,0 +1,29 @@ +package com.example.pap_teste.data; + +import androidx.lifecycle.LiveData; +import androidx.room.Dao; +import androidx.room.Delete; +import androidx.room.Insert; +import androidx.room.OnConflictStrategy; +import androidx.room.Query; +import androidx.room.Update; +import com.example.pap_teste.models.LocalReservation; +import java.util.List; + +@Dao +public interface ReservationDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + void insert(LocalReservation reservation); + + @Update + void update(LocalReservation reservation); + + @Delete + void delete(LocalReservation reservation); + + @Query("SELECT * FROM local_reservations ORDER BY date DESC, time DESC") + LiveData> getAllReservations(); + + @Query("SELECT * FROM local_reservations WHERE date = :date AND tableNumber = :tableId AND status != 'CANCELLED'") + List getReservationsForTable(String date, int tableId); +} diff --git a/app/src/main/java/com/example/pap_teste/models/LocalReservation.java b/app/src/main/java/com/example/pap_teste/models/LocalReservation.java new file mode 100644 index 0000000..880a22a --- /dev/null +++ b/app/src/main/java/com/example/pap_teste/models/LocalReservation.java @@ -0,0 +1,51 @@ +package com.example.pap_teste.models; + +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "local_reservations") +public class LocalReservation { + @PrimaryKey(autoGenerate = true) + private int id; + private String firebaseId; + private String restaurantName; + private String restaurantEmail; + private String date; + private String time; + private int guests; + private String status; + private int tableNumber; + private String notes; + + public LocalReservation() {} + + public int getId() { return id; } + public void setId(int id) { this.id = id; } + + public String getFirebaseId() { return firebaseId; } + public void setFirebaseId(String firebaseId) { this.firebaseId = firebaseId; } + + public String getRestaurantName() { return restaurantName; } + public void setRestaurantName(String restaurantName) { this.restaurantName = restaurantName; } + + public String getRestaurantEmail() { return restaurantEmail; } + public void setRestaurantEmail(String restaurantEmail) { this.restaurantEmail = restaurantEmail; } + + public String getDate() { return date; } + public void setDate(String date) { this.date = date; } + + public String getTime() { return time; } + public void setTime(String time) { this.time = time; } + + public int getGuests() { return guests; } + public void setGuests(int guests) { this.guests = guests; } + + public String getStatus() { return status; } + public void setStatus(String status) { this.status = status; } + + public int getTableNumber() { return tableNumber; } + public void setTableNumber(int tableNumber) { this.tableNumber = tableNumber; } + + public String getNotes() { return notes; } + public void setNotes(String notes) { this.notes = notes; } +} diff --git a/app/src/main/java/com/example/pap_teste/models/Reserva.java b/app/src/main/java/com/example/pap_teste/models/Reserva.java index 6f5ab33..d29286b 100644 --- a/app/src/main/java/com/example/pap_teste/models/Reserva.java +++ b/app/src/main/java/com/example/pap_teste/models/Reserva.java @@ -1,14 +1,25 @@ package com.example.pap_teste.models; +import com.google.firebase.Timestamp; + public class Reserva { private String id; + private String userId; + private String restaurantId; + private String restaurantName; private String clienteEmail; private String restauranteName; private String restauranteEmail; private String data; private String hora; private int pessoas; + private int guests; private String estado; + private String status; + private Timestamp date; + private String timeSlot; + private String specialRequests; + private Timestamp createdAt; public Reserva() { } @@ -25,27 +36,41 @@ public class Reserva { this.estado = estado; } + // Existing getters/setters public String getId() { return id; } public void setId(String id) { this.id = id; } - public String getClienteEmail() { return clienteEmail; } public void setClienteEmail(String clienteEmail) { this.clienteEmail = clienteEmail; } - public String getRestauranteName() { return restauranteName; } public void setRestauranteName(String restauranteName) { this.restauranteName = restauranteName; } - public String getRestauranteEmail() { return restauranteEmail; } public void setRestauranteEmail(String restauranteEmail) { this.restauranteEmail = restauranteEmail; } - public String getData() { return data; } public void setData(String data) { this.data = data; } - public String getHora() { return hora; } public void setHora(String hora) { this.hora = hora; } - public int getPessoas() { return pessoas; } public void setPessoas(int pessoas) { this.pessoas = pessoas; } - public String getEstado() { return estado; } public void setEstado(String estado) { this.estado = estado; } + + // New getters/setters to fix ReservationActivity + public String getUserId() { return userId != null ? userId : clienteEmail; } + public void setUserId(String userId) { this.userId = userId; } + public String getRestaurantId() { return restaurantId != null ? restaurantId : restauranteEmail; } + public void setRestaurantId(String restaurantId) { this.restaurantId = restaurantId; } + public String getRestaurantName() { return restaurantName != null ? restaurantName : restauranteName; } + public void setRestaurantName(String restaurantName) { this.restaurantName = restaurantName; } + public int getGuests() { return guests != 0 ? guests : pessoas; } + public void setGuests(int guests) { this.guests = guests; } + public String getStatus() { return status != null ? status : estado; } + public void setStatus(String status) { this.status = status; } + public Timestamp getDate() { return date; } + public void setDate(Timestamp date) { this.date = date; } + public String getTimeSlot() { return timeSlot != null ? timeSlot : hora; } + public void setTimeSlot(String timeSlot) { this.timeSlot = timeSlot; } + public String getSpecialRequests() { return specialRequests; } + public void setSpecialRequests(String specialRequests) { this.specialRequests = specialRequests; } + public Timestamp getCreatedAt() { return createdAt; } + public void setCreatedAt(Timestamp createdAt) { this.createdAt = createdAt; } } diff --git a/app/src/main/java/com/example/pap_teste/viewmodels/ReservationViewModel.java b/app/src/main/java/com/example/pap_teste/viewmodels/ReservationViewModel.java new file mode 100644 index 0000000..c002c8e --- /dev/null +++ b/app/src/main/java/com/example/pap_teste/viewmodels/ReservationViewModel.java @@ -0,0 +1,53 @@ +package com.example.pap_teste.viewmodels; + +import android.app.Application; +import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import com.example.pap_teste.data.AppDatabase; +import com.example.pap_teste.data.ReservationDao; +import com.example.pap_teste.models.LocalReservation; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class ReservationViewModel extends AndroidViewModel { + private final ReservationDao reservationDao; + private final ExecutorService executorService; + private final LiveData> allReservations; + + public ReservationViewModel(@NonNull Application application) { + super(application); + AppDatabase db = AppDatabase.getInstance(application); + reservationDao = db.reservationDao(); + allReservations = reservationDao.getAllReservations(); + executorService = Executors.newSingleThreadExecutor(); + } + + public LiveData> getAllReservations() { + return allReservations; + } + + public void insert(LocalReservation reservation) { + executorService.execute(() -> reservationDao.insert(reservation)); + } + + public void update(LocalReservation reservation) { + executorService.execute(() -> reservationDao.update(reservation)); + } + + public void delete(LocalReservation reservation) { + executorService.execute(() -> reservationDao.delete(reservation)); + } + + public void checkConflict(String date, int tableId, OnConflictCheckListener listener) { + executorService.execute(() -> { + List conflicts = reservationDao.getReservationsForTable(date, tableId); + listener.onResult(conflicts.isEmpty()); + }); + } + + public interface OnConflictCheckListener { + void onResult(boolean isAvailable); + } +} diff --git a/app/src/main/res/layout/activity_nova_reserva.xml b/app/src/main/res/layout/activity_nova_reserva.xml index f665f28..d099154 100644 --- a/app/src/main/res/layout/activity_nova_reserva.xml +++ b/app/src/main/res/layout/activity_nova_reserva.xml @@ -161,6 +161,24 @@ android:textColor="@color/colorTextPrimary" app:strokeColor="@color/colorDivider" /> + + + + + + + + diff --git a/app/src/main/res/layout/activity_splash.xml b/app/src/main/res/layout/activity_splash.xml new file mode 100644 index 0000000..3071c49 --- /dev/null +++ b/app/src/main/res/layout/activity_splash.xml @@ -0,0 +1,47 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout/item_reserva_local.xml b/app/src/main/res/layout/item_reserva_local.xml new file mode 100644 index 0000000..3fe9548 --- /dev/null +++ b/app/src/main/res/layout/item_reserva_local.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_table.xml b/app/src/main/res/layout/item_table.xml new file mode 100644 index 0000000..d27aa16 --- /dev/null +++ b/app/src/main/res/layout/item_table.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 57bf325..8d2cc6d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,6 +13,7 @@ googleServices = "4.4.2" firebaseDatabase = "22.0.1" glide = "4.16.0" firebaseStorage = "21.0.1" +room = "2.6.1" [libraries] junit = { group = "junit", name = "junit", version.ref = "junit" } @@ -27,6 +28,9 @@ firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.r firebase-database = { group = "com.google.firebase", name = "firebase-database", version.ref = "firebaseDatabase" } glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "glide" } firebase-storage = { group = "com.google.firebase", name = "firebase-storage", version.ref = "firebaseStorage" } +room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } +room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } +room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" }