From a921553f2b01e3cfca6a0c4caa8f3ebbafffdc02 Mon Sep 17 00:00:00 2001 From: 230409 <230409@epvc.pt> Date: Wed, 18 Mar 2026 10:40:19 +0000 Subject: [PATCH] ... --- .../pap_teste/ClientDashboardActivity.java | 28 ++++- .../ExplorarRestaurantesActivity.java | 64 ++++------ .../example/pap_teste/FavoritosActivity.java | 53 +++++++- .../com/example/pap_teste/MainActivity.java | 51 ++++++++ .../pap_teste/NovaReservaActivity.java | 62 ++++------ .../pap_teste/ProfileDashboardActivity.java | 15 ++- .../com/example/pap_teste/ReservaAdapter.java | 25 +++- .../example/pap_teste/RestaurantAdapter.java | 57 ++++++++- .../example/pap_teste/models/Restaurant.java | 9 ++ .../res/layout/activity_client_dashboard.xml | 3 +- .../layout/activity_explorar_restaurantes.xml | 117 +++++++++--------- app/src/main/res/layout/activity_main.xml | 47 +++++-- .../main/res/layout/activity_nova_reserva.xml | 117 +++++++++--------- .../main/res/layout/item_reserva_cliente.xml | 11 ++ 14 files changed, 438 insertions(+), 221 deletions(-) diff --git a/app/src/main/java/com/example/pap_teste/ClientDashboardActivity.java b/app/src/main/java/com/example/pap_teste/ClientDashboardActivity.java index f97ced8..5ef1152 100644 --- a/app/src/main/java/com/example/pap_teste/ClientDashboardActivity.java +++ b/app/src/main/java/com/example/pap_teste/ClientDashboardActivity.java @@ -53,6 +53,7 @@ public class ClientDashboardActivity extends AppCompatActivity { }); updateGreeting(); + fetchProfilePicture(); setupCategories(); setupActions(); } @@ -61,6 +62,22 @@ public class ClientDashboardActivity extends AppCompatActivity { txtGreeting.setText(String.format("Olá, %s", displayName != null ? displayName : "convidado")); } + private void fetchProfilePicture() { + if (email == null) return; + String documentId = email.replace(".", "_").replace("@", "_at_"); + com.google.firebase.database.FirebaseDatabase.getInstance().getReference() + .child("users").child(documentId).child("photoUrl") + .get().addOnSuccessListener(snapshot -> { + if (!isDestroyed() && snapshot.exists()) { + String photoUrl = snapshot.getValue(String.class); + if (photoUrl != null && !photoUrl.isEmpty()) { + android.widget.ImageView imgProfile = findViewById(R.id.imgProfile); + com.bumptech.glide.Glide.with(this).load(photoUrl).circleCrop().into(imgProfile); + } + } + }); + } + private void setupCategories() { RecyclerView rv = findViewById(R.id.rvCategories); List cats = new ArrayList<>(); @@ -90,9 +107,14 @@ public class ClientDashboardActivity extends AppCompatActivity { findViewById(R.id.btnVoltar).setOnClickListener(v -> finish()); findViewById(R.id.btnCheckIn).setOnClickListener(v -> { - Intent intent = new Intent(this, CheckInAntecipadoActivity.class); - intent.putExtra("restaurant_email", "sabor_arte@restaurante.com"); - startActivity(intent); + boolean hasLocationPermission = androidx.core.app.ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) == android.content.pm.PackageManager.PERMISSION_GRANTED; + if (!hasLocationPermission) { + android.widget.Toast.makeText(this, "Dê permissões de localização para aceder ao check-in.", android.widget.Toast.LENGTH_SHORT).show(); + } else { + Intent intent = new Intent(this, CheckInAntecipadoActivity.class); + intent.putExtra("restaurant_email", "sabor_arte@restaurante.com"); + startActivity(intent); + } }); findViewById(R.id.btnPartilhar).setOnClickListener( diff --git a/app/src/main/java/com/example/pap_teste/ExplorarRestaurantesActivity.java b/app/src/main/java/com/example/pap_teste/ExplorarRestaurantesActivity.java index e83cbc3..068409d 100644 --- a/app/src/main/java/com/example/pap_teste/ExplorarRestaurantesActivity.java +++ b/app/src/main/java/com/example/pap_teste/ExplorarRestaurantesActivity.java @@ -25,7 +25,7 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity { private String selectedDate = null; private String selectedTime = null; - private int selectedPartySize = 0; + @Override protected void onCreate(Bundle savedInstanceState) { @@ -120,51 +120,37 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity { } }); } - private void setupReservationOptions() { - // Dates - androidx.recyclerview.widget.RecyclerView rvDates = findViewById(R.id.rvDates); - java.util.List dates = new java.util.ArrayList<>(); - dates.add("Hoje"); - dates.add("Amanhã"); - dates.add("Quarta, 12 Mar"); - dates.add("Quinta, 13 Mar"); - dates.add("Sexta, 14 Mar"); - rvDates.setAdapter(new ReservationOptionAdapter(dates, date -> selectedDate = date)); + android.widget.Button btnDate = findViewById(R.id.btnSelectDate); + android.widget.Button btnTime = findViewById(R.id.btnSelectTime); - // Times - androidx.recyclerview.widget.RecyclerView rvTimes = findViewById(R.id.rvTimes); - java.util.List times = new java.util.ArrayList<>(); - times.add("12:00"); - times.add("13:00"); - times.add("19:00"); - times.add("20:00"); - times.add("21:00"); - times.add("22:00"); - rvTimes.setAdapter(new ReservationOptionAdapter(times, time -> selectedTime = time)); + 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(); + }); - // Party Size - androidx.recyclerview.widget.RecyclerView rvParty = findViewById(R.id.rvPartySize); - java.util.List party = new java.util.ArrayList<>(); - party.add("1 pessoa"); - party.add("2 pessoas"); - party.add("3 pessoas"); - party.add("4 pessoas"); - party.add("5 pessoas"); - party.add("6+ pessoas"); - rvParty.setAdapter(new ReservationOptionAdapter(party, size -> { - try { - selectedPartySize = Integer.parseInt(size.split(" ")[0].replace("+", "")); - } catch (Exception e) { - selectedPartySize = 6; - } - })); + 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() { - if (selectedDate == null || selectedTime == null || selectedPartySize == 0) { + android.widget.EditText etPartySize = findViewById(R.id.etPartySize); + int partySize = 0; + try { + partySize = Integer.parseInt(etPartySize.getText().toString()); + } catch (Exception e) {} + + 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(); return; @@ -185,7 +171,7 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity { selectedRestaurant.getEmail(), selectedDate, selectedTime, - selectedPartySize, + partySize, "Pendente"); if (id != null) { diff --git a/app/src/main/java/com/example/pap_teste/FavoritosActivity.java b/app/src/main/java/com/example/pap_teste/FavoritosActivity.java index b243ba8..a10be70 100644 --- a/app/src/main/java/com/example/pap_teste/FavoritosActivity.java +++ b/app/src/main/java/com/example/pap_teste/FavoritosActivity.java @@ -3,15 +3,33 @@ package com.example.pap_teste; import android.os.Bundle; 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 android.widget.Toast; + +import com.example.pap_teste.models.Restaurant; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseUser; +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.List; public class FavoritosActivity extends AppCompatActivity { + private androidx.recyclerview.widget.RecyclerView rv; + private RestaurantAdapter adapter; + private List list; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -28,16 +46,39 @@ public class FavoritosActivity extends AppCompatActivity { back.setOnClickListener(v -> finish()); } + rv = findViewById(R.id.rvFavoritos); + list = new ArrayList<>(); + adapter = new RestaurantAdapter(list, null); + rv.setAdapter(adapter); + setupFavoritesList(); } private void setupFavoritesList() { - androidx.recyclerview.widget.RecyclerView rv = findViewById(R.id.rvFavoritos); - java.util.List list = new java.util.ArrayList<>(); - list.add(new com.example.pap_teste.models.Restaurant("Sabor & Arte", "Tradicional", "sabor_arte@restaurante.com", true)); - list.add(new com.example.pap_teste.models.Restaurant("O Chuveiro", "Mariscos", "chuveiro@restaurante.com", true)); + FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); + if (user != null && user.getEmail() != null) { + String encodedUserEmail = user.getEmail().replace(".", "_").replace("@", "_at_"); + DatabaseReference favRef = FirebaseDatabase.getInstance().getReference("users") + .child(encodedUserEmail).child("favorites"); - RestaurantAdapter adapter = new RestaurantAdapter(list, null); - rv.setAdapter(adapter); + favRef.addValueEventListener(new ValueEventListener() { + @Override + public void onDataChange(@NonNull DataSnapshot snapshot) { + list.clear(); + for (DataSnapshot ds : snapshot.getChildren()) { + Restaurant restaurant = ds.getValue(Restaurant.class); + if (restaurant != null) { + list.add(restaurant); + } + } + adapter.notifyDataSetChanged(); + } + + @Override + public void onCancelled(@NonNull DatabaseError error) { + Toast.makeText(FavoritosActivity.this, "Erro ao carregar favoritos.", Toast.LENGTH_SHORT).show(); + } + }); + } } } 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 c59057c..405a7e5 100644 --- a/app/src/main/java/com/example/pap_teste/MainActivity.java +++ b/app/src/main/java/com/example/pap_teste/MainActivity.java @@ -67,6 +67,9 @@ public class MainActivity extends AppCompatActivity { private EditText inputEstablishmentName; private EditText inputEstablishmentEmail; private EditText inputEstablishmentPhone; + private android.widget.TextView txtForgotPassword; + private android.widget.ImageView iconPasswordVisibility; + private boolean isPasswordVisible = false; private boolean hasCreatedAccount; private FirebaseAuth firebaseAuth; private DatabaseReference databaseReference; @@ -149,6 +152,50 @@ public class MainActivity extends AppCompatActivity { inputEstablishmentName = findViewById(R.id.inputEstablishmentName); inputEstablishmentEmail = findViewById(R.id.inputEstablishmentEmail); inputEstablishmentPhone = findViewById(R.id.inputEstablishmentPhone); + txtForgotPassword = findViewById(R.id.txtForgotPassword); + iconPasswordVisibility = findViewById(R.id.iconPasswordVisibility); + + setupPasswordFeatures(); + } + + private void setupPasswordFeatures() { + iconPasswordVisibility.setOnClickListener(v -> { + isPasswordVisible = !isPasswordVisible; + if (isPasswordVisible) { + inputPassword.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); + iconPasswordVisibility.setImageResource(R.drawable.ic_visibility); + } else { + inputPassword.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD); + iconPasswordVisibility.setImageResource(R.drawable.ic_visibility_off); + } + inputPassword.setSelection(inputPassword.getText().length()); + }); + + txtForgotPassword.setOnClickListener(v -> { + String email = inputEmail.getText().toString().trim(); + if (TextUtils.isEmpty(email)) { + Toast.makeText(this, "Por favor, introduza o seu email primeiro.", Toast.LENGTH_SHORT).show(); + return; + } + + new AlertDialog.Builder(this) + .setTitle("Recuperar palavra-passe") + .setMessage("Deseja enviar um email de recuperação para " + email + "?") + .setPositiveButton("Sim", (dialog, which) -> { + if (firebaseAuth != null) { + firebaseAuth.sendPasswordResetEmail(email) + .addOnCompleteListener(task -> { + if (task.isSuccessful()) { + Toast.makeText(MainActivity.this, "Email de recuperação enviado!", Toast.LENGTH_LONG).show(); + } else { + Toast.makeText(MainActivity.this, "Falha ao enviar email. Verifique se o endereço está correto.", Toast.LENGTH_LONG).show(); + } + }); + } + }) + .setNegativeButton("Não", null) + .show(); + }); } private void setupTypeToggle() { @@ -217,6 +264,10 @@ public class MainActivity extends AppCompatActivity { inputEstablishmentName.setVisibility(creatingAccount && isEstablishment ? View.VISIBLE : View.GONE); inputEstablishmentEmail.setVisibility(creatingAccount && isEstablishment ? View.VISIBLE : View.GONE); inputEstablishmentPhone.setVisibility(creatingAccount && isEstablishment ? View.VISIBLE : View.GONE); + + if (txtForgotPassword != null) { + txtForgotPassword.setVisibility(creatingAccount ? View.GONE : View.VISIBLE); + } } private void handlePrimaryAction() { 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 0d9736a..2f3501e 100644 --- a/app/src/main/java/com/example/pap_teste/NovaReservaActivity.java +++ b/app/src/main/java/com/example/pap_teste/NovaReservaActivity.java @@ -138,52 +138,38 @@ public class NovaReservaActivity extends AppCompatActivity { private String selectedDate = null; private String selectedTime = null; - private int selectedPartySize = 0; private void setupReservationOptions() { - // Dates - RecyclerView rvDates = findViewById(R.id.rvDates); - java.util.List dates = new java.util.ArrayList<>(); - dates.add("Hoje"); - dates.add("Amanhã"); - dates.add("Quarta, 12 Mar"); - dates.add("Quinta, 13 Mar"); - dates.add("Sexta, 14 Mar"); - rvDates.setAdapter(new ReservationOptionAdapter(dates, date -> selectedDate = date)); + android.widget.Button btnDate = findViewById(R.id.btnSelectDate); + android.widget.Button btnTime = findViewById(R.id.btnSelectTime); - // Times - RecyclerView rvTimes = findViewById(R.id.rvTimes); - java.util.List times = new java.util.ArrayList<>(); - times.add("12:00"); - times.add("13:00"); - times.add("19:00"); - times.add("20:00"); - times.add("21:00"); - times.add("22:00"); - rvTimes.setAdapter(new ReservationOptionAdapter(times, time -> selectedTime = time)); + 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(); + }); - // Party Size - RecyclerView rvParty = findViewById(R.id.rvPartySize); - java.util.List party = new java.util.ArrayList<>(); - party.add("1 pessoa"); - party.add("2 pessoas"); - party.add("3 pessoas"); - party.add("4 pessoas"); - party.add("5 pessoas"); - party.add("6+ pessoas"); - rvParty.setAdapter(new ReservationOptionAdapter(party, size -> { - try { - selectedPartySize = Integer.parseInt(size.split(" ")[0].replace("+", "")); - } catch (Exception e) { - selectedPartySize = 6; - } - })); + 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() { - if (selectedDate == null || selectedTime == null || selectedPartySize == 0) { + android.widget.EditText etPartySize = findViewById(R.id.etPartySize); + int partySize = 0; + try { + partySize = Integer.parseInt(etPartySize.getText().toString()); + } catch (Exception e) {} + + 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(); return; @@ -203,7 +189,7 @@ public class NovaReservaActivity extends AppCompatActivity { selectedRestaurant.getEmail(), selectedDate, selectedTime, - selectedPartySize, + partySize, "Pendente"); if (id != null) { diff --git a/app/src/main/java/com/example/pap_teste/ProfileDashboardActivity.java b/app/src/main/java/com/example/pap_teste/ProfileDashboardActivity.java index 21e02e8..8b4a96e 100644 --- a/app/src/main/java/com/example/pap_teste/ProfileDashboardActivity.java +++ b/app/src/main/java/com/example/pap_teste/ProfileDashboardActivity.java @@ -168,10 +168,21 @@ public class ProfileDashboardActivity extends AppCompatActivity { storageRef.getDownloadUrl().addOnSuccessListener(uri -> { photoUrl = uri.toString(); Glide.with(this).load(photoUrl).circleCrop().into(imgProfile); - Toast.makeText(this, "Foto carregada!", Toast.LENGTH_SHORT).show(); + + // Salvar a nova URL da foto imediatamente na DB + Map photoUpdate = new HashMap<>(); + photoUpdate.put("photoUrl", photoUrl); + databaseReference.child(documentId).updateChildren(photoUpdate).addOnCompleteListener(dbTask -> { + if (dbTask.isSuccessful()) { + Toast.makeText(this, "Foto atualizada com sucesso!", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(this, "Foto enviada, mas não foi possível atualizar o perfil.", Toast.LENGTH_SHORT).show(); + } + }); }); }).addOnFailureListener(e -> { - Toast.makeText(this, "Falha no upload.", Toast.LENGTH_SHORT).show(); + Toast.makeText(this, "Falha no upload: " + e.getMessage(), Toast.LENGTH_LONG).show(); + android.util.Log.e("ProfileUpload", "Upload failed", e); }); } diff --git a/app/src/main/java/com/example/pap_teste/ReservaAdapter.java b/app/src/main/java/com/example/pap_teste/ReservaAdapter.java index 0c9b082..9dddf7d 100644 --- a/app/src/main/java/com/example/pap_teste/ReservaAdapter.java +++ b/app/src/main/java/com/example/pap_teste/ReservaAdapter.java @@ -44,7 +44,27 @@ public class ReservaAdapter extends RecyclerView.Adapter { @@ -25,7 +36,7 @@ public class RestaurantAdapter extends RecyclerView.Adapter { + favRef.addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(@NonNull DataSnapshot snapshot) { + if (snapshot.exists()) { + favRef.removeValue(); + } else { + favRef.setValue(restaurant); + } + } + + @Override + public void onCancelled(@NonNull DatabaseError error) { } + }); + }); + } } @Override @@ -49,10 +98,12 @@ public class RestaurantAdapter extends RecyclerView.Adapter + android:src="@drawable/circle_bg" /> - - - - + android:orientation="vertical" + android:paddingHorizontal="24dp" + android:layout_marginTop="16dp"> - - + - +