diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1a89b7c..a549fb2 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -32,7 +32,6 @@ android { dependencies { implementation("androidx.appcompat:appcompat:1.6.1") implementation("com.google.android.material:material:1.11.0") - implementation("androidx.constraintlayout:constraintlayout:2.1.4") // OkHttp (For HTTP) implementation("com.squareup.okhttp3:okhttp:4.12.0") // Moshi (For JSON) diff --git a/app/src/main/java/com/thebrokenrail/mtudining/activity/list/ListActivity.java b/app/src/main/java/com/thebrokenrail/mtudining/activity/list/ListActivity.java index 2edeb7e..33d55b3 100644 --- a/app/src/main/java/com/thebrokenrail/mtudining/activity/list/ListActivity.java +++ b/app/src/main/java/com/thebrokenrail/mtudining/activity/list/ListActivity.java @@ -36,6 +36,7 @@ public class ListActivity extends AppCompatActivity { // Setup RecyclerView adapter = new ListAdapter(viewModel.task); + viewModel.task.sendDataToListeners(); RecyclerView recyclerView = findViewById(R.id.recycler_view); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(adapter); diff --git a/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/ItemDialog.java b/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/ItemDialog.java index 0f59dfa..a027f1c 100644 --- a/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/ItemDialog.java +++ b/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/ItemDialog.java @@ -61,7 +61,9 @@ public class ItemDialog { message.append("\n\n"); } message.append(context.getString(R.string.portion), new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - message.append(item.portion); + if (item.portion != null) { + message.append(item.portion); + } message.append('\n'); message.append(context.getString(R.string.ingredients), new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); if (item.ingredients != null) { 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 f3677e8..4206a2d 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 @@ -61,15 +61,14 @@ public class MenuActivity extends AppCompatActivity { String name = getIntent().getStringExtra(NAME_EXTRA); toolbar.setTitle(name); String id = getIntent().getStringExtra(ID_EXTRA); - if (!viewModel.task.isSetup()) { - viewModel.task.setup(id, new Date()); - } + viewModel.task.setup(id); latitude = getIntent().getDoubleExtra(LATITUDE_EXTRA, 0); longitude = getIntent().getDoubleExtra(LONGITUDE_EXTRA, 0); street = getIntent().getStringExtra(STREET_EXTRA); // Setup RecyclerView - adapter = new MenuAdapter(viewModel.task); + adapter = new MenuAdapter(viewModel.task, viewModel.state); + viewModel.task.sendDataToListeners(); RecyclerView recyclerView = findViewById(R.id.recycler_view); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(adapter); @@ -113,7 +112,7 @@ public class MenuActivity extends AppCompatActivity { * @param newDate New date */ public void updateDate(Date newDate) { - viewModel.task.setup(null, newDate); + viewModel.state.setDate(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 55f1404..0dcee5f 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 @@ -24,8 +24,14 @@ import java.util.Date; * Adapter for listing dining halls. */ class MenuAdapter extends TaskAdapter { - MenuAdapter(Task task) { + /** + * Current state. + */ + private final MenuState state; + + MenuAdapter(Task task, MenuState state) { super(task); + this.state = state; } @Override @@ -43,8 +49,9 @@ class MenuAdapter extends TaskAdapter { */ private MenuData.Meal getMeal() { if (getResult() != null) { + String selectedMeal = state.getSelectedMeal(); for (MenuData.Meal meal : getResult().meals) { - if (meal.name.equals(getResult().selectedMeal)) { + if (meal.name.equals(selectedMeal)) { // Found Meal return meal; } @@ -93,8 +100,7 @@ class MenuAdapter extends TaskAdapter { // 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 current = ((MenuTask) getTask()).getDate(); + Date current = state.getDate(); date.setText(DateUtil.toString(current)); // Handle Click dateField.setup(); @@ -104,26 +110,28 @@ class MenuAdapter extends TaskAdapter { }); // Set Meal - boolean hasMeal = getResult() != null; + boolean isLoaded = getResult() != null; TextInputLayout mealField = view.findViewById(R.id.menu_meal_field); MaterialAutoCompleteTextView meal = view.findViewById(R.id.menu_meal); - if (hasMeal) { + if (isLoaded) { // Enable/Disable Field - hasMeal = getResult().meals.size() > 0; - mealField.setEnabled(hasMeal); - if (hasMeal) { + isLoaded = getResult().meals.size() > 0; + mealField.setEnabled(isLoaded); + if (isLoaded) { // Set Dropdown Options meal.setSimpleItems(getResult().meals.stream().map(x -> x.name).toArray(String[]::new)); // Set Selection - meal.setText(getResult().selectedMeal, false); + meal.setText(state.getSelectedMeal(), 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); + if (!newMeal.equals(state.getSelectedMeal())) { + int oldItemCount = getItemCount(); + state.setSelectedMeal(newMeal); + reloadUI(oldItemCount); + } }); } else { // Show N/A 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 a596a3b..2a3983d 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 @@ -17,8 +17,5 @@ class MenuData { this.name = name; } } - public final List meals = new ArrayList<>(); - // This may be changed at runtime. - public String selectedMeal = null; } diff --git a/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuState.java b/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuState.java new file mode 100644 index 0000000..9dc71c3 --- /dev/null +++ b/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuState.java @@ -0,0 +1,61 @@ +package com.thebrokenrail.mtudining.activity.menu; + +import androidx.lifecycle.SavedStateHandle; + +import java.util.Date; + +/** + * Current state of menu screen. + */ +public class MenuState { + private static final String DATE_KEY = "date"; + private static final String SELECTED_MEAL_KEY = "selected_meal"; + + /** + * Data that should survive process-death. + */ + private final SavedStateHandle savedStateHandle; + + public MenuState(SavedStateHandle savedStateHandle) { + this.savedStateHandle = savedStateHandle; + if (!savedStateHandle.contains(DATE_KEY)) { + setDate(new Date()); + } + if (!savedStateHandle.contains(SELECTED_MEAL_KEY)) { + setSelectedMeal(""); + } + } + + /** + * Get currently selected date. + * @return The data + */ + @SuppressWarnings({"DataFlowIssue"}) + public Date getDate() { + return new Date((long) savedStateHandle.get(DATE_KEY)); + } + + /** + * Set selected date. + * @param date The new date + */ + public void setDate(Date date) { + savedStateHandle.set(DATE_KEY, date.getTime()); + } + + /** + * Get currently selected meal. + * @return The meal + */ + public String getSelectedMeal() { + return savedStateHandle.get(SELECTED_MEAL_KEY); + } + + /** + * Set selected meal. + * @param selectedMeal The new meal + */ + public void setSelectedMeal(String selectedMeal) { + savedStateHandle.set(SELECTED_MEAL_KEY, selectedMeal); + } +} 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 42beb46..6487089 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 @@ -8,7 +8,6 @@ import com.thebrokenrail.mtudining.util.Category; import com.thebrokenrail.mtudining.util.Constants; import java.util.Comparator; -import java.util.Date; /** * Task for loading menu information. @@ -17,44 +16,25 @@ public class MenuTask extends Task { private final Connection connection; private String locationId; - private Date date; + private final MenuState state; - public MenuTask(Connection connection) { + public MenuTask(Connection connection, MenuState state) { this.connection = connection; + this.state = state; } /** * Setup task. - * @param locationId Location ID (or null to use existing) - * @param date Date + * @param locationId Location ID */ - void setup(String locationId, Date date) { - if (locationId != null) { - this.locationId = locationId; - } - this.date = date; - } - - /** - * Check if task is setup. - * @return If the task is setup - */ - boolean isSetup() { - return locationId != null; - } - - /** - * Get date. - * @return The date - */ - public Date getDate() { - return date; + void setup(String locationId) { + this.locationId = locationId; } @Override protected void startImpl(long id) { // Get Periods - Periods periods = new Periods(Constants.PLATFORM, locationId, date); + Periods periods = new Periods(Constants.PLATFORM, locationId, state.getDate()); connection.send(periods, periodsResponse -> { // Loaded Periods @@ -69,7 +49,7 @@ public class MenuTask extends Task { 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); + PeriodDetail periodDetail = new PeriodDetail(Constants.PLATFORM, locationId, state.getDate(), period.id); connection.send(periodDetail, periodDetailResponse -> { // Loaded Period Details @@ -104,8 +84,6 @@ public class MenuTask extends Task { }); data.meals.add(meal); } - // Set Selected Meal - data.selectedMeal = data.meals.get(0).name; } else { // No Periods To Load done(id, data); @@ -115,4 +93,29 @@ public class MenuTask extends Task { done(id, null); }); } + + @Override + protected void done(long id, MenuData obj) { + // Update Selected Meal If Needed + if (obj != null && obj.meals.size() > 0) { + boolean currentSelectedMealIsValid = false; + String selectedMeal = state.getSelectedMeal(); + if (selectedMeal != null) { + // Check If Current Selection Is Valid + for (MenuData.Meal meal : obj.meals) { + if (selectedMeal.equals(meal.name)) { + currentSelectedMealIsValid = true; + break; + } + } + } + if (!currentSelectedMealIsValid) { + // Current Selection Is Invalid, Change It + state.setSelectedMeal(obj.meals.get(0).name); + } + } + + // Call Parent + super.done(id, obj); + } } diff --git a/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuViewModel.java b/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuViewModel.java index ad58d19..532b541 100644 --- a/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuViewModel.java +++ b/app/src/main/java/com/thebrokenrail/mtudining/activity/menu/MenuViewModel.java @@ -1,8 +1,8 @@ package com.thebrokenrail.mtudining.activity.menu; +import androidx.lifecycle.SavedStateHandle; import androidx.lifecycle.ViewModel; -import com.thebrokenrail.mtudining.activity.task.Task; import com.thebrokenrail.mtudining.api.Connection; /** @@ -10,5 +10,11 @@ import com.thebrokenrail.mtudining.api.Connection; */ public class MenuViewModel extends ViewModel { private final Connection connection = new Connection(); - public final MenuTask task = new MenuTask(connection); + public final MenuState state; + public final MenuTask task; + + public MenuViewModel(SavedStateHandle savedStateHandle) { + state = new MenuState(savedStateHandle); + task = new MenuTask(connection, state); + } } 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 31f990c..879132c 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 @@ -29,6 +29,7 @@ public abstract class Task { public void start() { lastStart = System.currentTimeMillis(); status = Status.IN_PROGRESS; + result = null; startImpl(lastStart); // Update Listeners sendDataToListeners(); @@ -95,25 +96,15 @@ public abstract class Task { public void addListener(Listener listener) { if (!listeners.contains(listener)) { listeners.add(listener); - // Send Data - sendDataToListener(listener); } } - /** - * Send data to specific listener. - * @param listener The listener - */ - private void sendDataToListener(Listener listener) { - listener.update(status, status == Status.DONE ? result : null); - } - /** * Send data to all listeners. */ - private void sendDataToListeners() { + public void sendDataToListeners() { for (Listener listener : listeners) { - sendDataToListener(listener); + listener.update(status, status == Status.DONE ? result : 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 6cc1990..5ec44fd 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 @@ -208,12 +208,4 @@ public abstract class TaskAdapter extends RecyclerView.Adapter getTask() { - return task; - } }