Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 824ff28001 | |||
| 86dbe2d319 | |||
| 8c1e01dc4c | |||
| 995d23ac7a | |||
| eaa3d86fc9 | |||
| d192568ed8 | |||
| 31a7cbb2df | |||
| fe9266263f |
4
.idea/deploymentTargetSelector.xml
generated
4
.idea/deploymentTargetSelector.xml
generated
@@ -4,10 +4,10 @@
|
|||||||
<selectionStates>
|
<selectionStates>
|
||||||
<SelectionState runConfigName="app">
|
<SelectionState runConfigName="app">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
<DropdownSelection timestamp="2026-04-14T16:06:26.670067Z">
|
<DropdownSelection timestamp="2026-06-25T17:21:43.291530Z">
|
||||||
<Target type="DEFAULT_BOOT">
|
<Target type="DEFAULT_BOOT">
|
||||||
<handle>
|
<handle>
|
||||||
<DeviceId pluginId="PhysicalDevice" identifier="serial=b93659d0e5dd" />
|
<DeviceId pluginId="LocalEmulator" identifier="path=/Users/230409/.android/avd/Pixel_8_Pro.avd" />
|
||||||
</handle>
|
</handle>
|
||||||
</Target>
|
</Target>
|
||||||
</DropdownSelection>
|
</DropdownSelection>
|
||||||
|
|||||||
@@ -14,6 +14,10 @@
|
|||||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
|
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||||
|
|
||||||
|
<!-- Notification permissions -->
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
@@ -113,6 +117,11 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".ReservationNotificationService"
|
||||||
|
android:exported="false"
|
||||||
|
android:foregroundServiceType="dataSync" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -37,19 +37,17 @@ public class ClientDashboardActivity extends AppCompatActivity {
|
|||||||
private TextView txtGreeting;
|
private TextView txtGreeting;
|
||||||
private ImageView imgProfile;
|
private ImageView imgProfile;
|
||||||
private EditText etSearch;
|
private EditText etSearch;
|
||||||
private ChipGroup chipGroupCategories;
|
private RecyclerView rvFeatured;
|
||||||
private RecyclerView rvFeatured, rvMainRestaurants;
|
|
||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
private View layoutFeatured, layoutAllRestaurants;
|
private View layoutFeatured;
|
||||||
|
private android.widget.LinearLayout categoriesContainer;
|
||||||
|
|
||||||
private List<Restaurant> allRestaurants = new ArrayList<>();
|
private List<Restaurant> allRestaurants = new ArrayList<>();
|
||||||
private List<Restaurant> filteredRestaurants = new ArrayList<>();
|
private List<Restaurant> filteredRestaurants = new ArrayList<>();
|
||||||
|
|
||||||
private RestaurantAdapter mainAdapter;
|
|
||||||
private FeaturedRestaurantAdapter featuredAdapter;
|
private FeaturedRestaurantAdapter featuredAdapter;
|
||||||
|
|
||||||
private final String[] CATEGORIES = { "Tudo", "Carnes", "Massas", "Sushi", "Pizzas", "Sobremesas" };
|
private final String[] CATEGORIES = { "Carnes", "Massas", "Sushi", "Pizzas", "Sobremesas" };
|
||||||
private String currentCategoryFilter = "Tudo";
|
|
||||||
private String currentSearchFilter = "";
|
private String currentSearchFilter = "";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -70,43 +68,38 @@ public class ClientDashboardActivity extends AppCompatActivity {
|
|||||||
initViews();
|
initViews();
|
||||||
setupBottomNavigation();
|
setupBottomNavigation();
|
||||||
setupSearch();
|
setupSearch();
|
||||||
setupCategories();
|
|
||||||
|
|
||||||
updateGreeting();
|
updateGreeting();
|
||||||
fetchProfilePicture();
|
fetchProfilePicture();
|
||||||
fetchRestaurants();
|
fetchRestaurants();
|
||||||
|
|
||||||
|
// Iniciar serviço de notificações se for Android O ou superior
|
||||||
|
Intent serviceIntent = new Intent(this, ReservationNotificationService.class);
|
||||||
|
startService(serviceIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RestaurantAdapter.OnRestaurantClickListener clickListener;
|
||||||
|
|
||||||
private void initViews() {
|
private void initViews() {
|
||||||
txtGreeting = findViewById(R.id.txtClientGreeting);
|
txtGreeting = findViewById(R.id.txtClientGreeting);
|
||||||
imgProfile = findViewById(R.id.imgProfile);
|
imgProfile = findViewById(R.id.imgProfile);
|
||||||
etSearch = findViewById(R.id.etSearch);
|
etSearch = findViewById(R.id.etSearch);
|
||||||
chipGroupCategories = findViewById(R.id.chipGroupCategories);
|
|
||||||
rvFeatured = findViewById(R.id.rvFeatured);
|
rvFeatured = findViewById(R.id.rvFeatured);
|
||||||
rvMainRestaurants = findViewById(R.id.rvMainRestaurants);
|
|
||||||
progressBar = findViewById(R.id.progressBar);
|
progressBar = findViewById(R.id.progressBar);
|
||||||
layoutFeatured = findViewById(R.id.layoutFeatured);
|
layoutFeatured = findViewById(R.id.layoutFeatured);
|
||||||
layoutAllRestaurants = findViewById(R.id.layoutAllRestaurants);
|
categoriesContainer = findViewById(R.id.categoriesContainer);
|
||||||
|
|
||||||
rvFeatured.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
|
rvFeatured.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
|
||||||
rvMainRestaurants.setLayoutManager(new LinearLayoutManager(this));
|
|
||||||
|
|
||||||
// Click listener for restaurants to open booking flow
|
// Click listener for restaurants to open booking flow
|
||||||
RestaurantAdapter.OnRestaurantClickListener clickListener = restaurant -> {
|
clickListener = restaurant -> {
|
||||||
Intent intent = new Intent(this, ExplorarRestaurantesActivity.class);
|
Intent intent = new Intent(this, ExplorarRestaurantesActivity.class);
|
||||||
// Reusing existing activity for details if needed, or pass data to
|
intent.putExtra("restaurant", restaurant);
|
||||||
// NovaReservaActivity
|
|
||||||
// We pass the filter so it can maybe open directly or we just pass restaurant
|
|
||||||
// email
|
|
||||||
intent.putExtra("category_filter", restaurant.getCategory());
|
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
};
|
};
|
||||||
|
|
||||||
featuredAdapter = new FeaturedRestaurantAdapter(new ArrayList<>(), clickListener);
|
featuredAdapter = new FeaturedRestaurantAdapter(new ArrayList<>(), clickListener);
|
||||||
mainAdapter = new RestaurantAdapter(filteredRestaurants, clickListener);
|
|
||||||
|
|
||||||
rvFeatured.setAdapter(featuredAdapter);
|
rvFeatured.setAdapter(featuredAdapter);
|
||||||
rvMainRestaurants.setAdapter(mainAdapter);
|
|
||||||
|
|
||||||
// Click listener for profile picture in the header
|
// Click listener for profile picture in the header
|
||||||
findViewById(R.id.cardProfile).setOnClickListener(v -> {
|
findViewById(R.id.cardProfile).setOnClickListener(v -> {
|
||||||
@@ -163,10 +156,10 @@ public class ClientDashboardActivity extends AppCompatActivity {
|
|||||||
private void fetchRestaurants() {
|
private void fetchRestaurants() {
|
||||||
progressBar.setVisibility(View.VISIBLE);
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
layoutFeatured.setVisibility(View.GONE);
|
layoutFeatured.setVisibility(View.GONE);
|
||||||
layoutAllRestaurants.setVisibility(View.GONE);
|
categoriesContainer.removeAllViews();
|
||||||
|
|
||||||
DatabaseReference usersRef = FirebaseDatabase.getInstance().getReference("Restaurantes");
|
DatabaseReference usersRef = FirebaseDatabase.getInstance().getReference("Restaurantes");
|
||||||
usersRef.addListenerForSingleValueEvent(new ValueEventListener() {
|
usersRef.addValueEventListener(new ValueEventListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onDataChange(@androidx.annotation.NonNull DataSnapshot snapshot) {
|
public void onDataChange(@androidx.annotation.NonNull DataSnapshot snapshot) {
|
||||||
progressBar.setVisibility(View.GONE);
|
progressBar.setVisibility(View.GONE);
|
||||||
@@ -176,7 +169,8 @@ public class ClientDashboardActivity extends AppCompatActivity {
|
|||||||
String role = ds.child("role").getValue(String.class);
|
String role = ds.child("role").getValue(String.class);
|
||||||
String accountType = ds.child("accountType").getValue(String.class);
|
String accountType = ds.child("accountType").getValue(String.class);
|
||||||
|
|
||||||
if ("ADMIN".equalsIgnoreCase(role) || "ESTABELECIMENTO".equalsIgnoreCase(accountType)) {
|
// Aceitar todos os registos na coleção Restaurantes
|
||||||
|
if (true) {
|
||||||
String name = ds.child("establishmentName").getValue(String.class);
|
String name = ds.child("establishmentName").getValue(String.class);
|
||||||
if (name == null)
|
if (name == null)
|
||||||
name = ds.child("displayName").getValue(String.class);
|
name = ds.child("displayName").getValue(String.class);
|
||||||
@@ -206,47 +200,6 @@ public class ClientDashboardActivity extends AppCompatActivity {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupCategories() {
|
|
||||||
for (String category : CATEGORIES) {
|
|
||||||
Chip chip = new Chip(this);
|
|
||||||
chip.setText(category);
|
|
||||||
chip.setCheckable(true);
|
|
||||||
chip.setClickable(true);
|
|
||||||
// Default styling
|
|
||||||
chip.setChipBackgroundColorResource(R.color.colorSurface);
|
|
||||||
chip.setTextColor(getResources().getColor(R.color.colorTextSecondary));
|
|
||||||
chip.setChipStrokeWidth(0f);
|
|
||||||
|
|
||||||
if (category.equals("Tudo")) {
|
|
||||||
chip.setChecked(true);
|
|
||||||
chip.setChipBackgroundColorResource(R.color.colorPrimary);
|
|
||||||
chip.setTextColor(getResources().getColor(R.color.white));
|
|
||||||
}
|
|
||||||
|
|
||||||
chip.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
|
||||||
if (isChecked) {
|
|
||||||
// Reset all other chips visual state
|
|
||||||
for (int i = 0; i < chipGroupCategories.getChildCount(); i++) {
|
|
||||||
Chip c = (Chip) chipGroupCategories.getChildAt(i);
|
|
||||||
if (c != chip) {
|
|
||||||
c.setChipBackgroundColorResource(R.color.colorSurface);
|
|
||||||
c.setTextColor(getResources().getColor(R.color.colorTextSecondary));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chip.setChipBackgroundColorResource(R.color.colorPrimary);
|
|
||||||
chip.setTextColor(getResources().getColor(R.color.white));
|
|
||||||
|
|
||||||
currentCategoryFilter = category;
|
|
||||||
applyFilters();
|
|
||||||
} else if (currentCategoryFilter.equals(category)) {
|
|
||||||
// Prevent unchecking the currently selected chip
|
|
||||||
chip.setChecked(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
chipGroupCategories.addView(chip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupSearch() {
|
private void setupSearch() {
|
||||||
etSearch.addTextChangedListener(new TextWatcher() {
|
etSearch.addTextChangedListener(new TextWatcher() {
|
||||||
@Override
|
@Override
|
||||||
@@ -270,35 +223,46 @@ public class ClientDashboardActivity extends AppCompatActivity {
|
|||||||
String normalizedSearch = normalizeString(currentSearchFilter);
|
String normalizedSearch = normalizeString(currentSearchFilter);
|
||||||
|
|
||||||
for (Restaurant r : allRestaurants) {
|
for (Restaurant r : allRestaurants) {
|
||||||
boolean matchesCategory = currentCategoryFilter.equals("Tudo")
|
|
||||||
|| currentCategoryFilter.equals(r.getCategory());
|
|
||||||
|
|
||||||
String normalizedName = normalizeString(r.getName());
|
String normalizedName = normalizeString(r.getName());
|
||||||
boolean matchesSearch = currentSearchFilter.isEmpty() || normalizedName.contains(normalizedSearch);
|
boolean matchesSearch = currentSearchFilter.isEmpty() || normalizedName.contains(normalizedSearch);
|
||||||
|
|
||||||
if (matchesCategory && matchesSearch) {
|
if (matchesSearch) {
|
||||||
filteredRestaurants.add(r);
|
filteredRestaurants.add(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mainAdapter.notifyDataSetChanged();
|
// Update featured carousel with top restaurants
|
||||||
|
|
||||||
// Update featured (just the first 3 for demo, or based on a specific logic)
|
|
||||||
List<Restaurant> featuredList = new ArrayList<>();
|
List<Restaurant> featuredList = new ArrayList<>();
|
||||||
for (int i = 0; i < Math.min(3, filteredRestaurants.size()); i++) {
|
for (int i = 0; i < Math.min(3, filteredRestaurants.size()); i++) {
|
||||||
featuredList.add(filteredRestaurants.get(i));
|
featuredList.add(filteredRestaurants.get(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
featuredAdapter = new FeaturedRestaurantAdapter(featuredList,
|
featuredAdapter = new FeaturedRestaurantAdapter(featuredList, clickListener);
|
||||||
mainAdapter instanceof RestaurantAdapter ? restaurant -> {
|
|
||||||
Intent intent = new Intent(this, ExplorarRestaurantesActivity.class);
|
|
||||||
intent.putExtra("category_filter", restaurant.getCategory());
|
|
||||||
startActivity(intent);
|
|
||||||
} : null);
|
|
||||||
rvFeatured.setAdapter(featuredAdapter);
|
rvFeatured.setAdapter(featuredAdapter);
|
||||||
|
|
||||||
layoutFeatured.setVisibility(featuredList.isEmpty() ? View.GONE : View.VISIBLE);
|
layoutFeatured.setVisibility(featuredList.isEmpty() ? View.GONE : View.VISIBLE);
|
||||||
layoutAllRestaurants.setVisibility(filteredRestaurants.isEmpty() ? View.GONE : View.VISIBLE);
|
|
||||||
|
// Update category rows
|
||||||
|
categoriesContainer.removeAllViews();
|
||||||
|
for (String category : CATEGORIES) {
|
||||||
|
List<Restaurant> catList = new ArrayList<>();
|
||||||
|
for (Restaurant r : filteredRestaurants) {
|
||||||
|
if (category.equals(r.getCategory())) {
|
||||||
|
catList.add(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!catList.isEmpty()) {
|
||||||
|
View rowView = android.view.LayoutInflater.from(this).inflate(R.layout.item_category_row, categoriesContainer, false);
|
||||||
|
TextView txtTitle = rowView.findViewById(R.id.txtCategoryTitle);
|
||||||
|
RecyclerView rvCategory = rowView.findViewById(R.id.rvCategoryRestaurants);
|
||||||
|
|
||||||
|
txtTitle.setText(category);
|
||||||
|
rvCategory.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
|
||||||
|
rvCategory.setAdapter(new FeaturedRestaurantAdapter(catList, clickListener));
|
||||||
|
|
||||||
|
categoriesContainer.addView(rowView);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String normalizeString(String str) {
|
private String normalizeString(String str) {
|
||||||
|
|||||||
@@ -31,6 +31,15 @@ import com.google.firebase.storage.StorageReference;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.activity.result.contract.ActivityResultContracts;
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.app.TimePickerDialog;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import com.example.pap_teste.models.ScheduleDay;
|
||||||
|
|
||||||
public class DefinicoesAdminActivity extends AppCompatActivity {
|
public class DefinicoesAdminActivity extends AppCompatActivity {
|
||||||
|
|
||||||
@@ -42,6 +51,9 @@ public class DefinicoesAdminActivity extends AppCompatActivity {
|
|||||||
private String photoUrl;
|
private String photoUrl;
|
||||||
private ActivityResultLauncher<Intent> imagePickerLauncher;
|
private ActivityResultLauncher<Intent> imagePickerLauncher;
|
||||||
private String[] categories = {"Carnes", "Massas", "Sushi", "Pizzas", "Sobremesas", "Italiana", "Moderna"};
|
private String[] categories = {"Carnes", "Massas", "Sushi", "Pizzas", "Sobremesas", "Italiana", "Moderna"};
|
||||||
|
private String[] dayNames = {"Domingo", "Segunda-feira", "Terça-feira", "Quarta-feira", "Quinta-feira", "Sexta-feira", "Sábado"};
|
||||||
|
private String[] dayKeys = {"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"};
|
||||||
|
private List<View> scheduleViews = new ArrayList<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -105,6 +117,29 @@ public class DefinicoesAdminActivity extends AppCompatActivity {
|
|||||||
}).show();
|
}).show();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
LinearLayout containerSchedule = findViewById(R.id.containerSchedule);
|
||||||
|
LayoutInflater inflater = LayoutInflater.from(this);
|
||||||
|
for (int i = 0; i < dayNames.length; i++) {
|
||||||
|
View view = inflater.inflate(R.layout.item_schedule_day, containerSchedule, false);
|
||||||
|
TextView tvDayName = view.findViewById(R.id.tvDayName);
|
||||||
|
TextView tvOpenTime = view.findViewById(R.id.tvOpenTime);
|
||||||
|
TextView tvCloseTime = view.findViewById(R.id.tvCloseTime);
|
||||||
|
CheckBox cbClosed = view.findViewById(R.id.cbClosed);
|
||||||
|
|
||||||
|
tvDayName.setText(dayNames[i]);
|
||||||
|
|
||||||
|
tvOpenTime.setOnClickListener(v -> showTimePicker(tvOpenTime));
|
||||||
|
tvCloseTime.setOnClickListener(v -> showTimePicker(tvCloseTime));
|
||||||
|
|
||||||
|
cbClosed.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
|
tvOpenTime.setEnabled(!isChecked);
|
||||||
|
tvCloseTime.setEnabled(!isChecked);
|
||||||
|
});
|
||||||
|
|
||||||
|
containerSchedule.addView(view);
|
||||||
|
scheduleViews.add(view);
|
||||||
|
}
|
||||||
|
|
||||||
loadCurrentSettings();
|
loadCurrentSettings();
|
||||||
|
|
||||||
btnSave.setOnClickListener(v -> saveSettings());
|
btnSave.setOnClickListener(v -> saveSettings());
|
||||||
@@ -130,6 +165,13 @@ public class DefinicoesAdminActivity extends AppCompatActivity {
|
|||||||
builder.show();
|
builder.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showTimePicker(TextView targetTextView) {
|
||||||
|
java.util.Calendar cal = java.util.Calendar.getInstance();
|
||||||
|
new TimePickerDialog(this, (view, hourOfDay, minute) -> {
|
||||||
|
targetTextView.setText(String.format(java.util.Locale.getDefault(), "%02d:%02d", hourOfDay, minute));
|
||||||
|
}, cal.get(java.util.Calendar.HOUR_OF_DAY), cal.get(java.util.Calendar.MINUTE), true).show();
|
||||||
|
}
|
||||||
|
|
||||||
private void uploadImageToFirebase(Uri imageUri) {
|
private void uploadImageToFirebase(Uri imageUri) {
|
||||||
if (documentId == null) return;
|
if (documentId == null) return;
|
||||||
|
|
||||||
@@ -179,6 +221,27 @@ public class DefinicoesAdminActivity extends AppCompatActivity {
|
|||||||
Glide.with(DefinicoesAdminActivity.this).load(photoUrl).circleCrop().into(imgLogo);
|
Glide.with(DefinicoesAdminActivity.this).load(photoUrl).circleCrop().into(imgLogo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (snapshot.hasChild("schedule")) {
|
||||||
|
for (int i = 0; i < dayKeys.length; i++) {
|
||||||
|
DataSnapshot daySnapshot = snapshot.child("schedule").child(dayKeys[i]);
|
||||||
|
if (daySnapshot.exists()) {
|
||||||
|
ScheduleDay sd = daySnapshot.getValue(ScheduleDay.class);
|
||||||
|
if (sd != null) {
|
||||||
|
View view = scheduleViews.get(i);
|
||||||
|
CheckBox cbClosed = view.findViewById(R.id.cbClosed);
|
||||||
|
TextView tvOpenTime = view.findViewById(R.id.tvOpenTime);
|
||||||
|
TextView tvCloseTime = view.findViewById(R.id.tvCloseTime);
|
||||||
|
|
||||||
|
cbClosed.setChecked(sd.isClosed());
|
||||||
|
if (sd.getOpenTime() != null) tvOpenTime.setText(sd.getOpenTime());
|
||||||
|
if (sd.getCloseTime() != null) tvCloseTime.setText(sd.getCloseTime());
|
||||||
|
|
||||||
|
tvOpenTime.setEnabled(!sd.isClosed());
|
||||||
|
tvCloseTime.setEnabled(!sd.isClosed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,6 +276,17 @@ public class DefinicoesAdminActivity extends AppCompatActivity {
|
|||||||
updates.put("logoUrl", photoUrl);
|
updates.put("logoUrl", photoUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, ScheduleDay> scheduleMap = new HashMap<>();
|
||||||
|
for (int i = 0; i < dayKeys.length; i++) {
|
||||||
|
View view = scheduleViews.get(i);
|
||||||
|
CheckBox cbClosed = view.findViewById(R.id.cbClosed);
|
||||||
|
TextView tvOpenTime = view.findViewById(R.id.tvOpenTime);
|
||||||
|
TextView tvCloseTime = view.findViewById(R.id.tvCloseTime);
|
||||||
|
|
||||||
|
scheduleMap.put(dayKeys[i], new ScheduleDay(cbClosed.isChecked(), tvOpenTime.getText().toString(), tvCloseTime.getText().toString()));
|
||||||
|
}
|
||||||
|
updates.put("schedule", scheduleMap);
|
||||||
|
|
||||||
databaseReference.child(documentId).updateChildren(updates)
|
databaseReference.child(documentId).updateChildren(updates)
|
||||||
.addOnSuccessListener(aVoid -> {
|
.addOnSuccessListener(aVoid -> {
|
||||||
Toast.makeText(this, "Definições guardadas com sucesso!", Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, "Definições guardadas com sucesso!", Toast.LENGTH_SHORT).show();
|
||||||
|
|||||||
@@ -118,9 +118,9 @@ public class DetalhesReservasActivity extends AppCompatActivity {
|
|||||||
btnConfirmar.setOnClickListener(v -> {
|
btnConfirmar.setOnClickListener(v -> {
|
||||||
com.example.pap_teste.models.Reserva item = reservas.get(selectedIndex);
|
com.example.pap_teste.models.Reserva item = reservas.get(selectedIndex);
|
||||||
if ("Pendente".equals(item.getEstado())) {
|
if ("Pendente".equals(item.getEstado())) {
|
||||||
atualizarEstadoSelecionado("Confirmada");
|
mostrarMesasDisponiveis();
|
||||||
} else if ("Confirmada".equals(item.getEstado())) {
|
} else if ("Confirmada".equals(item.getEstado()) || item.getEstado().startsWith("Confirmada (Mesa")) {
|
||||||
atualizarEstadoSelecionado("Concluída");
|
showConcluirDialog();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -134,6 +134,97 @@ public class DetalhesReservasActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void mostrarMesasDisponiveis() {
|
||||||
|
if (selectedIndex < 0 || selectedIndex >= reservas.size()) {
|
||||||
|
Toast.makeText(this, "Selecione uma reserva.", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
com.example.pap_teste.models.Reserva item = reservas.get(selectedIndex);
|
||||||
|
|
||||||
|
com.google.firebase.database.DatabaseReference mesasRef = com.google.firebase.database.FirebaseDatabase.getInstance().getReference("Mesas");
|
||||||
|
mesasRef.orderByChild("restauranteEmail").equalTo(restaurantEmail).addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||||
|
List<com.example.pap_teste.models.Mesa> mesasLivres = new ArrayList<>();
|
||||||
|
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.getEstado() != null && m.getEstado().equalsIgnoreCase("Livre") && m.getCapacidade() >= item.getPessoas()) {
|
||||||
|
m.setId(ds.getKey());
|
||||||
|
mesasLivres.add(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mesasLivres.isEmpty()) {
|
||||||
|
new androidx.appcompat.app.AlertDialog.Builder(DetalhesReservasActivity.this)
|
||||||
|
.setTitle("Sem mesas disponíveis")
|
||||||
|
.setMessage("Não há mesas livres com capacidade suficiente para " + item.getPessoas() + " pessoas. Deseja confirmar a reserva mesmo assim (sem mesa atribuída)?")
|
||||||
|
.setPositiveButton("Sim", (dialog, which) -> atualizarEstadoSelecionado("Confirmada", null))
|
||||||
|
.setNegativeButton("Não", null)
|
||||||
|
.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] mesaOptions = new String[mesasLivres.size()];
|
||||||
|
for (int i = 0; i < mesasLivres.size(); i++) {
|
||||||
|
com.example.pap_teste.models.Mesa m = mesasLivres.get(i);
|
||||||
|
mesaOptions[i] = String.format("Mesa %d (%d lugares)", m.getNumero(), m.getCapacidade());
|
||||||
|
}
|
||||||
|
|
||||||
|
new androidx.appcompat.app.AlertDialog.Builder(DetalhesReservasActivity.this)
|
||||||
|
.setTitle("Atribuir Mesa")
|
||||||
|
.setItems(mesaOptions, (dialog, which) -> {
|
||||||
|
com.example.pap_teste.models.Mesa selecionada = mesasLivres.get(which);
|
||||||
|
confirmarReservaComMesa(item, selecionada);
|
||||||
|
})
|
||||||
|
.setNegativeButton("Cancelar", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||||
|
Toast.makeText(DetalhesReservasActivity.this, "Erro ao carregar mesas.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void confirmarReservaComMesa(com.example.pap_teste.models.Reserva reserva, com.example.pap_teste.models.Mesa mesa) {
|
||||||
|
String novoEstado = "Confirmada (Mesa " + mesa.getNumero() + ")";
|
||||||
|
|
||||||
|
java.util.Map<String, Object> updates = new java.util.HashMap<>();
|
||||||
|
updates.put("estado", novoEstado);
|
||||||
|
updates.put("motivo", null); // limpar possível motivo antigo
|
||||||
|
|
||||||
|
databaseReference.child(reserva.getId()).updateChildren(updates).addOnCompleteListener(task -> {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("Mesas").child(mesa.getId()).child("estado").setValue("Reservada")
|
||||||
|
.addOnCompleteListener(t2 -> {
|
||||||
|
Toast.makeText(this, "Reserva aceite e mesa atribuída com sucesso.", Toast.LENGTH_SHORT).show();
|
||||||
|
reserva.setEstado(novoEstado);
|
||||||
|
reserva.setMotivo(null);
|
||||||
|
mostrarDetalhe(reserva);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, "Erro ao confirmar reserva.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showConcluirDialog() {
|
||||||
|
if (selectedIndex < 0) return;
|
||||||
|
android.widget.EditText input = new android.widget.EditText(this);
|
||||||
|
input.setHint("Notas de conclusão (opcional)");
|
||||||
|
new androidx.appcompat.app.AlertDialog.Builder(this)
|
||||||
|
.setTitle("Concluir Reserva")
|
||||||
|
.setMessage("Deseja adicionar alguma nota ao concluir esta reserva?")
|
||||||
|
.setView(input)
|
||||||
|
.setPositiveButton("Concluir", (dialog, which) -> {
|
||||||
|
String motivo = input.getText().toString();
|
||||||
|
atualizarEstadoSelecionado("Concluída", motivo.isEmpty() ? null : motivo);
|
||||||
|
})
|
||||||
|
.setNegativeButton("Cancelar", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
private void showRecusarDialog() {
|
private void showRecusarDialog() {
|
||||||
if (selectedIndex < 0)
|
if (selectedIndex < 0)
|
||||||
return;
|
return;
|
||||||
@@ -142,7 +233,7 @@ public class DetalhesReservasActivity extends AppCompatActivity {
|
|||||||
new androidx.appcompat.app.AlertDialog.Builder(this)
|
new androidx.appcompat.app.AlertDialog.Builder(this)
|
||||||
.setTitle("Motivo da Recusa")
|
.setTitle("Motivo da Recusa")
|
||||||
.setItems(motivos, (dialog, which) -> {
|
.setItems(motivos, (dialog, which) -> {
|
||||||
atualizarEstadoSelecionado("Recusada (" + motivos[which] + ")");
|
atualizarEstadoSelecionado("Recusada", motivos[which]);
|
||||||
})
|
})
|
||||||
.setNegativeButton("Voltar", null)
|
.setNegativeButton("Voltar", null)
|
||||||
.show();
|
.show();
|
||||||
@@ -186,17 +277,27 @@ public class DetalhesReservasActivity extends AppCompatActivity {
|
|||||||
toggleButtons(null);
|
toggleButtons(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void atualizarEstadoSelecionado(String novoEstado) {
|
private void atualizarEstadoSelecionado(String novoEstado, String motivo) {
|
||||||
if (selectedIndex < 0 || selectedIndex >= reservas.size()) {
|
if (selectedIndex < 0 || selectedIndex >= reservas.size()) {
|
||||||
Toast.makeText(this, "Selecione uma reserva para atualizar.", Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, "Selecione uma reserva para atualizar.", Toast.LENGTH_SHORT).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
com.example.pap_teste.models.Reserva item = reservas.get(selectedIndex);
|
com.example.pap_teste.models.Reserva item = reservas.get(selectedIndex);
|
||||||
databaseReference.child(item.getId()).child("estado").setValue(novoEstado).addOnCompleteListener(task -> {
|
java.util.Map<String, Object> updates = new java.util.HashMap<>();
|
||||||
|
updates.put("estado", novoEstado);
|
||||||
|
if (motivo != null && !motivo.isEmpty()) {
|
||||||
|
updates.put("motivo", motivo);
|
||||||
|
} else {
|
||||||
|
updates.put("motivo", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
databaseReference.child(item.getId()).updateChildren(updates).addOnCompleteListener(task -> {
|
||||||
if (task.isSuccessful()) {
|
if (task.isSuccessful()) {
|
||||||
txtMensagem
|
txtMensagem
|
||||||
.setText(String.format("Reserva de %s marcada como %s.", item.getClienteEmail(), novoEstado));
|
.setText(String.format("Reserva de %s marcada como %s.", item.getClienteEmail(), novoEstado));
|
||||||
|
item.setEstado(novoEstado);
|
||||||
|
item.setMotivo(motivo);
|
||||||
mostrarDetalhe(item);
|
mostrarDetalhe(item);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -206,7 +307,13 @@ public class DetalhesReservasActivity extends AppCompatActivity {
|
|||||||
txtInfo.setText(String.format("%s • %s • %s • %dp", item.getClienteEmail(), item.getRestauranteName(),
|
txtInfo.setText(String.format("%s • %s • %s • %dp", item.getClienteEmail(), item.getRestauranteName(),
|
||||||
item.getHora(), item.getPessoas()));
|
item.getHora(), item.getPessoas()));
|
||||||
txtNotas.setText("Data: " + item.getData());
|
txtNotas.setText("Data: " + item.getData());
|
||||||
txtEstado.setText(String.format("Estado: %s", item.getEstado()));
|
|
||||||
|
String estadoTexto = item.getEstado();
|
||||||
|
if (item.getMotivo() != null && !item.getMotivo().isEmpty()) {
|
||||||
|
estadoTexto += " (" + item.getMotivo() + ")";
|
||||||
|
}
|
||||||
|
txtEstado.setText(String.format("Estado: %s", estadoTexto));
|
||||||
|
|
||||||
toggleButtons(item);
|
toggleButtons(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,21 +332,23 @@ public class DetalhesReservasActivity extends AppCompatActivity {
|
|||||||
btnRecusar.setVisibility(android.view.View.VISIBLE);
|
btnRecusar.setVisibility(android.view.View.VISIBLE);
|
||||||
btnApagar.setVisibility(android.view.View.GONE);
|
btnApagar.setVisibility(android.view.View.GONE);
|
||||||
break;
|
break;
|
||||||
case "Confirmada":
|
|
||||||
btnConfirmar.setText("Concluir");
|
|
||||||
btnConfirmar.setVisibility(android.view.View.VISIBLE);
|
|
||||||
btnRecusar.setVisibility(android.view.View.VISIBLE); // Still allow refusal
|
|
||||||
btnApagar.setVisibility(android.view.View.GONE);
|
|
||||||
break;
|
|
||||||
case "Concluída":
|
case "Concluída":
|
||||||
btnConfirmar.setVisibility(android.view.View.GONE);
|
btnConfirmar.setVisibility(android.view.View.GONE);
|
||||||
btnRecusar.setVisibility(android.view.View.GONE);
|
btnRecusar.setVisibility(android.view.View.GONE);
|
||||||
btnApagar.setVisibility(android.view.View.VISIBLE);
|
btnApagar.setVisibility(android.view.View.VISIBLE);
|
||||||
break;
|
break;
|
||||||
default: // Recusada or Cancelada
|
default:
|
||||||
btnConfirmar.setVisibility(android.view.View.GONE);
|
if (item.getEstado() != null && item.getEstado().startsWith("Confirmada")) {
|
||||||
btnRecusar.setVisibility(android.view.View.GONE);
|
btnConfirmar.setText("Concluir");
|
||||||
btnApagar.setVisibility(android.view.View.VISIBLE); // Allow deleting refused ones as well
|
btnConfirmar.setVisibility(android.view.View.VISIBLE);
|
||||||
|
btnRecusar.setVisibility(android.view.View.VISIBLE); // Still allow refusal
|
||||||
|
btnApagar.setVisibility(android.view.View.GONE);
|
||||||
|
} else {
|
||||||
|
// Recusada or Cancelada
|
||||||
|
btnConfirmar.setVisibility(android.view.View.GONE);
|
||||||
|
btnRecusar.setVisibility(android.view.View.GONE);
|
||||||
|
btnApagar.setVisibility(android.view.View.VISIBLE); // Allow deleting refused ones as well
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ public class EstablishmentDashboardActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
loadProximasReservas();
|
loadProximasReservas();
|
||||||
loadEstatisticas();
|
loadEstatisticas();
|
||||||
|
checkPendingReservationsOnLogin();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadProximasReservas() {
|
private void loadProximasReservas() {
|
||||||
@@ -247,4 +248,70 @@ public class EstablishmentDashboardActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkPendingReservationsOnLogin() {
|
||||||
|
String email = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL);
|
||||||
|
if (email == null) {
|
||||||
|
if (com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser() != null) {
|
||||||
|
email = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser().getEmail();
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final String finalEmail = email != null ? email.trim() : "";
|
||||||
|
|
||||||
|
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("reservas")
|
||||||
|
.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||||
|
java.util.List<com.example.pap_teste.models.Reserva> pendingReservations = new java.util.ArrayList<>();
|
||||||
|
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(finalEmail)) {
|
||||||
|
if ("Pendente".equals(r.getEstado())) {
|
||||||
|
pendingReservations.add(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pendingReservations.isEmpty()) {
|
||||||
|
showPendingReservationsDialog(pendingReservations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showPendingReservationsDialog(java.util.List<com.example.pap_teste.models.Reserva> pendingReservations) {
|
||||||
|
if (isFinishing() || isDestroyed()) return;
|
||||||
|
android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(this);
|
||||||
|
builder.setTitle("Reservas Pendentes (" + pendingReservations.size() + ")");
|
||||||
|
|
||||||
|
StringBuilder message = new StringBuilder("Tem reservas a aguardar confirmação:\n\n");
|
||||||
|
for (com.example.pap_teste.models.Reserva r : pendingReservations) {
|
||||||
|
message.append("• ").append(r.getData()).append(" às ").append(r.getHora())
|
||||||
|
.append(" - ").append(r.getPessoas()).append(" pessoas")
|
||||||
|
.append("\n Cliente: ").append(r.getClienteEmail()).append("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.setMessage(message.toString());
|
||||||
|
builder.setPositiveButton("Ver Reservas", (dialog, which) -> {
|
||||||
|
Intent intent = new Intent(EstablishmentDashboardActivity.this, DetalhesReservasActivity.class);
|
||||||
|
intent.putExtra(MainActivity.EXTRA_EMAIL, getIntent().getStringExtra(MainActivity.EXTRA_EMAIL));
|
||||||
|
startActivity(intent);
|
||||||
|
});
|
||||||
|
builder.setNegativeButton("Fechar", (dialog, which) -> dialog.dismiss());
|
||||||
|
|
||||||
|
android.app.AlertDialog dialog = builder.create();
|
||||||
|
dialog.show();
|
||||||
|
|
||||||
|
android.widget.TextView textView = dialog.findViewById(android.R.id.message);
|
||||||
|
if (textView != null) {
|
||||||
|
textView.setTextSize(18);
|
||||||
|
textView.setGravity(android.view.Gravity.CENTER);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,11 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
|||||||
back.setOnClickListener(v -> handleBackNavigation());
|
back.setOnClickListener(v -> handleBackNavigation());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getIntent().hasExtra("restaurant")) {
|
||||||
|
selectedRestaurant = (com.example.pap_teste.models.Restaurant) getIntent().getSerializableExtra("restaurant");
|
||||||
|
currentState = State.DETAILS;
|
||||||
|
}
|
||||||
|
|
||||||
setupRestaurantList();
|
setupRestaurantList();
|
||||||
updateViewState();
|
updateViewState();
|
||||||
}
|
}
|
||||||
@@ -135,7 +140,7 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
|||||||
if (progressBar != null)
|
if (progressBar != null)
|
||||||
progressBar.setVisibility(android.view.View.VISIBLE);
|
progressBar.setVisibility(android.view.View.VISIBLE);
|
||||||
|
|
||||||
query.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
query.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||||
if (progressBar != null)
|
if (progressBar != null)
|
||||||
@@ -145,7 +150,8 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
|||||||
String role = ds.child("role").getValue(String.class);
|
String role = ds.child("role").getValue(String.class);
|
||||||
String accountType = ds.child("accountType").getValue(String.class);
|
String accountType = ds.child("accountType").getValue(String.class);
|
||||||
|
|
||||||
if ("ADMIN".equalsIgnoreCase(role) || "ESTABELECIMENTO".equalsIgnoreCase(accountType)) {
|
// Aceitar todos os registos na coleção Restaurantes
|
||||||
|
if (true) {
|
||||||
String name = ds.child("establishmentName").getValue(String.class);
|
String name = ds.child("establishmentName").getValue(String.class);
|
||||||
if (name == null)
|
if (name == null)
|
||||||
name = ds.child("displayName").getValue(String.class);
|
name = ds.child("displayName").getValue(String.class);
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ public class FavoritosActivity extends AppCompatActivity {
|
|||||||
list = new ArrayList<>();
|
list = new ArrayList<>();
|
||||||
adapter = new RestaurantAdapter(list, restaurant -> {
|
adapter = new RestaurantAdapter(list, restaurant -> {
|
||||||
android.content.Intent intent = new android.content.Intent(this, ExplorarRestaurantesActivity.class);
|
android.content.Intent intent = new android.content.Intent(this, ExplorarRestaurantesActivity.class);
|
||||||
intent.putExtra("category_filter", restaurant.getCategory()); // just as demo
|
intent.putExtra("restaurant", restaurant);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
});
|
});
|
||||||
rv.setAdapter(adapter);
|
rv.setAdapter(adapter);
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ public class ListaEsperaActivity extends AppCompatActivity {
|
|||||||
List<com.example.pap_teste.models.Mesa> mesasLivres = new ArrayList<>();
|
List<com.example.pap_teste.models.Mesa> mesasLivres = new ArrayList<>();
|
||||||
for (DataSnapshot ds : snapshot.getChildren()) {
|
for (DataSnapshot ds : snapshot.getChildren()) {
|
||||||
com.example.pap_teste.models.Mesa m = ds.getValue(com.example.pap_teste.models.Mesa.class);
|
com.example.pap_teste.models.Mesa m = ds.getValue(com.example.pap_teste.models.Mesa.class);
|
||||||
if (m != null && m.getEstado() != null && m.getEstado().equalsIgnoreCase("Livre")) {
|
if (m != null && m.getEstado() != null && m.getEstado().equalsIgnoreCase("Livre") && m.getCapacidade() >= item.getPessoas()) {
|
||||||
m.setId(ds.getKey());
|
m.setId(ds.getKey());
|
||||||
mesasLivres.add(m);
|
mesasLivres.add(m);
|
||||||
}
|
}
|
||||||
@@ -114,7 +114,7 @@ public class ListaEsperaActivity extends AppCompatActivity {
|
|||||||
if (mesasLivres.isEmpty()) {
|
if (mesasLivres.isEmpty()) {
|
||||||
new androidx.appcompat.app.AlertDialog.Builder(ListaEsperaActivity.this)
|
new androidx.appcompat.app.AlertDialog.Builder(ListaEsperaActivity.this)
|
||||||
.setTitle("Sem mesas disponíveis")
|
.setTitle("Sem mesas disponíveis")
|
||||||
.setMessage("Não há mesas livres registadas. Deseja confirmar a reserva mesmo assim (sem lugar reservado)?")
|
.setMessage("Não há mesas livres com capacidade suficiente para " + item.getPessoas() + " pessoas. Deseja confirmar a reserva mesmo assim (sem lugar reservado)?")
|
||||||
.setPositiveButton("Sim", (dialog, which) -> atualizarEstadoSelecionado("Confirmada"))
|
.setPositiveButton("Sim", (dialog, which) -> atualizarEstadoSelecionado("Confirmada"))
|
||||||
.setNegativeButton("Não", null)
|
.setNegativeButton("Não", null)
|
||||||
.show();
|
.show();
|
||||||
|
|||||||
@@ -54,9 +54,6 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
private AccountType selectedAccountType = AccountType.CLIENTE;
|
private AccountType selectedAccountType = AccountType.CLIENTE;
|
||||||
private AccountAction selectedAccountAction = AccountAction.ENTRAR;
|
private AccountAction selectedAccountAction = AccountAction.ENTRAR;
|
||||||
|
|
||||||
private Button btnCliente;
|
|
||||||
private Button btnEstabelecimento;
|
|
||||||
private Button btnEntrar;
|
private Button btnEntrar;
|
||||||
private Button btnCriarConta;
|
private Button btnCriarConta;
|
||||||
private Button btnPrimaryAction;
|
private Button btnPrimaryAction;
|
||||||
@@ -101,12 +98,13 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
return insets;
|
return insets;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
selectedAccountType = AccountType.CLIENTE;
|
||||||
|
|
||||||
FirebaseApp.initializeApp(this);
|
FirebaseApp.initializeApp(this);
|
||||||
firebaseAuth = FirebaseAuth.getInstance();
|
firebaseAuth = FirebaseAuth.getInstance();
|
||||||
databaseReference = FirebaseDatabase.getInstance().getReference();
|
databaseReference = FirebaseDatabase.getInstance().getReference();
|
||||||
|
|
||||||
bindViews();
|
bindViews();
|
||||||
setupTypeToggle();
|
|
||||||
setupActionToggle();
|
setupActionToggle();
|
||||||
setupPrimaryAction();
|
setupPrimaryAction();
|
||||||
checkPermissions();
|
checkPermissions();
|
||||||
@@ -162,12 +160,19 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
permissionsNeeded.add(Manifest.permission.READ_EXTERNAL_STORAGE);
|
permissionsNeeded.add(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
if (ContextCompat.checkSelfPermission(this,
|
||||||
|
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
permissionsNeeded.add(Manifest.permission.POST_NOTIFICATIONS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!permissionsNeeded.isEmpty()) {
|
if (!permissionsNeeded.isEmpty()) {
|
||||||
new AlertDialog.Builder(this)
|
new AlertDialog.Builder(this)
|
||||||
.setTitle("Permissões Necessárias")
|
.setTitle("Permissões Necessárias")
|
||||||
.setMessage(
|
.setMessage(
|
||||||
"Para o correto funcionamento do check-in, serviços de proximidade e fotos da galeria, precisamos de algumas permissões.")
|
"Para o correto funcionamento do check-in, receber notificações de reservas e acesso a fotos, precisamos de algumas permissões.")
|
||||||
.setPositiveButton("Configurar", (dialog, which) -> {
|
.setPositiveButton("Configurar", (dialog, which) -> {
|
||||||
permissionRequest.launch(permissionsNeeded.toArray(new String[0]));
|
permissionRequest.launch(permissionsNeeded.toArray(new String[0]));
|
||||||
})
|
})
|
||||||
@@ -177,8 +182,6 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void bindViews() {
|
private void bindViews() {
|
||||||
btnCliente = findViewById(R.id.btnCliente);
|
|
||||||
btnEstabelecimento = findViewById(R.id.btnEstabelecimento);
|
|
||||||
btnEntrar = findViewById(R.id.btnEntrar);
|
btnEntrar = findViewById(R.id.btnEntrar);
|
||||||
btnCriarConta = findViewById(R.id.btnCriarConta);
|
btnCriarConta = findViewById(R.id.btnCriarConta);
|
||||||
btnPrimaryAction = findViewById(R.id.btnFinalCriarConta);
|
btnPrimaryAction = findViewById(R.id.btnFinalCriarConta);
|
||||||
@@ -240,17 +243,6 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupTypeToggle() {
|
|
||||||
btnCliente.setOnClickListener(v -> {
|
|
||||||
selectedAccountType = AccountType.CLIENTE;
|
|
||||||
updateTypeButtons();
|
|
||||||
});
|
|
||||||
btnEstabelecimento.setOnClickListener(v -> {
|
|
||||||
selectedAccountType = AccountType.ESTABELECIMENTO;
|
|
||||||
updateTypeButtons();
|
|
||||||
});
|
|
||||||
updateTypeButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupActionToggle() {
|
private void setupActionToggle() {
|
||||||
btnEntrar.setOnClickListener(v -> {
|
btnEntrar.setOnClickListener(v -> {
|
||||||
@@ -269,11 +261,6 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
updatePrimaryActionState();
|
updatePrimaryActionState();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateTypeButtons() {
|
|
||||||
setSelectedState(btnCliente, selectedAccountType == AccountType.CLIENTE);
|
|
||||||
setSelectedState(btnEstabelecimento, selectedAccountType == AccountType.ESTABELECIMENTO);
|
|
||||||
updateInputVisibility();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateActionButtons() {
|
private void updateActionButtons() {
|
||||||
setSelectedState(btnEntrar, selectedAccountAction == AccountAction.ENTRAR);
|
setSelectedState(btnEntrar, selectedAccountAction == AccountAction.ENTRAR);
|
||||||
|
|||||||
@@ -120,13 +120,29 @@ public class MinhasReservasActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void cancelReservation(Reserva reserva) {
|
private void cancelReservation(Reserva reserva) {
|
||||||
databaseReference.child(reserva.getId()).child("estado").setValue("Cancelada")
|
android.widget.EditText input = new android.widget.EditText(this);
|
||||||
.addOnCompleteListener(task -> {
|
input.setHint("Motivo do cancelamento (opcional)");
|
||||||
if (task.isSuccessful()) {
|
new androidx.appcompat.app.AlertDialog.Builder(this)
|
||||||
Toast.makeText(this, "Reserva cancelada.", Toast.LENGTH_SHORT).show();
|
.setTitle("Cancelar Reserva")
|
||||||
} else {
|
.setMessage("Deseja indicar o motivo do cancelamento?")
|
||||||
Toast.makeText(this, "Erro ao cancelar reserva.", Toast.LENGTH_SHORT).show();
|
.setView(input)
|
||||||
|
.setPositiveButton("Confirmar", (dialog, which) -> {
|
||||||
|
String motivo = input.getText().toString();
|
||||||
|
java.util.Map<String, Object> updates = new java.util.HashMap<>();
|
||||||
|
updates.put("estado", "Cancelada");
|
||||||
|
if (!motivo.isEmpty()) {
|
||||||
|
updates.put("motivo", motivo);
|
||||||
}
|
}
|
||||||
});
|
databaseReference.child(reserva.getId()).updateChildren(updates)
|
||||||
|
.addOnCompleteListener(task -> {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
Toast.makeText(this, "Reserva cancelada.", Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, "Erro ao cancelar reserva.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.setNegativeButton("Voltar", null)
|
||||||
|
.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,18 @@ public class NovaReservaActivity extends AppCompatActivity {
|
|||||||
} else {
|
} else {
|
||||||
txtTitle.setText("Reserva: " + (selectedRestaurant != null ? selectedRestaurant.getName() : ""));
|
txtTitle.setText("Reserva: " + (selectedRestaurant != null ? selectedRestaurant.getName() : ""));
|
||||||
setupReservationOptions();
|
setupReservationOptions();
|
||||||
|
|
||||||
|
android.widget.TextView txtFechado = findViewById(R.id.txtRestauranteFechado);
|
||||||
|
boolean isFechado = selectedRestaurant != null && selectedRestaurant.isFechadoWebsite();
|
||||||
|
|
||||||
|
if (txtFechado != null) {
|
||||||
|
txtFechado.setVisibility(isFechado ? android.view.View.VISIBLE : android.view.View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
findViewById(R.id.btnSelectDate).setEnabled(!isFechado);
|
||||||
|
findViewById(R.id.btnSelectTime).setEnabled(!isFechado);
|
||||||
|
findViewById(R.id.etPartySize).setEnabled(!isFechado);
|
||||||
|
findViewById(R.id.btnConfirmarReserva).setEnabled(!isFechado);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,17 +131,31 @@ public class NovaReservaActivity extends AppCompatActivity {
|
|||||||
String role = ds.child("role").getValue(String.class);
|
String role = ds.child("role").getValue(String.class);
|
||||||
String accountType = ds.child("accountType").getValue(String.class);
|
String accountType = ds.child("accountType").getValue(String.class);
|
||||||
|
|
||||||
if ("ADMIN".equalsIgnoreCase(role) || "ESTABELECIMENTO".equalsIgnoreCase(accountType)) {
|
// Aceitar todos os registos na coleção Restaurantes
|
||||||
|
if (true) {
|
||||||
String name = ds.child("establishmentName").getValue(String.class);
|
String name = ds.child("establishmentName").getValue(String.class);
|
||||||
if (name == null)
|
if (name == null)
|
||||||
name = ds.child("displayName").getValue(String.class);
|
name = ds.child("displayName").getValue(String.class);
|
||||||
String email = ds.child("email").getValue(String.class);
|
String email = ds.child("email").getValue(String.class);
|
||||||
String cat = ds.child("category").getValue(String.class);
|
String cat = ds.child("category").getValue(String.class);
|
||||||
String logoUrl = ds.child("logoUrl").getValue(String.class);
|
String logoUrl = ds.child("logoUrl").getValue(String.class);
|
||||||
|
Boolean fechadoWebsite = ds.child("fechadoWebsite").getValue(Boolean.class);
|
||||||
|
|
||||||
if (name != null && email != null) {
|
if (name != null && email != null) {
|
||||||
filteredList.add(new com.example.pap_teste.models.Restaurant(name, cat, email,
|
com.example.pap_teste.models.Restaurant rest = new com.example.pap_teste.models.Restaurant(name, cat, email,
|
||||||
false, logoUrl));
|
false, logoUrl);
|
||||||
|
if (fechadoWebsite != null) {
|
||||||
|
rest.setFechadoWebsite(fechadoWebsite);
|
||||||
|
}
|
||||||
|
if (ds.hasChild("schedule")) {
|
||||||
|
java.util.Map<String, com.example.pap_teste.models.ScheduleDay> schMap = new java.util.HashMap<>();
|
||||||
|
for (com.google.firebase.database.DataSnapshot schDs : ds.child("schedule").getChildren()) {
|
||||||
|
com.example.pap_teste.models.ScheduleDay sd = schDs.getValue(com.example.pap_teste.models.ScheduleDay.class);
|
||||||
|
schMap.put(schDs.getKey(), sd);
|
||||||
|
}
|
||||||
|
rest.setSchedule(schMap);
|
||||||
|
}
|
||||||
|
filteredList.add(rest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,17 +190,70 @@ public class NovaReservaActivity extends AppCompatActivity {
|
|||||||
btnDate.setOnClickListener(v -> {
|
btnDate.setOnClickListener(v -> {
|
||||||
java.util.Calendar cal = java.util.Calendar.getInstance();
|
java.util.Calendar cal = java.util.Calendar.getInstance();
|
||||||
new android.app.DatePickerDialog(this, (view, year, month, dayOfMonth) -> {
|
new android.app.DatePickerDialog(this, (view, year, month, dayOfMonth) -> {
|
||||||
selectedDate = dayOfMonth + "/" + (month + 1) + "/" + year;
|
java.util.Calendar selectedCal = java.util.Calendar.getInstance();
|
||||||
btnDate.setText(selectedDate);
|
selectedCal.set(year, month, dayOfMonth);
|
||||||
|
int dayOfWeek = selectedCal.get(java.util.Calendar.DAY_OF_WEEK);
|
||||||
|
String[] dayKeys = {"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"};
|
||||||
|
String dayKey = dayKeys[dayOfWeek - 1];
|
||||||
|
|
||||||
|
boolean isClosed = false;
|
||||||
|
if (selectedRestaurant.getSchedule() != null && selectedRestaurant.getSchedule().containsKey(dayKey)) {
|
||||||
|
com.example.pap_teste.models.ScheduleDay sd = selectedRestaurant.getSchedule().get(dayKey);
|
||||||
|
if (sd != null && sd.isClosed()) isClosed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isClosed) {
|
||||||
|
android.widget.Toast.makeText(this, "O restaurante encontra-se encerrado na data selecionada.", android.widget.Toast.LENGTH_LONG).show();
|
||||||
|
selectedDate = null;
|
||||||
|
btnDate.setText("Selecionar Data");
|
||||||
|
btnTime.setEnabled(false);
|
||||||
|
} else {
|
||||||
|
selectedDate = dayOfMonth + "/" + (month + 1) + "/" + year;
|
||||||
|
btnDate.setText(selectedDate);
|
||||||
|
btnTime.setEnabled(true);
|
||||||
|
}
|
||||||
}, cal.get(java.util.Calendar.YEAR), cal.get(java.util.Calendar.MONTH),
|
}, cal.get(java.util.Calendar.YEAR), cal.get(java.util.Calendar.MONTH),
|
||||||
cal.get(java.util.Calendar.DAY_OF_MONTH)).show();
|
cal.get(java.util.Calendar.DAY_OF_MONTH)).show();
|
||||||
});
|
});
|
||||||
|
|
||||||
btnTime.setOnClickListener(v -> {
|
btnTime.setOnClickListener(v -> {
|
||||||
|
if (selectedDate == null) {
|
||||||
|
android.widget.Toast.makeText(this, "Por favor, selecione primeiro a data.", android.widget.Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
java.util.Calendar cal = java.util.Calendar.getInstance();
|
java.util.Calendar cal = java.util.Calendar.getInstance();
|
||||||
new android.app.TimePickerDialog(this, (view, hourOfDay, minute) -> {
|
new android.app.TimePickerDialog(this, (view, hourOfDay, minute) -> {
|
||||||
selectedTime = String.format(java.util.Locale.getDefault(), "%02d:%02d", hourOfDay, minute);
|
String chosenTime = String.format(java.util.Locale.getDefault(), "%02d:%02d", hourOfDay, minute);
|
||||||
btnTime.setText(selectedTime);
|
|
||||||
|
boolean isOutOfHours = false;
|
||||||
|
if (selectedDate != null && selectedRestaurant.getSchedule() != null) {
|
||||||
|
try {
|
||||||
|
java.util.Date d = new java.text.SimpleDateFormat("d/M/yyyy", java.util.Locale.getDefault()).parse(selectedDate);
|
||||||
|
java.util.Calendar selectedCal = java.util.Calendar.getInstance();
|
||||||
|
if (d != null) selectedCal.setTime(d);
|
||||||
|
int dayOfWeek = selectedCal.get(java.util.Calendar.DAY_OF_WEEK);
|
||||||
|
String[] dayKeys = {"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"};
|
||||||
|
String dayKey = dayKeys[dayOfWeek - 1];
|
||||||
|
|
||||||
|
com.example.pap_teste.models.ScheduleDay sd = selectedRestaurant.getSchedule().get(dayKey);
|
||||||
|
if (sd != null && !sd.isClosed()) {
|
||||||
|
if (sd.getOpenTime() != null && sd.getCloseTime() != null && !sd.getOpenTime().isEmpty() && !sd.getCloseTime().isEmpty()) {
|
||||||
|
if (chosenTime.compareTo(sd.getOpenTime()) < 0 || chosenTime.compareTo(sd.getCloseTime()) > 0) {
|
||||||
|
isOutOfHours = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOutOfHours) {
|
||||||
|
android.widget.Toast.makeText(this, "Por favor selecione uma hora dentro do horário de funcionamento.", android.widget.Toast.LENGTH_LONG).show();
|
||||||
|
selectedTime = null;
|
||||||
|
btnTime.setText("Selecionar Hora");
|
||||||
|
} else {
|
||||||
|
selectedTime = chosenTime;
|
||||||
|
btnTime.setText(selectedTime);
|
||||||
|
}
|
||||||
}, cal.get(java.util.Calendar.HOUR_OF_DAY), cal.get(java.util.Calendar.MINUTE), true).show();
|
}, cal.get(java.util.Calendar.HOUR_OF_DAY), cal.get(java.util.Calendar.MINUTE), true).show();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ public class ProfileDashboardActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
private void performLogOut() {
|
private void performLogOut() {
|
||||||
try {
|
try {
|
||||||
|
stopService(new Intent(this, ReservationNotificationService.class));
|
||||||
FirebaseAuth.getInstance().signOut();
|
FirebaseAuth.getInstance().signOut();
|
||||||
Toast.makeText(this, "Sessão terminada com sucesso.", Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, "Sessão terminada com sucesso.", Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import com.example.pap_teste.models.Reserva;
|
||||||
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
|
import com.google.firebase.auth.FirebaseUser;
|
||||||
|
import com.google.firebase.database.ChildEventListener;
|
||||||
|
import com.google.firebase.database.DataSnapshot;
|
||||||
|
import com.google.firebase.database.DatabaseError;
|
||||||
|
import com.google.firebase.database.DatabaseReference;
|
||||||
|
import com.google.firebase.database.FirebaseDatabase;
|
||||||
|
|
||||||
|
public class ReservationNotificationService extends Service {
|
||||||
|
|
||||||
|
private static final String CHANNEL_ID = "ReservaNotifications";
|
||||||
|
private DatabaseReference reservasRef;
|
||||||
|
private ChildEventListener childEventListener;
|
||||||
|
private String currentUserEmail;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
createNotificationChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
||||||
|
if (user != null && user.getEmail() != null) {
|
||||||
|
currentUserEmail = user.getEmail();
|
||||||
|
startListeningForReservations();
|
||||||
|
} else {
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
return START_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startListeningForReservations() {
|
||||||
|
if (reservasRef != null && childEventListener != null) {
|
||||||
|
return; // Already listening
|
||||||
|
}
|
||||||
|
|
||||||
|
reservasRef = FirebaseDatabase.getInstance().getReference("reservas");
|
||||||
|
childEventListener = new ChildEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onChildAdded(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {
|
||||||
|
// Not needed for new reservations created by the user
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChildChanged(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {
|
||||||
|
Reserva reserva = snapshot.getValue(Reserva.class);
|
||||||
|
if (reserva != null && currentUserEmail.equals(reserva.getClienteEmail())) {
|
||||||
|
String estado = reserva.getEstado();
|
||||||
|
if (estado != null && (estado.startsWith("Confirmada") || estado.equals("Recusada") || estado.equals("Concluída") || estado.equals("Cancelada"))) {
|
||||||
|
sendNotification(reserva);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChildRemoved(@NonNull DataSnapshot snapshot) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChildMoved(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancelled(@NonNull DatabaseError error) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
reservasRef.addChildEventListener(childEventListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendNotification(Reserva reserva) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
|
||||||
|
ContextCompat.checkSelfPermission(this, android.Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
return; // Permission not granted
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent intent = new Intent(this, MainActivity.class); // Or deep link to reservations
|
||||||
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||||
|
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
|
||||||
|
|
||||||
|
String title = "Atualização de Reserva";
|
||||||
|
String message = "A sua reserva no " + reserva.getRestauranteName() + " foi " + reserva.getEstado().toLowerCase() + ".";
|
||||||
|
|
||||||
|
if (reserva.getMotivo() != null && !reserva.getMotivo().isEmpty()) {
|
||||||
|
message += " Motivo: " + reserva.getMotivo();
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
|
||||||
|
.setSmallIcon(R.drawable.na_mesa) // Assuming na_mesa is a valid drawable
|
||||||
|
.setContentTitle(title)
|
||||||
|
.setContentText(message)
|
||||||
|
.setStyle(new NotificationCompat.BigTextStyle().bigText(message))
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||||
|
.setContentIntent(pendingIntent)
|
||||||
|
.setAutoCancel(true);
|
||||||
|
|
||||||
|
NotificationManager notificationManager = getSystemService(NotificationManager.class);
|
||||||
|
if (notificationManager != null) {
|
||||||
|
notificationManager.notify(reserva.getId().hashCode(), builder.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createNotificationChannel() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
CharSequence name = "Notificações de Reservas";
|
||||||
|
String description = "Notificações sobre o estado das suas reservas";
|
||||||
|
int importance = NotificationManager.IMPORTANCE_HIGH;
|
||||||
|
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
|
||||||
|
channel.setDescription(description);
|
||||||
|
|
||||||
|
NotificationManager notificationManager = getSystemService(NotificationManager.class);
|
||||||
|
if (notificationManager != null) {
|
||||||
|
notificationManager.createNotificationChannel(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (reservasRef != null && childEventListener != null) {
|
||||||
|
reservasRef.removeEventListener(childEventListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ public class Reserva {
|
|||||||
private String hora;
|
private String hora;
|
||||||
private int pessoas;
|
private int pessoas;
|
||||||
private String estado; // Pendente, Confirmada, Concluída, Cancelada, Recusada
|
private String estado; // Pendente, Confirmada, Concluída, Cancelada, Recusada
|
||||||
|
private String motivo;
|
||||||
|
|
||||||
public Reserva() {
|
public Reserva() {
|
||||||
// Required for Firebase
|
// Required for Firebase
|
||||||
@@ -89,4 +90,12 @@ public class Reserva {
|
|||||||
public void setEstado(String estado) {
|
public void setEstado(String estado) {
|
||||||
this.estado = estado;
|
this.estado = estado;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getMotivo() {
|
||||||
|
return motivo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMotivo(String motivo) {
|
||||||
|
this.motivo = motivo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.example.pap_teste.models;
|
package com.example.pap_teste.models;
|
||||||
|
|
||||||
public class Restaurant {
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class Restaurant implements Serializable {
|
||||||
private String name;
|
private String name;
|
||||||
private String category;
|
private String category;
|
||||||
private String email;
|
private String email;
|
||||||
@@ -8,6 +10,7 @@ public class Restaurant {
|
|||||||
private String logoUrl;
|
private String logoUrl;
|
||||||
private Double ratingAvg;
|
private Double ratingAvg;
|
||||||
private Integer ratingCount;
|
private Integer ratingCount;
|
||||||
|
private boolean fechadoWebsite;
|
||||||
|
|
||||||
// No-argument constructor required for Firebase
|
// No-argument constructor required for Firebase
|
||||||
public Restaurant() {
|
public Restaurant() {
|
||||||
@@ -39,9 +42,15 @@ public class Restaurant {
|
|||||||
public void setEmail(String email) { this.email = email; }
|
public void setEmail(String email) { this.email = email; }
|
||||||
public void setAvailable(boolean available) { this.available = available; }
|
public void setAvailable(boolean available) { this.available = available; }
|
||||||
public void setLogoUrl(String logoUrl) { this.logoUrl = logoUrl; }
|
public void setLogoUrl(String logoUrl) { this.logoUrl = logoUrl; }
|
||||||
|
public boolean isFechadoWebsite() { return fechadoWebsite; }
|
||||||
|
public void setFechadoWebsite(boolean fechadoWebsite) { this.fechadoWebsite = fechadoWebsite; }
|
||||||
|
|
||||||
public Double getRatingAvg() { return ratingAvg; }
|
public Double getRatingAvg() { return ratingAvg; }
|
||||||
public void setRatingAvg(Double ratingAvg) { this.ratingAvg = ratingAvg; }
|
public void setRatingAvg(Double ratingAvg) { this.ratingAvg = ratingAvg; }
|
||||||
public Integer getRatingCount() { return ratingCount; }
|
public Integer getRatingCount() { return ratingCount; }
|
||||||
public void setRatingCount(Integer ratingCount) { this.ratingCount = ratingCount; }
|
public void setRatingCount(Integer ratingCount) { this.ratingCount = ratingCount; }
|
||||||
|
|
||||||
|
private java.util.Map<String, ScheduleDay> schedule;
|
||||||
|
public java.util.Map<String, ScheduleDay> getSchedule() { return schedule; }
|
||||||
|
public void setSchedule(java.util.Map<String, ScheduleDay> schedule) { this.schedule = schedule; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.example.pap_teste.models;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class ScheduleDay implements Serializable {
|
||||||
|
private boolean closed;
|
||||||
|
private String openTime;
|
||||||
|
private String closeTime;
|
||||||
|
|
||||||
|
public ScheduleDay() {
|
||||||
|
// No-argument constructor for Firebase
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScheduleDay(boolean closed, String openTime, String closeTime) {
|
||||||
|
this.closed = closed;
|
||||||
|
this.openTime = openTime;
|
||||||
|
this.closeTime = closeTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isClosed() {
|
||||||
|
return closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClosed(boolean closed) {
|
||||||
|
this.closed = closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOpenTime() {
|
||||||
|
return openTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOpenTime(String openTime) {
|
||||||
|
this.openTime = openTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCloseTime() {
|
||||||
|
return closeTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCloseTime(String closeTime) {
|
||||||
|
this.closeTime = closeTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,37 +36,22 @@
|
|||||||
android:id="@+id/txtClientGreeting"
|
android:id="@+id/txtClientGreeting"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="sans-serif"
|
||||||
android:text="Olá, convidado!"
|
android:text="Olá, convidado!"
|
||||||
android:textColor="@color/colorTextPrimary"
|
android:textColor="@color/colorTextPrimary"
|
||||||
android:textSize="28sp"
|
android:textSize="28sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:fontFamily="sans-serif"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@id/imgNotification"
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
android:layout_marginEnd="16dp" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Encontre a sua próxima mesa."
|
android:text="Encontre a sua próxima mesa."
|
||||||
android:textColor="@color/colorTextSecondary"
|
android:textColor="@color/colorTextSecondary"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
app:layout_constraintTop_toBottomOf="@id/txtClientGreeting"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@id/imgNotification" />
|
app:layout_constraintTop_toBottomOf="@id/txtClientGreeting" />
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/imgNotification"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:background="@drawable/bg_circle_white"
|
|
||||||
android:src="@android:drawable/ic_popup_reminder"
|
|
||||||
app:tint="@color/colorTextPrimary"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
app:layout_constraintTop_toTopOf="@id/cardProfile"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/cardProfile"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/cardProfile" />
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:id="@+id/cardProfile"
|
android:id="@+id/cardProfile"
|
||||||
@@ -127,24 +112,6 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<!-- Category Pills -->
|
|
||||||
<HorizontalScrollView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:scrollbars="none"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:paddingHorizontal="20dp">
|
|
||||||
|
|
||||||
<com.google.android.material.chip.ChipGroup
|
|
||||||
android:id="@+id/chipGroupCategories"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:singleLine="true"
|
|
||||||
app:singleSelection="true">
|
|
||||||
<!-- Chips will be added programmatically -->
|
|
||||||
</com.google.android.material.chip.ChipGroup>
|
|
||||||
</HorizontalScrollView>
|
|
||||||
|
|
||||||
<!-- Loading Spinner -->
|
<!-- Loading Spinner -->
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
@@ -188,36 +155,14 @@
|
|||||||
tools:listitem="@layout/item_restaurant_featured" />
|
tools:listitem="@layout/item_restaurant_featured" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- Main Restaurant Grid/List -->
|
<!-- Dynamic Categories Container -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/layoutAllRestaurants"
|
android:id="@+id/categoriesContainer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:visibility="gone"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginTop="32dp">
|
android:paddingBottom="16dp" />
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtListTitle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="24dp"
|
|
||||||
android:text="Todos os Restaurantes"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:fontFamily="sans-serif" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/rvMainRestaurants"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:paddingBottom="16dp"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
tools:listitem="@layout/item_restaurant" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
@@ -156,6 +156,22 @@
|
|||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:background="@drawable/input_bg" />
|
android:background="@drawable/input_bg" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="Horário de Funcionamento"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/containerSchedule"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:orientation="vertical" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/btnSaveSettings"
|
android:id="@+id/btnSaveSettings"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -19,13 +19,14 @@
|
|||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/logoNaMesa"
|
android:id="@+id/logoNaMesa"
|
||||||
android:layout_width="100dp"
|
android:layout_width="160dp"
|
||||||
android:layout_height="100dp"
|
android:layout_height="156dp"
|
||||||
|
android:layout_marginTop="124dp"
|
||||||
android:src="@drawable/na_mesa"
|
android:src="@drawable/na_mesa"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.508"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
android:layout_marginTop="50dp" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/txtTitle"
|
android:id="@+id/txtTitle"
|
||||||
@@ -70,43 +71,13 @@
|
|||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="387dp"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="24dp">
|
android:padding="24dp">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="50dp"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/bg_tabs"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:padding="3dp">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btnCliente"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:background="@drawable/btn_light"
|
|
||||||
android:text="Cliente"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="#231F1F"
|
|
||||||
android:textSize="14sp" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btnEstabelecimento"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:background="@android:color/transparent"
|
|
||||||
android:text="Estabelecimento"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textSize="14sp" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="50dp"
|
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:background="@drawable/bg_tabs"
|
android:background="@drawable/bg_tabs"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
@@ -221,37 +192,37 @@
|
|||||||
android:layout_height="54dp"
|
android:layout_height="54dp"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:padding="12dp"
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:src="@drawable/ic_visibility_off"
|
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless" />
|
android:padding="12dp"
|
||||||
|
android:src="@drawable/ic_visibility_off" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/txtForgotPassword"
|
android:id="@+id/txtForgotPassword"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:layout_gravity="end"
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
android:text="Esqueceu-se da palavra-passe?"
|
android:text="Esqueceu-se da palavra-passe?"
|
||||||
android:textColor="@color/colorPrimary"
|
android:textColor="@color/colorPrimary"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:visibility="visible"
|
android:visibility="visible" />
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"/>
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/btnFinalCriarConta"
|
android:id="@+id/btnFinalCriarConta"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="56dp"
|
android:layout_height="56dp"
|
||||||
android:layout_marginTop="24dp"
|
android:layout_marginTop="24dp"
|
||||||
app:cornerRadius="14dp"
|
|
||||||
app:backgroundTint="@color/colorPrimary"
|
|
||||||
android:text="Entrar"
|
android:text="Entrar"
|
||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/white"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:textColor="@color/white" />
|
app:backgroundTint="@color/colorPrimary"
|
||||||
|
app:cornerRadius="14dp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|||||||
@@ -122,6 +122,19 @@
|
|||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:layout_marginBottom="24dp" />
|
android:layout_marginBottom="24dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtRestauranteFechado"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Restaurante fechado temporariamente. Não é possível efetuar reservas."
|
||||||
|
android:textColor="@android:color/holo_red_dark"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="8dp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
32
app/src/main/res/layout/item_category_row.xml
Normal file
32
app/src/main/res/layout/item_category_row.xml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginTop="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtCategoryTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:fontFamily="sans-serif"
|
||||||
|
tools:text="Sushi" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvCategoryRestaurants"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingStart="24dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/item_restaurant_featured" />
|
||||||
|
</LinearLayout>
|
||||||
52
app/src/main/res/layout/item_schedule_day.xml
Normal file
52
app/src/main/res/layout/item_schedule_day.xml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingVertical="8dp">
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/cbClosed"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Folga"
|
||||||
|
android:textColor="@color/colorTextSecondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvDayName"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:text="Segunda-feira"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvOpenTime"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="09:00"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:background="@drawable/input_bg"
|
||||||
|
android:textColor="@color/colorTextPrimary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="-"
|
||||||
|
android:layout_marginHorizontal="8dp"
|
||||||
|
android:textColor="@color/colorTextSecondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvCloseTime"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="22:00"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:background="@drawable/input_bg"
|
||||||
|
android:textColor="@color/colorTextPrimary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -14,7 +14,28 @@
|
|||||||
|
|
||||||
<!-- Typography base generic configuration -->
|
<!-- Typography base generic configuration -->
|
||||||
<item name="android:fontFamily">sans-serif</item>
|
<item name="android:fontFamily">sans-serif</item>
|
||||||
|
|
||||||
|
<!-- Modern Shape Appearance (Rounded Corners) -->
|
||||||
|
<item name="shapeAppearanceSmallComponent">@style/ShapeAppearance.App.SmallComponent</item>
|
||||||
|
<item name="shapeAppearanceMediumComponent">@style/ShapeAppearance.App.MediumComponent</item>
|
||||||
|
<item name="shapeAppearanceLargeComponent">@style/ShapeAppearance.App.LargeComponent</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Theme.Pap_teste" parent="Base.Theme.Pap_teste" />
|
<style name="Theme.Pap_teste" parent="Base.Theme.Pap_teste" />
|
||||||
|
|
||||||
|
<!-- Shape Styles -->
|
||||||
|
<style name="ShapeAppearance.App.SmallComponent" parent="ShapeAppearance.Material3.SmallComponent">
|
||||||
|
<item name="cornerFamily">rounded</item>
|
||||||
|
<item name="cornerSize">12dp</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="ShapeAppearance.App.MediumComponent" parent="ShapeAppearance.Material3.MediumComponent">
|
||||||
|
<item name="cornerFamily">rounded</item>
|
||||||
|
<item name="cornerSize">16dp</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="ShapeAppearance.App.LargeComponent" parent="ShapeAppearance.Material3.LargeComponent">
|
||||||
|
<item name="cornerFamily">rounded</item>
|
||||||
|
<item name="cornerSize">24dp</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user