diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml
index 0c0c338..4c59370 100644
--- a/.idea/deploymentTargetDropDown.xml
+++ b/.idea/deploymentTargetDropDown.xml
@@ -3,7 +3,20 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuActivity.java b/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuActivity.java
index db04bbb..cf74b94 100644
--- a/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuActivity.java
+++ b/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuActivity.java
@@ -25,6 +25,7 @@ public class MenuActivity extends AppCompatActivity {
public static final String NAME_EXTRA = "mtu_name";
public static final String ID_EXTRA = "mtu_id";
+ private MenuViewModel viewModel;
private MenuAdapter adapter;
@Override
@@ -41,13 +42,15 @@ public class MenuActivity extends AppCompatActivity {
// Get View Model
ViewModelProvider viewModelProvider = new ViewModelProvider(this);
- MenuViewModel viewModel = viewModelProvider.get(MenuViewModel.class);
+ viewModel = viewModelProvider.get(MenuViewModel.class);
// Get Info
String name = getIntent().getStringExtra(NAME_EXTRA);
- String id = getIntent().getStringExtra(ID_EXTRA);
toolbar.setTitle(name);
- viewModel.task.setup(id, new Date());
+ String id = getIntent().getStringExtra(ID_EXTRA);
+ if (!viewModel.task.isSetup()) {
+ viewModel.task.setup(id, new Date());
+ }
// Setup RecyclerView
adapter = new MenuAdapter(viewModel.task);
@@ -55,6 +58,9 @@ public class MenuActivity extends AppCompatActivity {
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
EdgeToEdgeUtil.setup(this, recyclerView);
+
+ // Handle Existing Date Picker
+ MenuDatePicker.setupListeners(getSupportFragmentManager());
}
@Override
@@ -73,4 +79,13 @@ public class MenuActivity extends AppCompatActivity {
return super.onOptionsItemSelected(item);
}
}
+
+ /**
+ * Update selected date.
+ * @param newDate New date
+ */
+ public void updateDate(Date newDate) {
+ viewModel.task.setup(null, newDate);
+ viewModel.task.start();
+ }
}
diff --git a/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuAdapter.java b/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuAdapter.java
index 53c7aa6..332eb8e 100644
--- a/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuAdapter.java
+++ b/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuAdapter.java
@@ -1,6 +1,5 @@
package com.thebrokenrail.mtudining.activity.menu;
-import android.text.InputType;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -9,13 +8,15 @@ import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.textfield.MaterialAutoCompleteTextView;
-import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import com.thebrokenrail.mtudining.R;
import com.thebrokenrail.mtudining.activity.task.Task;
import com.thebrokenrail.mtudining.activity.task.TaskAdapter;
import com.thebrokenrail.mtudining.util.DateUtil;
import com.thebrokenrail.mtudining.widget.CategoryView;
+import com.thebrokenrail.mtudining.widget.CustomDropDownView;
+
+import java.util.Date;
/**
* Adapter for listing dining halls.
@@ -34,32 +35,57 @@ class MenuAdapter extends TaskAdapter {
return category;
}
+ /**
+ * Get currently selected meal.
+ * @return A meal menu
+ */
+ private MenuData.Meal getMeal() {
+ if (getResult() != null) {
+ for (MenuData.Meal meal : getResult().meals) {
+ if (meal.name.equals(getResult().selectedMeal)) {
+ // Found Meal
+ return meal;
+ }
+ }
+ }
+ // Could Not Find Meal
+ return null;
+ }
+
@Override
protected void bindItemView(View view, int position) {
- /*ListData.Category data = getResult().categories.get(position);
+ MenuData.Meal meal = getMeal();
+ assert meal != null;
+ MenuData.Meal.Category data = meal.categories.get(position - getFirstElementPosition());
// Setup View
CategoryView category = (CategoryView) view;
category.setup(data.isOpen, data.name, () -> {
// Open/Close Category
data.isOpen = !data.isOpen;
- notifyItemChanged(getResult().categories.indexOf(data));
+ notifyItemChanged(meal.categories.indexOf(data) + getFirstElementPosition());
});
// Add Locations
category.clearItems();
- for (ListData.Category.Element location : data.locations) {
- category.addItem(location.name, () -> {
+ for (MenuData.Meal.Category.Element item : data.items) {
+ category.addItem(item.name, () -> {
// Do Something!
});
- }*/
+ }
}
@Override
protected int getDataSize() {
- return 0;
+ MenuData.Meal meal = getMeal();
+ if (meal != null) {
+ return meal.categories.size();
+ } else {
+ return 0;
+ }
}
@Override
protected View createHeader(@NonNull ViewGroup parent) {
+ // Inflate Header XML
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
return layoutInflater.inflate(R.layout.menu_header, parent, false);
}
@@ -67,16 +93,49 @@ class MenuAdapter extends TaskAdapter {
@Override
protected void bindHeader(View view) {
// Set Date
+ CustomDropDownView dateField = view.findViewById(R.id.menu_date_field);
MaterialAutoCompleteTextView date = view.findViewById(R.id.menu_date);
// Retrieve from Task as this must show even when result is not available.
- date.setText(DateUtil.toString(((MenuTask) getTask()).getDate()));
+ Date current = ((MenuTask) getTask()).getDate();
+ date.setText(DateUtil.toString(current));
+ // Handle Click
+ dateField.setup();
+ dateField.setOnDropDown(() -> {
+ // Open Date Picker
+ MenuDatePicker.show(current, ((MenuActivity) view.getContext()).getSupportFragmentManager());
+ });
// Set Meal
boolean hasMeal = getResult() != null;
- TextInputLayout meal = view.findViewById(R.id.menu_meal_field);
- meal.setEnabled(hasMeal);
+ TextInputLayout mealField = view.findViewById(R.id.menu_meal_field);
+ MaterialAutoCompleteTextView meal = view.findViewById(R.id.menu_meal);
if (hasMeal) {
-
+ // Enable/Disable Field
+ hasMeal = getResult().meals.size() > 0;
+ mealField.setEnabled(hasMeal);
+ if (hasMeal) {
+ // Set Dropdown Options
+ meal.setSimpleItems(getResult().meals.stream().map(x -> x.name).toArray(String[]::new));
+ // Set Selection
+ meal.setText(getResult().selectedMeal, false);
+ // Handle Meal Selection
+ meal.setOnItemClickListener((parent, view1, position, id) -> {
+ // Get New Meal
+ String newMeal = (String) parent.getAdapter().getItem(position);
+ // Update UI
+ int oldItemCount = getItemCount();
+ getResult().selectedMeal = newMeal;
+ reloadUI(oldItemCount);
+ });
+ } else {
+ // Show N/A
+ meal.setText(meal.getResources().getString(R.string.not_available), false);
+ }
+ } else {
+ // Disable Field
+ mealField.setEnabled(false);
+ // Show No Text
+ meal.setText("", false);
}
}
}
diff --git a/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuData.java b/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuData.java
index 5028c34..daa0290 100644
--- a/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuData.java
+++ b/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuData.java
@@ -7,12 +7,10 @@ class MenuData {
public static class Meal {
public static class Category {
public static class Element {
- public final String id;
public final String name;
public final String description;
- public Element(String id, String name, String description) {
- this.id = id;
+ public Element(String name, String description) {
this.name = name;
this.description = description;
}
@@ -20,7 +18,7 @@ class MenuData {
public final String name;
public boolean isOpen = true;
- public final List locations = new ArrayList<>();
+ public final List items = new ArrayList<>();
public Category(String name) {
this.name = name;
@@ -38,5 +36,6 @@ class MenuData {
}
public final List meals = new ArrayList<>();
- public String selectedMeal;
+ // This may be changed at runtime.
+ public String selectedMeal = null;
}
diff --git a/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuDatePicker.java b/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuDatePicker.java
new file mode 100644
index 0000000..0c71f13
--- /dev/null
+++ b/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuDatePicker.java
@@ -0,0 +1,86 @@
+package com.thebrokenrail.mtudining.activity.menu;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+
+import com.google.android.material.datepicker.MaterialDatePicker;
+import com.thebrokenrail.mtudining.R;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Date picker for menu selection.
+ */
+public class MenuDatePicker {
+ private final static String TAG = "menu_date_picker";
+
+ /**
+ * Show date picker.
+ * @param current Currently selected date
+ * @param fragmentManager Support fragment manager
+ */
+ public static void show(Date current, FragmentManager fragmentManager) {
+ // Get Date In UTC
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(current);
+ Calendar utcCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ copyDateFromCalender(calendar, utcCalendar);
+ // Build Picker
+ MaterialDatePicker datePicker = MaterialDatePicker.Builder.datePicker()
+ .setTitleText(R.string.select_date)
+ .setSelection(utcCalendar.getTimeInMillis())
+ .build();
+ // Show
+ datePicker.show(fragmentManager, TAG);
+ // Handle Selection
+ setupListeners(datePicker);
+ }
+
+ /**
+ * Setup event listeners on existing date picker.
+ * @param datePicker The date picker
+ */
+ private static void setupListeners(MaterialDatePicker datePicker) {
+ datePicker.addOnPositiveButtonClickListener(selection -> {
+ // Get Selection
+ if (selection != null) {
+ // Get New Date
+ Calendar calendar = Calendar.getInstance();
+ Calendar utcCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ Date selectedDate = new Date(selection);
+ utcCalendar.setTime(selectedDate);
+ copyDateFromCalender(utcCalendar, calendar);
+ Date newDate = calendar.getTime();
+ // Get Activity
+ MenuActivity activity = (MenuActivity) datePicker.requireActivity();
+ // Update Date
+ activity.updateDate(newDate);
+ }
+ });
+ }
+
+ /**
+ * Setup event listeners on existing date picker.
+ * @param fragmentManager Support fragment manager
+ */
+ @SuppressWarnings({"unchecked"})
+ public static void setupListeners(FragmentManager fragmentManager) {
+ // Get Picker
+ Fragment fragment = fragmentManager.findFragmentByTag(TAG);
+ if (fragment != null) {
+ MaterialDatePicker datePicker = (MaterialDatePicker) fragment;
+ setupListeners(datePicker);
+ }
+ }
+
+ private static void copyDateFromCalender(Calendar src, Calendar dst) {
+ int year = src.get(Calendar.YEAR);
+ int month = src.get(Calendar.MONTH);
+ int day = src.get(Calendar.DAY_OF_MONTH);
+ dst.set(Calendar.YEAR, year);
+ dst.set(Calendar.MONTH, month);
+ dst.set(Calendar.DAY_OF_MONTH, day);
+ }
+}
diff --git a/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuTask.java b/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuTask.java
index 0062db9..c154a3c 100644
--- a/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuTask.java
+++ b/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuTask.java
@@ -2,11 +2,16 @@ package com.thebrokenrail.mtudining.activity.menu;
import com.thebrokenrail.mtudining.activity.task.Task;
import com.thebrokenrail.mtudining.api.Connection;
+import com.thebrokenrail.mtudining.api.method.PeriodDetail;
import com.thebrokenrail.mtudining.api.method.Periods;
import com.thebrokenrail.mtudining.util.Constants;
+import java.util.Comparator;
import java.util.Date;
+/**
+ * Task for loading menu information.
+ */
public class MenuTask extends Task {
private final Connection connection;
@@ -29,6 +34,14 @@ public class MenuTask extends Task {
this.date = date;
}
+ /**
+ * Check if task is setup.
+ * @return If the task is setup
+ */
+ boolean isSetup() {
+ return locationId != null;
+ }
+
/**
* Get date.
* @return The date
@@ -43,10 +56,61 @@ public class MenuTask extends Task {
Periods periods = new Periods(Constants.PLATFORM, locationId, date);
connection.send(periods, periodsResponse -> {
// Loaded Periods
- for (Periods.Response.Period period : periodsResponse.periods) {
- System.out.println("Period: " + period.name);
+
+ // Sort Periods/Meals
+ periodsResponse.periods.sort(Comparator.comparingInt(a -> a.sort_order));
+
+ // Load Menu For Each Period
+ MenuData data = new MenuData();
+ int[] remaining = {periodsResponse.periods.size()};
+ if (remaining[0] > 0) {
+ // Start Loading Menus
+ for (Periods.Response.Period period : periodsResponse.periods) {
+ // Load Period Details
+ MenuData.Meal meal = new MenuData.Meal(period.id, period.name);
+ PeriodDetail periodDetail = new PeriodDetail(Constants.PLATFORM, locationId, date, period.id);
+ connection.send(periodDetail, periodDetailResponse -> {
+ // Loaded Period Details
+
+ // Sort Categories
+ periodDetailResponse.menu.periods.categories.sort(Comparator.comparingInt(a -> a.sort_order));
+
+ // Add Data
+ for (PeriodDetail.Response.Menu.PeriodData.MenuCategory category : periodDetailResponse.menu.periods.categories) {
+ MenuData.Meal.Category menuCategory = new MenuData.Meal.Category(category.name);
+
+ // Sort Items
+ category.items.sort(Comparator.comparingInt(a -> a.sort_order));
+
+ // Add Items To Category
+ for (PeriodDetail.Response.Menu.PeriodData.MenuCategory.MenuItem item : category.items) {
+ menuCategory.items.add(new MenuData.Meal.Category.Element(item.name, item.desc));
+ }
+
+ // Skip Empty Category
+ if (menuCategory.items.size() > 0) {
+ meal.categories.add(menuCategory);
+ }
+ }
+
+ // Check If All Data Has Been Loaded
+ remaining[0]--;
+ if (remaining[0] == 0) {
+ // All Data Loaded
+ done(id, data);
+ }
+ }, () -> {
+ // Error
+ done(id, null);
+ });
+ data.meals.add(meal);
+ }
+ // Set Selected Meal
+ data.selectedMeal = data.meals.get(0).name;
+ } else {
+ // No Periods To Load
+ done(id, data);
}
- done(id, null);
}, () -> {
// Error
done(id, null);
diff --git a/app/src/main/java/com/thebrokenrail/mtudining/activity/task/Task.java b/app/src/main/java/com/thebrokenrail/mtudining/activity/task/Task.java
index bbc6d7e..8923c50 100644
--- a/app/src/main/java/com/thebrokenrail/mtudining/activity/task/Task.java
+++ b/app/src/main/java/com/thebrokenrail/mtudining/activity/task/Task.java
@@ -21,7 +21,7 @@ public abstract class Task {
/**
* Last time when {@link #start()} was called.
*/
- private long lastStart;
+ private Long lastStart = null;
/**
* Start task.
@@ -67,6 +67,8 @@ public abstract class Task {
}
// Update Listeners
sendDataToListeners();
+ // Ignore Future Responses From This API Call
+ lastStart = null;
}
/**
diff --git a/app/src/main/java/com/thebrokenrail/mtudining/activity/task/TaskAdapter.java b/app/src/main/java/com/thebrokenrail/mtudining/activity/task/TaskAdapter.java
index d94e292..6b3151c 100644
--- a/app/src/main/java/com/thebrokenrail/mtudining/activity/task/TaskAdapter.java
+++ b/app/src/main/java/com/thebrokenrail/mtudining/activity/task/TaskAdapter.java
@@ -198,12 +198,15 @@ public abstract class TaskAdapter extends RecyclerView.Adapter {
+ private final int platform;
+ private final String locationId;
+ private final String date;
+ private final String period;
+
+ public PeriodDetail(int platform, String locationId, Date date, String period) {
+ this.platform = platform;
+ this.locationId = locationId;
+ this.date = DateUtil.toString(date);
+ this.period = period;
+ }
+
+ @Override
+ public String getPath() {
+ return "/location/" + locationId + "/periods/" + period + "?platform=" + platform + "&date=" + date;
+ }
+
+ @Override
+ public Class getResponseClass() {
+ return Response.class;
+ }
+
+ public static class Response {
+ public static class Menu {
+ public static class PeriodData {
+ public static class MenuCategory {
+ public static class MenuItem {
+ public String name;
+ public String desc;
+ public int sort_order;
+ }
+ public String name;
+ public List