Compare commits
11 Commits
31a7cbb2df
...
teste
| Author | SHA1 | Date | |
|---|---|---|---|
| f4d2889718 | |||
| fedcf44c49 | |||
| b2bb591bb7 | |||
| 585af79ffa | |||
| 9d57ed4d0a | |||
| 824ff28001 | |||
| 86dbe2d319 | |||
| 8c1e01dc4c | |||
| 995d23ac7a | |||
| eaa3d86fc9 | |||
| d192568ed8 |
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-30T16:03:11.250011Z">
|
||||||
<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>
|
||||||
|
|||||||
BIN
OperatingHoursDay.class
Normal file
BIN
OperatingHoursDay.class
Normal file
Binary file not shown.
BIN
TestFirebase.class
Normal file
BIN
TestFirebase.class
Normal file
Binary file not shown.
17
TestFirebase.java
Normal file
17
TestFirebase.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import java.beans.Introspector;
|
||||||
|
import java.beans.PropertyDescriptor;
|
||||||
|
|
||||||
|
public class TestFirebase {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
PropertyDescriptor[] pds = Introspector.getBeanInfo(OperatingHoursDay.class).getPropertyDescriptors();
|
||||||
|
for (PropertyDescriptor pd : pds) {
|
||||||
|
System.out.println("Property: " + pd.getName() + ", readMethod: " + (pd.getReadMethod() != null ? pd.getReadMethod().getName() : "null"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OperatingHoursDay {
|
||||||
|
private boolean isOpen;
|
||||||
|
public boolean getIsOpen() { return isOpen; }
|
||||||
|
public void setIsOpen(boolean isOpen) { this.isOpen = isOpen; }
|
||||||
|
}
|
||||||
@@ -15,6 +15,10 @@
|
|||||||
<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"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
@@ -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
|
|
||||||
// NovaReservaActivity
|
|
||||||
// We pass the filter so it can maybe open directly or we just pass restaurant
|
|
||||||
// email
|
|
||||||
intent.putExtra("restaurant", restaurant);
|
intent.putExtra("restaurant", restaurant);
|
||||||
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,7 +156,7 @@ 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.addValueEventListener(new ValueEventListener() {
|
usersRef.addValueEventListener(new ValueEventListener() {
|
||||||
@@ -207,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
|
||||||
@@ -271,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("restaurant", restaurant);
|
|
||||||
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() {
|
||||||
@@ -231,20 +232,93 @@ public class EstablishmentDashboardActivity extends AppCompatActivity {
|
|||||||
.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
|
.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) {
|
||||||
|
int totalMesas = 0;
|
||||||
|
int mesasOcupadas = 0;
|
||||||
int mesasLivres = 0;
|
int mesasLivres = 0;
|
||||||
for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) {
|
for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) {
|
||||||
com.example.pap_teste.models.Mesa mesa = ds.getValue(com.example.pap_teste.models.Mesa.class);
|
com.example.pap_teste.models.Mesa mesa = ds.getValue(com.example.pap_teste.models.Mesa.class);
|
||||||
if (mesa != null && mesa.getRestauranteEmail() != null && mesa.getRestauranteEmail().trim().equalsIgnoreCase(finalEmail)) {
|
if (mesa != null && (mesa.getRestauranteEmail() == null || mesa.getRestauranteEmail().trim().equalsIgnoreCase(finalEmail))) {
|
||||||
if ("Livre".equalsIgnoreCase(mesa.getEstado())) {
|
totalMesas++;
|
||||||
|
if ("Ocupada".equalsIgnoreCase(mesa.getEstado()) || "Reservada".equalsIgnoreCase(mesa.getEstado())) {
|
||||||
|
mesasOcupadas++;
|
||||||
|
} else if ("Livre".equalsIgnoreCase(mesa.getEstado())) {
|
||||||
mesasLivres++;
|
mesasLivres++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (txtMesasLivres != null) txtMesasLivres.setText(String.format(java.util.Locale.US, "%02d", mesasLivres));
|
if (txtMesasLivres != null) txtMesasLivres.setText(mesasOcupadas + " / " + totalMesas);
|
||||||
|
TextView txtMesasLivresSub = findViewById(R.id.txtMesasLivresSub);
|
||||||
|
if (txtMesasLivresSub != null) txtMesasLivresSub.setText(mesasLivres + " livres");
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,13 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
|||||||
txtTitle.setText("Reserva: " + (selectedRestaurant != null ? selectedRestaurant.getName() : ""));
|
txtTitle.setText("Reserva: " + (selectedRestaurant != null ? selectedRestaurant.getName() : ""));
|
||||||
setupReservationOptions();
|
setupReservationOptions();
|
||||||
loadRestaurantRating();
|
loadRestaurantRating();
|
||||||
|
|
||||||
|
boolean isFechado = selectedRestaurant != null && selectedRestaurant.isFechadoWebsite();
|
||||||
|
findViewById(R.id.btnSelectDate).setEnabled(!isFechado);
|
||||||
|
findViewById(R.id.btnSelectTime).setEnabled(!isFechado);
|
||||||
|
android.view.View etPartySize = findViewById(R.id.etPartySize);
|
||||||
|
if (etPartySize != null) etPartySize.setEnabled(!isFechado);
|
||||||
|
findViewById(R.id.btnConfirmarReserva).setEnabled(!isFechado);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,8 +167,32 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
|||||||
String logoUrl = ds.child("logoUrl").getValue(String.class);
|
String logoUrl = ds.child("logoUrl").getValue(String.class);
|
||||||
|
|
||||||
if (name != null && email != null) {
|
if (name != null && email != null) {
|
||||||
restaurantsList
|
com.example.pap_teste.models.Restaurant rest = new com.example.pap_teste.models.Restaurant(name, cat, email, false, logoUrl);
|
||||||
.add(new com.example.pap_teste.models.Restaurant(name, cat, email, false, logoUrl));
|
Boolean fechadoWebsite = ds.child("fechadoWebsite").getValue(Boolean.class);
|
||||||
|
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);
|
||||||
|
} else if (ds.hasChild("operatingHours")) {
|
||||||
|
java.util.Map<String, com.example.pap_teste.models.ScheduleDay> schMap = new java.util.HashMap<>();
|
||||||
|
for (com.google.firebase.database.DataSnapshot schDs : ds.child("operatingHours").getChildren()) {
|
||||||
|
com.example.pap_teste.models.ScheduleDay sd = schDs.getValue(com.example.pap_teste.models.ScheduleDay.class);
|
||||||
|
if (schDs.hasChild("isOpen")) {
|
||||||
|
Boolean isOpen = schDs.child("isOpen").getValue(Boolean.class);
|
||||||
|
if (isOpen != null && sd != null) {
|
||||||
|
sd.setClosed(!isOpen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
schMap.put(schDs.getKey(), sd);
|
||||||
|
}
|
||||||
|
rest.setSchedule(schMap);
|
||||||
|
}
|
||||||
|
restaurantsList.add(rest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,18 +223,72 @@ public class ExplorarRestaurantesActivity 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) -> {
|
android.app.DatePickerDialog dpd = 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);
|
||||||
}, cal.get(java.util.Calendar.YEAR), cal.get(java.util.Calendar.MONTH),
|
int dayOfWeek = selectedCal.get(java.util.Calendar.DAY_OF_WEEK);
|
||||||
cal.get(java.util.Calendar.DAY_OF_MONTH)).show();
|
|
||||||
|
boolean isClosed = selectedRestaurant.isClosedForDay(dayOfWeek);
|
||||||
|
|
||||||
|
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.DAY_OF_MONTH));
|
||||||
|
dpd.getDatePicker().setMinDate(System.currentTimeMillis() - 1000);
|
||||||
|
dpd.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);
|
||||||
|
|
||||||
|
com.example.pap_teste.models.ScheduleDay sd = selectedRestaurant.getScheduleForDay(dayOfWeek);
|
||||||
|
if (sd != null && !sd.isClosed()) {
|
||||||
|
if (sd.getOpenTime() != null && sd.getCloseTime() != null && !sd.getOpenTime().isEmpty() && !sd.getCloseTime().isEmpty()) {
|
||||||
|
String open = sd.getOpenTime();
|
||||||
|
String close = sd.getCloseTime();
|
||||||
|
if (open.compareTo(close) < 0) {
|
||||||
|
if (chosenTime.compareTo(open) < 0 || chosenTime.compareTo(close) > 0) {
|
||||||
|
isOutOfHours = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (chosenTime.compareTo(open) < 0 && chosenTime.compareTo(close) > 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();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -161,11 +161,18 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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]));
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,10 +139,44 @@ public class NovaReservaActivity extends AppCompatActivity {
|
|||||||
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);
|
||||||
|
} else if (ds.hasChild("operatingHours")) {
|
||||||
|
java.util.Map<String, com.example.pap_teste.models.ScheduleDay> schMap = new java.util.HashMap<>();
|
||||||
|
for (com.google.firebase.database.DataSnapshot schDs : ds.child("operatingHours").getChildren()) {
|
||||||
|
com.example.pap_teste.models.ScheduleDay sd = schDs.getValue(com.example.pap_teste.models.ScheduleDay.class);
|
||||||
|
// Handle case where operatingHours has isOpen instead of closed
|
||||||
|
if (schDs.hasChild("isOpen")) {
|
||||||
|
Boolean isOpen = schDs.child("isOpen").getValue(Boolean.class);
|
||||||
|
if (isOpen != null && sd != null) {
|
||||||
|
sd.setClosed(!isOpen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
schMap.put(schDs.getKey(), sd);
|
||||||
|
}
|
||||||
|
rest.setSchedule(schMap);
|
||||||
|
} else if (ds.hasChild("horario")) {
|
||||||
|
java.util.Map<String, com.example.pap_teste.models.ScheduleDay> schMap = new java.util.HashMap<>();
|
||||||
|
for (com.google.firebase.database.DataSnapshot schDs : ds.child("horario").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,18 +210,72 @@ 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) -> {
|
android.app.DatePickerDialog dpd = 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);
|
||||||
}, cal.get(java.util.Calendar.YEAR), cal.get(java.util.Calendar.MONTH),
|
int dayOfWeek = selectedCal.get(java.util.Calendar.DAY_OF_WEEK);
|
||||||
cal.get(java.util.Calendar.DAY_OF_MONTH)).show();
|
|
||||||
|
boolean isClosed = selectedRestaurant.isClosedForDay(dayOfWeek);
|
||||||
|
|
||||||
|
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.DAY_OF_MONTH));
|
||||||
|
dpd.getDatePicker().setMinDate(System.currentTimeMillis() - 1000);
|
||||||
|
dpd.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);
|
||||||
|
|
||||||
|
com.example.pap_teste.models.ScheduleDay sd = selectedRestaurant.getScheduleForDay(dayOfWeek);
|
||||||
|
if (sd != null && !sd.isClosed()) {
|
||||||
|
if (sd.getOpenTime() != null && sd.getCloseTime() != null && !sd.getOpenTime().isEmpty() && !sd.getCloseTime().isEmpty()) {
|
||||||
|
String open = sd.getOpenTime();
|
||||||
|
String close = sd.getCloseTime();
|
||||||
|
if (open.compareTo(close) < 0) {
|
||||||
|
if (chosenTime.compareTo(open) < 0 || chosenTime.compareTo(close) > 0) {
|
||||||
|
isOutOfHours = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (chosenTime.compareTo(open) < 0 && chosenTime.compareTo(close) > 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.example.pap_teste.models;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import com.google.firebase.database.PropertyName;
|
||||||
|
|
||||||
|
public class OperatingHoursDay implements Serializable {
|
||||||
|
@PropertyName("isOpen")
|
||||||
|
private boolean openStatus;
|
||||||
|
private String openTime;
|
||||||
|
private String closeTime;
|
||||||
|
|
||||||
|
public OperatingHoursDay() {
|
||||||
|
// Construtor vazio necessário para Firebase
|
||||||
|
}
|
||||||
|
|
||||||
|
@PropertyName("isOpen")
|
||||||
|
public boolean getOpenStatus() {
|
||||||
|
return openStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PropertyName("isOpen")
|
||||||
|
public void setOpenStatus(boolean openStatus) {
|
||||||
|
this.openStatus = openStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ public class Restaurant implements Serializable {
|
|||||||
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() {
|
||||||
@@ -41,9 +42,51 @@ public class Restaurant implements Serializable {
|
|||||||
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; }
|
||||||
|
|
||||||
|
public ScheduleDay getScheduleForDay(int dayOfWeekCalendar) {
|
||||||
|
if (schedule == null) return null;
|
||||||
|
String[] dayKeys = {"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"};
|
||||||
|
String[] dayKeysPt = {"domingo", "segunda-feira", "terça-feira", "quarta-feira", "quinta-feira", "sexta-feira", "sábado"};
|
||||||
|
String[] dayKeysPtShort = {"domingo", "segunda", "terça", "quarta", "quinta", "sexta", "sábado"};
|
||||||
|
int index = dayOfWeekCalendar - 1;
|
||||||
|
|
||||||
|
ScheduleDay sd = schedule.get(dayKeys[index]);
|
||||||
|
if (sd == null) sd = schedule.get(dayKeysPt[index]);
|
||||||
|
if (sd == null) sd = schedule.get(dayKeysPtShort[index]);
|
||||||
|
if (sd == null) sd = schedule.get(String.valueOf(index));
|
||||||
|
|
||||||
|
if (sd == null) {
|
||||||
|
for (java.util.Map.Entry<String, ScheduleDay> entry : schedule.entrySet()) {
|
||||||
|
String k = entry.getKey().toLowerCase(java.util.Locale.ROOT).replace("-", "").replace(" ", "");
|
||||||
|
String expected1 = dayKeys[index].replace("-", "");
|
||||||
|
String expected2 = dayKeysPt[index].replace("-", "");
|
||||||
|
String expected3 = dayKeysPtShort[index].replace("-", "");
|
||||||
|
|
||||||
|
if (k.equals(expected1) || k.equals(expected2) || k.equals(expected3) || k.equals(String.valueOf(index))) {
|
||||||
|
return entry.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isClosedForDay(int dayOfWeekCalendar) {
|
||||||
|
if (schedule == null || schedule.isEmpty()) return false; // If no schedule is set at all, default to open to avoid locking out old restaurants.
|
||||||
|
ScheduleDay sd = getScheduleForDay(dayOfWeekCalendar);
|
||||||
|
if (sd == null) {
|
||||||
|
return true; // Se o dia não existe no mapa de um restaurante com horário, está fechado.
|
||||||
|
}
|
||||||
|
return sd.isClosed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package com.example.pap_teste.models;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class ScheduleDay implements Serializable {
|
||||||
|
private boolean closed;
|
||||||
|
private Boolean isOpen;
|
||||||
|
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.isOpen = !closed;
|
||||||
|
this.openTime = openTime;
|
||||||
|
this.closeTime = closeTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isClosed() {
|
||||||
|
if (isOpen != null) {
|
||||||
|
return !isOpen;
|
||||||
|
}
|
||||||
|
return closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClosed(boolean closed) {
|
||||||
|
this.closed = closed;
|
||||||
|
if (this.isOpen == null) {
|
||||||
|
this.isOpen = !closed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handles potential "isClosed" boolean in Firebase
|
||||||
|
public void setIsClosed(Boolean isClosed) {
|
||||||
|
if (isClosed != null) {
|
||||||
|
this.closed = isClosed;
|
||||||
|
this.isOpen = !isClosed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handles potential "fechado" boolean from Portuguese web dashboards
|
||||||
|
public void setFechado(Boolean fechado) {
|
||||||
|
if (fechado != null) {
|
||||||
|
this.closed = fechado;
|
||||||
|
this.isOpen = !fechado;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@com.google.firebase.database.PropertyName("isOpen")
|
||||||
|
public Boolean getIsOpen() {
|
||||||
|
return isOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
@com.google.firebase.database.PropertyName("isOpen")
|
||||||
|
public void setIsOpen(Boolean isOpen) {
|
||||||
|
this.isOpen = isOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
app/src/main/res/drawable/ic_clock_gold.xml
Normal file
13
app/src/main/res/drawable/ic_clock_gold.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="#D4AF37">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z" />
|
||||||
|
</vector>
|
||||||
@@ -112,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
|
||||||
@@ -173,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"
|
||||||
|
|||||||
@@ -71,116 +71,150 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="32dp"
|
android:layout_marginTop="32dp"
|
||||||
android:orientation="horizontal">
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<!-- MESAS OCUPADAS CARD -->
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:id="@+id/cardReservasHoje"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="6dp"
|
app:cardBackgroundColor="#1F1D1A"
|
||||||
android:layout_weight="1"
|
|
||||||
android:foreground="?attr/selectableItemBackground"
|
|
||||||
app:cardBackgroundColor="@color/colorSurface"
|
|
||||||
app:cardCornerRadius="16dp"
|
app:cardCornerRadius="16dp"
|
||||||
app:cardElevation="2dp">
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
<LinearLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:padding="20dp">
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
android:id="@+id/lblMesasOcupadas"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Reservas"
|
android:text="MESAS OCUPADAS"
|
||||||
android:textColor="@color/colorTextSecondary"
|
android:textColor="#888175"
|
||||||
android:textSize="13sp" />
|
android:textSize="14sp"
|
||||||
|
android:letterSpacing="0.05"
|
||||||
|
android:fontFamily="serif"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<TextView
|
<ImageView
|
||||||
android:id="@+id/txtReservasHojeDash"
|
android:layout_width="24dp"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="24dp"
|
||||||
android:layout_height="wrap_content"
|
android:src="@drawable/ic_clock_gold"
|
||||||
android:layout_marginTop="6dp"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
android:text="00"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
android:textColor="@color/colorPrimary"
|
app:layout_constraintBottom_toBottomOf="@id/lblMesasOcupadas" />
|
||||||
android:textSize="30sp"
|
|
||||||
android:fontFamily="sans-serif-medium" />
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="6dp"
|
|
||||||
android:layout_marginEnd="6dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
app:cardBackgroundColor="@color/colorSurface"
|
|
||||||
app:cardCornerRadius="16dp"
|
|
||||||
app:cardElevation="2dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Mesas"
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textSize="13sp" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/txtMesasLivresDash"
|
android:id="@+id/txtMesasLivresDash"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="6dp"
|
android:layout_marginTop="16dp"
|
||||||
android:text="00"
|
android:text="0 / 0"
|
||||||
android:textColor="@color/colorTextPrimary"
|
android:textColor="#FFFFFF"
|
||||||
android:textSize="30sp"
|
android:textSize="38sp"
|
||||||
android:fontFamily="sans-serif-medium" />
|
android:fontFamily="serif-monospace"
|
||||||
</LinearLayout>
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/lblMesasOcupadas" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtMesasLivresSub"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="0 livres"
|
||||||
|
android:textColor="#D4AF37"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtMesasLivresDash" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<LinearLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="6dp"
|
android:layout_marginTop="12dp"
|
||||||
android:layout_weight="1"
|
android:orientation="horizontal">
|
||||||
app:cardBackgroundColor="@color/colorSurface"
|
|
||||||
app:cardCornerRadius="16dp"
|
|
||||||
app:cardElevation="2dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
<!-- RESERVAS CARD -->
|
||||||
android:layout_width="match_parent"
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/cardReservasHoje"
|
||||||
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:layout_marginEnd="6dp"
|
||||||
android:orientation="vertical"
|
android:layout_weight="1"
|
||||||
android:padding="16dp">
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Espera"
|
android:gravity="center"
|
||||||
android:textColor="@color/colorTextSecondary"
|
android:orientation="vertical"
|
||||||
android:textSize="13sp" />
|
android:padding="16dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/txtListaEsperaDash"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Reservas"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtReservasHojeDash"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:text="00"
|
||||||
|
android:textColor="@color/colorPrimary"
|
||||||
|
android:textSize="30sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<!-- ESPERA CARD -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
app:cardBackgroundColor="@color/colorSurface"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="6dp"
|
android:gravity="center"
|
||||||
android:text="00"
|
android:orientation="vertical"
|
||||||
android:textColor="@color/colorWarning"
|
android:padding="16dp">
|
||||||
android:textSize="30sp"
|
|
||||||
android:fontFamily="sans-serif-medium" />
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Espera"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtListaEsperaDash"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:text="00"
|
||||||
|
android:textColor="@color/colorWarning"
|
||||||
|
android:textSize="30sp"
|
||||||
|
android:fontFamily="sans-serif-medium" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp = "9.1.0"
|
agp = "9.1.1"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
junitVersion = "1.3.0"
|
junitVersion = "1.3.0"
|
||||||
espressoCore = "3.7.0"
|
espressoCore = "3.7.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user