End Of Day 1
This commit is contained in:
parent
eef05a9883
commit
5bfc54ccd8
@ -17,13 +17,16 @@
|
||||
|
||||
<activity
|
||||
android:name=".activity.list.ListActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.MTUDining">
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.menu.MenuActivity"
|
||||
android:exported="false" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -1,9 +0,0 @@
|
||||
package com.thebrokenrail.mtudining.activity;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
/**
|
||||
* This activity lists the menu for a given dining hall.
|
||||
*/
|
||||
public class MenuActivity extends AppCompatActivity {
|
||||
}
|
@ -14,7 +14,7 @@ import com.thebrokenrail.mtudining.R;
|
||||
import com.thebrokenrail.mtudining.util.EdgeToEdgeUtil;
|
||||
|
||||
/**
|
||||
* This activity lists the available food halls.
|
||||
* This activity lists the available dining halls.
|
||||
*/
|
||||
public class ListActivity extends AppCompatActivity {
|
||||
private ListAdapter adapter;
|
||||
@ -41,4 +41,10 @@ public class ListActivity extends AppCompatActivity {
|
||||
recyclerView.setAdapter(adapter);
|
||||
EdgeToEdgeUtil.setup(this, recyclerView);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
adapter.onDestroy();
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,13 @@
|
||||
package com.thebrokenrail.mtudining.activity.list;
|
||||
|
||||
import android.util.TypedValue;
|
||||
import android.content.Intent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.thebrokenrail.mtudining.R;
|
||||
import com.thebrokenrail.mtudining.activity.menu.MenuActivity;
|
||||
import com.thebrokenrail.mtudining.activity.task.Task;
|
||||
import com.thebrokenrail.mtudining.activity.task.TaskAdapter;
|
||||
import com.thebrokenrail.mtudining.widget.CategoryView;
|
||||
@ -17,60 +15,40 @@ import com.thebrokenrail.mtudining.widget.CategoryView;
|
||||
/**
|
||||
* Adapter for listing dining halls.
|
||||
*/
|
||||
public class ListAdapter extends TaskAdapter<ListData, ListAdapter.ViewHolder> {
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
private final CategoryView view;
|
||||
private ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
view = (CategoryView) itemView;
|
||||
}
|
||||
}
|
||||
|
||||
public ListAdapter(Task<ListData> task) {
|
||||
class ListAdapter extends TaskAdapter<ListData> {
|
||||
ListAdapter(Task<ListData> task) {
|
||||
super(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ViewHolder createItemViewHolder(@NonNull ViewGroup parent) {
|
||||
protected View createItemView(@NonNull ViewGroup parent) {
|
||||
// Create View
|
||||
CategoryView category = new CategoryView(parent.getContext(), null);
|
||||
RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT);
|
||||
category.setLayoutParams(layoutParams);
|
||||
return new ViewHolder(category);
|
||||
return category;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bindDataViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
ListData.Category data = getResult().categories.get(position);
|
||||
protected void bindItemView(View view, int position) {
|
||||
ListData.Category data = getResult().categories.get(position - getFirstElementPosition());
|
||||
// Setup View
|
||||
holder.view.setup(data.isOpen, data.name, () -> {
|
||||
CategoryView category = (CategoryView) view;
|
||||
category.setup(data.isOpen, data.name, () -> {
|
||||
// Open/Close Category
|
||||
data.isOpen = !data.isOpen;
|
||||
notifyItemChanged(getResult().categories.indexOf(data));
|
||||
notifyItemChanged(getResult().categories.indexOf(data) + getFirstElementPosition());
|
||||
});
|
||||
// Add Locations
|
||||
holder.view.children.removeAllViews();
|
||||
for (ListData.Element location : data.locations) {
|
||||
AppCompatTextView item = new AppCompatTextView(holder.view.getContext());
|
||||
// Text
|
||||
item.setText(location.name);
|
||||
// Text Size
|
||||
item.setTextSize(TypedValue.COMPLEX_UNIT_PX, item.getResources().getDimension(R.dimen.item_font_size));
|
||||
// Padding
|
||||
int margin = holder.view.getResources().getDimensionPixelSize(R.dimen.margin);
|
||||
item.setPadding(margin, margin, margin, margin);
|
||||
// Make Clickable
|
||||
item.setClickable(true);
|
||||
item.setFocusable(true);
|
||||
// Ripple Effect
|
||||
TypedValue outValue = new TypedValue();
|
||||
holder.view.getContext().getTheme().resolveAttribute(androidx.appcompat.R.attr.selectableItemBackground, outValue, true);
|
||||
item.setBackgroundResource(outValue.resourceId);
|
||||
// Layout
|
||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
item.setLayoutParams(layoutParams);
|
||||
// Add To View
|
||||
holder.view.children.addView(item);
|
||||
category.clearItems();
|
||||
for (ListData.Category.Element location : data.locations) {
|
||||
category.addItem(location.name, () -> {
|
||||
// Open Menu
|
||||
Intent intent = new Intent(category.getContext(), MenuActivity.class);
|
||||
intent.putExtra(MenuActivity.ID_EXTRA, location.id);
|
||||
intent.putExtra(MenuActivity.NAME_EXTRA, location.name);
|
||||
category.getContext().startActivity(intent);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,8 @@ import java.util.List;
|
||||
/**
|
||||
* Data to be displayed in {@link ListActivity}.
|
||||
*/
|
||||
public class ListData {
|
||||
class ListData {
|
||||
public static class Category {
|
||||
public static class Element {
|
||||
public final String id;
|
||||
public final String name;
|
||||
@ -17,7 +18,6 @@ public class ListData {
|
||||
}
|
||||
}
|
||||
|
||||
public static class Category {
|
||||
public final String name;
|
||||
public boolean isOpen = true;
|
||||
public final List<Element> locations = new ArrayList<>();
|
||||
@ -30,7 +30,7 @@ public class ListData {
|
||||
public final String siteId;
|
||||
public final List<Category> categories = new ArrayList<>();
|
||||
|
||||
public ListData(String siteId) {
|
||||
ListData(String siteId) {
|
||||
this.siteId = siteId;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,56 @@
|
||||
package com.thebrokenrail.mtudining.activity.list;
|
||||
|
||||
import com.thebrokenrail.mtudining.activity.task.Task;
|
||||
import com.thebrokenrail.mtudining.api.Connection;
|
||||
import com.thebrokenrail.mtudining.api.method.AllLocations;
|
||||
import com.thebrokenrail.mtudining.api.method.Info;
|
||||
import com.thebrokenrail.mtudining.util.Constants;
|
||||
|
||||
/**
|
||||
* Task for retrieving site ID and list of dining halls.
|
||||
*/
|
||||
class ListTask extends Task<ListData> {
|
||||
private final Connection connection;
|
||||
|
||||
ListTask(Connection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startImpl(long id) {
|
||||
// Load Site Info
|
||||
Info info = new Info();
|
||||
connection.send(info, infoResponse -> {
|
||||
// Load Locations
|
||||
AllLocations allLocations = new AllLocations(Constants.PLATFORM, infoResponse.site.id, true, false, true);
|
||||
connection.send(allLocations, allLocationsResponse -> {
|
||||
// Success
|
||||
ListData data = new ListData(infoResponse.site.id);
|
||||
// Find Active Locations
|
||||
for (AllLocations.Response.Building building : allLocationsResponse.buildings) {
|
||||
if (building.active) {
|
||||
// Found Active Building
|
||||
ListData.Category category = new ListData.Category(building.name);
|
||||
for (AllLocations.Response.Location location : building.locations) {
|
||||
if (location.active) {
|
||||
// Found Active Location
|
||||
category.locations.add(new ListData.Category.Element(location.id, location.name));
|
||||
}
|
||||
}
|
||||
// Skip Empty Category
|
||||
if (category.locations.size() > 0) {
|
||||
data.categories.add(category);
|
||||
}
|
||||
}
|
||||
}
|
||||
done(id, data);
|
||||
}, () -> {
|
||||
// Failed Fetching Location Info
|
||||
done(id, null);
|
||||
});
|
||||
}, () -> {
|
||||
// Failed Fetching Site Info
|
||||
done(id, null);
|
||||
});
|
||||
}
|
||||
}
|
@ -4,52 +4,11 @@ import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.thebrokenrail.mtudining.activity.task.Task;
|
||||
import com.thebrokenrail.mtudining.api.Connection;
|
||||
import com.thebrokenrail.mtudining.api.method.AllLocations;
|
||||
import com.thebrokenrail.mtudining.api.method.Info;
|
||||
import com.thebrokenrail.mtudining.util.Constants;
|
||||
|
||||
/**
|
||||
* Data preserved between screen rotations for {@link ListActivity}.
|
||||
*/
|
||||
public class ListViewModel extends ViewModel {
|
||||
private final Connection connection = new Connection();
|
||||
public final Task<ListData> task = new Task<ListData>() {
|
||||
@Override
|
||||
protected void startImpl() {
|
||||
// Load Site Info
|
||||
Info info = new Info();
|
||||
connection.send(info, infoResponse -> {
|
||||
// Load Locations
|
||||
AllLocations allLocations = new AllLocations(Constants.PLATFORM, infoResponse.site.id, true, false, true);
|
||||
connection.send(allLocations, allLocationsResponse -> {
|
||||
// Success
|
||||
ListData data = new ListData(infoResponse.site.id);
|
||||
// Find Active Locations
|
||||
for (AllLocations.Response.Building building : allLocationsResponse.buildings) {
|
||||
if (building.active) {
|
||||
// Found Active Building
|
||||
ListData.Category category = new ListData.Category(building.name);
|
||||
for (AllLocations.Response.Location location : building.locations) {
|
||||
if (location.active) {
|
||||
// Found Active Location
|
||||
category.locations.add(new ListData.Element(location.id, location.name));
|
||||
}
|
||||
}
|
||||
// Skip Empty Category
|
||||
if (category.locations.size() > 0) {
|
||||
data.categories.add(category);
|
||||
}
|
||||
}
|
||||
}
|
||||
done(data);
|
||||
}, () -> {
|
||||
// Failed Fetching Location Info
|
||||
done(null);
|
||||
});
|
||||
}, () -> {
|
||||
// Failed Fetching Site Info
|
||||
done(null);
|
||||
});
|
||||
}
|
||||
};
|
||||
public final Task<ListData> task = new ListTask(connection);
|
||||
}
|
||||
|
@ -0,0 +1,76 @@
|
||||
package com.thebrokenrail.mtudining.activity.menu;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.appbar.MaterialToolbar;
|
||||
import com.thebrokenrail.mtudining.R;
|
||||
import com.thebrokenrail.mtudining.util.EdgeToEdgeUtil;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* This activity lists the menu for a dining hall.
|
||||
*/
|
||||
public class MenuActivity extends AppCompatActivity {
|
||||
public static final String NAME_EXTRA = "mtu_name";
|
||||
public static final String ID_EXTRA = "mtu_id";
|
||||
|
||||
private MenuAdapter adapter;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
// Setup UI
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.activity_list);
|
||||
|
||||
// Toolbar
|
||||
MaterialToolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
// Get View Model
|
||||
ViewModelProvider viewModelProvider = new ViewModelProvider(this);
|
||||
MenuViewModel 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());
|
||||
|
||||
// Setup RecyclerView
|
||||
adapter = new MenuAdapter(viewModel.task);
|
||||
RecyclerView recyclerView = findViewById(R.id.recycler_view);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
recyclerView.setAdapter(adapter);
|
||||
EdgeToEdgeUtil.setup(this, recyclerView);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
adapter.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
// Make back button in toolbar act as normal back button.
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
getOnBackPressedDispatcher().onBackPressed();
|
||||
return true;
|
||||
} else {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package com.thebrokenrail.mtudining.activity.menu;
|
||||
|
||||
import android.text.InputType;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Adapter for listing dining halls.
|
||||
*/
|
||||
class MenuAdapter extends TaskAdapter<MenuData> {
|
||||
MenuAdapter(Task<MenuData> task) {
|
||||
super(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View createItemView(@NonNull ViewGroup parent) {
|
||||
// Create View
|
||||
CategoryView category = new CategoryView(parent.getContext(), null);
|
||||
RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT);
|
||||
category.setLayoutParams(layoutParams);
|
||||
return category;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bindItemView(View view, int position) {
|
||||
/*ListData.Category data = getResult().categories.get(position);
|
||||
// Setup View
|
||||
CategoryView category = (CategoryView) view;
|
||||
category.setup(data.isOpen, data.name, () -> {
|
||||
// Open/Close Category
|
||||
data.isOpen = !data.isOpen;
|
||||
notifyItemChanged(getResult().categories.indexOf(data));
|
||||
});
|
||||
// Add Locations
|
||||
category.clearItems();
|
||||
for (ListData.Category.Element location : data.locations) {
|
||||
category.addItem(location.name, () -> {
|
||||
// Do Something!
|
||||
});
|
||||
}*/
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDataSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View createHeader(@NonNull ViewGroup parent) {
|
||||
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
|
||||
return layoutInflater.inflate(R.layout.menu_header, parent, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bindHeader(View view) {
|
||||
// Set Date
|
||||
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()));
|
||||
|
||||
// Set Meal
|
||||
boolean hasMeal = getResult() != null;
|
||||
TextInputLayout meal = view.findViewById(R.id.menu_meal_field);
|
||||
meal.setEnabled(hasMeal);
|
||||
if (hasMeal) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package com.thebrokenrail.mtudining.activity.menu;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
|
||||
public final String name;
|
||||
public boolean isOpen = true;
|
||||
public final List<Element> locations = new ArrayList<>();
|
||||
|
||||
public Category(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public final String id;
|
||||
public final String name;
|
||||
public final List<Category> categories = new ArrayList<>();
|
||||
|
||||
public Meal(String id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public final List<Meal> meals = new ArrayList<>();
|
||||
public String selectedMeal;
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
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.Periods;
|
||||
import com.thebrokenrail.mtudining.util.Constants;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class MenuTask extends Task<MenuData> {
|
||||
private final Connection connection;
|
||||
|
||||
private String locationId;
|
||||
private Date date;
|
||||
|
||||
public MenuTask(Connection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup task.
|
||||
* @param locationId Location ID (or null to use existing)
|
||||
* @param date Date
|
||||
*/
|
||||
void setup(String locationId, Date date) {
|
||||
if (locationId != null) {
|
||||
this.locationId = locationId;
|
||||
}
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get date.
|
||||
* @return The date
|
||||
*/
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startImpl(long id) {
|
||||
// Get Periods
|
||||
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);
|
||||
}
|
||||
done(id, null);
|
||||
}, () -> {
|
||||
// Error
|
||||
done(id, null);
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.thebrokenrail.mtudining.activity.menu;
|
||||
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.thebrokenrail.mtudining.activity.task.Task;
|
||||
import com.thebrokenrail.mtudining.api.Connection;
|
||||
|
||||
/**
|
||||
* Data preserved between screen rotations for {@link MenuActivity}.
|
||||
*/
|
||||
public class MenuViewModel extends ViewModel {
|
||||
private final Connection connection = new Connection();
|
||||
public final MenuTask task = new MenuTask(connection);
|
||||
}
|
@ -18,19 +18,26 @@ public abstract class Task<E> {
|
||||
}
|
||||
private Status status = Status.NOT_STARTED;
|
||||
|
||||
/**
|
||||
* Last time when {@link #start()} was called.
|
||||
*/
|
||||
private long lastStart;
|
||||
|
||||
/**
|
||||
* Start task.
|
||||
*/
|
||||
public void start() {
|
||||
lastStart = System.currentTimeMillis();
|
||||
status = Status.IN_PROGRESS;
|
||||
startImpl();
|
||||
startImpl(lastStart);
|
||||
// Update Listeners
|
||||
sendDataToListeners();
|
||||
}
|
||||
/**
|
||||
* Implementation of {@link #start()}.
|
||||
* @param id Unique id for each call of {@link #start()}
|
||||
*/
|
||||
protected abstract void startImpl();
|
||||
protected abstract void startImpl(long id);
|
||||
|
||||
/**
|
||||
* Currently stored result (or null if none).
|
||||
@ -39,9 +46,16 @@ public abstract class Task<E> {
|
||||
|
||||
/**
|
||||
* Store result.
|
||||
* @param id ID from {@link #startImpl(long)}
|
||||
* @param obj Result (or null if error)
|
||||
*/
|
||||
protected void done(E obj) {
|
||||
protected void done(long id, E obj) {
|
||||
// Check ID
|
||||
if (id != lastStart) {
|
||||
// Ignore
|
||||
return;
|
||||
}
|
||||
// Store
|
||||
result = obj;
|
||||
// Update Status
|
||||
if (obj == null) {
|
||||
|
@ -1,18 +1,21 @@
|
||||
package com.thebrokenrail.mtudining.activity.task;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.thebrokenrail.mtudining.R;
|
||||
import com.thebrokenrail.mtudining.widget.LoaderView;
|
||||
|
||||
/**
|
||||
* RecyclerView adapter for a given {@link Task}.
|
||||
*/
|
||||
public abstract class TaskAdapter<E, VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements Task.Listener<E> {
|
||||
public abstract class TaskAdapter<E> extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements Task.Listener<E> {
|
||||
public static final int LOADER_TYPE = 1;
|
||||
public static final int DATA_TYPE = 0;
|
||||
public static final int ITEM_TYPE = 0;
|
||||
public static final int HEADER_TYPE = 2;
|
||||
|
||||
/**
|
||||
* The task.
|
||||
@ -56,11 +59,25 @@ public abstract class TaskAdapter<E, VH extends RecyclerView.ViewHolder> extends
|
||||
}
|
||||
|
||||
/**
|
||||
* Create view holder for item in task's result.
|
||||
* Create view for item in task's result.
|
||||
* @param parent The view parent
|
||||
* @return The new view holder
|
||||
* @return The new view
|
||||
*/
|
||||
protected abstract VH createItemViewHolder(@NonNull ViewGroup parent);
|
||||
protected abstract View createItemView(@NonNull ViewGroup parent);
|
||||
|
||||
/**
|
||||
* Create view for header.
|
||||
* @param parent The view parent
|
||||
* @return The new view
|
||||
*/
|
||||
protected View createHeader(@NonNull ViewGroup parent) {
|
||||
// Just Create Margin
|
||||
View view = new View(parent.getContext());
|
||||
int margin = parent.getResources().getDimensionPixelSize(R.dimen.margin);
|
||||
RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, margin);
|
||||
view.setLayoutParams(layoutParams);
|
||||
return view;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
@ -71,9 +88,12 @@ public abstract class TaskAdapter<E, VH extends RecyclerView.ViewHolder> extends
|
||||
RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT);
|
||||
loader.setLayoutParams(layoutParams);
|
||||
return new RecyclerView.ViewHolder(loader) {};
|
||||
} else if (viewType == DATA_TYPE) {
|
||||
} else if (viewType == ITEM_TYPE) {
|
||||
// Data
|
||||
return createItemViewHolder(parent);
|
||||
return new RecyclerView.ViewHolder(createItemView(parent)) {};
|
||||
} else if (viewType == HEADER_TYPE) {
|
||||
// Header
|
||||
return new RecyclerView.ViewHolder(createHeader(parent)) {};
|
||||
} else {
|
||||
// Unknown
|
||||
throw new RuntimeException();
|
||||
@ -81,22 +101,31 @@ public abstract class TaskAdapter<E, VH extends RecyclerView.ViewHolder> extends
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind view holder for item in task's result.
|
||||
* @param holder The view holder
|
||||
* Bind view for item in task's result.
|
||||
* @param view The view
|
||||
* @param position The item's position
|
||||
*/
|
||||
protected abstract void bindDataViewHolder(@NonNull VH holder, int position);
|
||||
protected abstract void bindItemView(View view, int position);
|
||||
|
||||
/**
|
||||
* Bind view for header.
|
||||
* @param view The view
|
||||
*/
|
||||
protected void bindHeader(View view) {
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||
int type = getItemViewType(position);
|
||||
if (type == LOADER_TYPE) {
|
||||
// Loader
|
||||
((LoaderView) holder.itemView).setup(currentStatus == Task.Status.ERROR, task::start);
|
||||
} else if (type == DATA_TYPE) {
|
||||
} else if (type == ITEM_TYPE) {
|
||||
// Data
|
||||
bindDataViewHolder((VH) holder, position);
|
||||
bindItemView(holder.itemView, position);
|
||||
} else if (type == HEADER_TYPE) {
|
||||
// Header
|
||||
bindHeader(holder.itemView);
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,23 +137,39 @@ public abstract class TaskAdapter<E, VH extends RecyclerView.ViewHolder> extends
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
int size;
|
||||
if (currentStatus != Task.Status.DONE) {
|
||||
// Only Loader
|
||||
return 1;
|
||||
size = 1;
|
||||
} else {
|
||||
// Data Size
|
||||
return getDataSize();
|
||||
size = getDataSize();
|
||||
}
|
||||
// Header
|
||||
size++;
|
||||
// Return
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get position of first non-header element.
|
||||
* @return Element position
|
||||
*/
|
||||
public int getFirstElementPosition() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (currentStatus != Task.Status.DONE && position == 0) {
|
||||
if (currentStatus != Task.Status.DONE && position == getFirstElementPosition()) {
|
||||
// Loader
|
||||
return LOADER_TYPE;
|
||||
} else if (position == 0) {
|
||||
// Header
|
||||
return HEADER_TYPE;
|
||||
} else {
|
||||
// Data
|
||||
return DATA_TYPE;
|
||||
// Item
|
||||
return ITEM_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,4 +205,12 @@ public abstract class TaskAdapter<E, VH extends RecyclerView.ViewHolder> extends
|
||||
// Add Items
|
||||
notifyItemRangeInserted(0, getItemCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get task.
|
||||
* @return The task
|
||||
*/
|
||||
public Task<E> getTask() {
|
||||
return task;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,38 @@
|
||||
package com.thebrokenrail.mtudining.api.method;
|
||||
|
||||
import com.thebrokenrail.mtudining.api.Method;
|
||||
import com.thebrokenrail.mtudining.util.DateUtil;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class Periods implements Method<Periods.Response> {
|
||||
private final int platform;
|
||||
private final String locationId;
|
||||
private final String date;
|
||||
|
||||
public Periods(int platform, String locationId, Date date) {
|
||||
this.platform = platform;
|
||||
this.locationId = locationId;
|
||||
this.date = DateUtil.toString(date);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return "/location/" + locationId + "/periods?platform=" + platform + "&date=" + date;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<Response> getResponseClass() {
|
||||
return Response.class;
|
||||
}
|
||||
|
||||
public static class Response {
|
||||
public static class Period {
|
||||
public String id;
|
||||
public int sort_order;
|
||||
public String name;
|
||||
}
|
||||
public List<Period> periods;
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.thebrokenrail.mtudining.util;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
public class DateUtil {
|
||||
public static String toString(Date date) {
|
||||
DateFormat df = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
|
||||
return df.format(date);
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@ import com.thebrokenrail.mtudining.R;
|
||||
*/
|
||||
public class CategoryView extends FrameLayout {
|
||||
private final MaterialCardView card;
|
||||
public final LinearLayout children;
|
||||
private final LinearLayout children;
|
||||
private final AppCompatTextView title;
|
||||
|
||||
public CategoryView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
@ -31,7 +31,7 @@ public class CategoryView extends FrameLayout {
|
||||
// Set Margin
|
||||
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||
int margin = getResources().getDimensionPixelSize(R.dimen.margin);
|
||||
layoutParams.setMargins(margin, margin, margin, margin);
|
||||
layoutParams.setMargins(margin, 0, margin, margin);
|
||||
inner.setLayoutParams(layoutParams);
|
||||
|
||||
// Add Title
|
||||
@ -74,4 +74,40 @@ public class CategoryView extends FrameLayout {
|
||||
card.setVisibility(isOpen ? VISIBLE : GONE);
|
||||
title.setOnClickListener(v -> onClickTitle.run());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear category.
|
||||
*/
|
||||
public void clearItems() {
|
||||
children.removeAllViews();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add item.
|
||||
* @param name Item name
|
||||
* @param onClick Click handler
|
||||
*/
|
||||
public void addItem(String name, Runnable onClick) {
|
||||
AppCompatTextView item = new AppCompatTextView(getContext());
|
||||
// Text
|
||||
item.setText(name);
|
||||
// Text Size
|
||||
item.setTextSize(TypedValue.COMPLEX_UNIT_PX, item.getResources().getDimension(R.dimen.item_font_size));
|
||||
// Padding
|
||||
int margin = getResources().getDimensionPixelSize(R.dimen.margin);
|
||||
item.setPadding(margin, margin, margin, margin);
|
||||
// Make Clickable
|
||||
item.setClickable(true);
|
||||
item.setFocusable(true);
|
||||
item.setOnClickListener(v -> onClick.run());
|
||||
// Ripple Effect
|
||||
TypedValue outValue = new TypedValue();
|
||||
getContext().getTheme().resolveAttribute(androidx.appcompat.R.attr.selectableItemBackground, outValue, true);
|
||||
item.setBackgroundResource(outValue.resourceId);
|
||||
// Layout
|
||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
item.setLayoutParams(layoutParams);
|
||||
// Add To View
|
||||
children.addView(item);
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ public class LoaderView extends LinearLayout {
|
||||
// Set Margin
|
||||
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||
int margin = getResources().getDimensionPixelSize(R.dimen.margin);
|
||||
layoutParams.setMargins(margin, margin, margin, margin);
|
||||
layoutParams.setMargins(margin, 0, margin, margin);
|
||||
inner.setLayoutParams(layoutParams);
|
||||
}
|
||||
|
||||
|
45
app/src/main/res/layout/menu_header.xml
Normal file
45
app/src/main/res/layout/menu_header.xml
Normal file
@ -0,0 +1,45 @@
|
||||
<?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"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- Date -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/menu_date_field"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/margin"
|
||||
android:hint="@string/date"
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
app:endIconMode="none">
|
||||
|
||||
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||
android:id="@+id/menu_date"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<!-- Meal -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/menu_meal_field"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/margin"
|
||||
android:layout_marginStart="@dimen/margin"
|
||||
android:layout_marginEnd="@dimen/margin"
|
||||
android:hint="@string/meal"
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu">
|
||||
|
||||
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||
android:id="@+id/menu_meal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</LinearLayout>
|
@ -3,4 +3,6 @@
|
||||
<string name="app_name">Dining[MTU]</string>
|
||||
<string name="retry">Retry</string>
|
||||
<string name="load_error">Unable to load data!</string>
|
||||
<string name="date">Date</string>
|
||||
<string name="meal">Meal</string>
|
||||
</resources>
|
Loading…
Reference in New Issue
Block a user