End Of Day 1
This commit is contained in:
parent
eef05a9883
commit
5bfc54ccd8
@ -17,13 +17,16 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.list.ListActivity"
|
android:name=".activity.list.ListActivity"
|
||||||
android:exported="true"
|
android:exported="true">
|
||||||
android:theme="@style/Theme.MTUDining">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".activity.menu.MenuActivity"
|
||||||
|
android:exported="false" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</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;
|
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 {
|
public class ListActivity extends AppCompatActivity {
|
||||||
private ListAdapter adapter;
|
private ListAdapter adapter;
|
||||||
@ -41,4 +41,10 @@ public class ListActivity extends AppCompatActivity {
|
|||||||
recyclerView.setAdapter(adapter);
|
recyclerView.setAdapter(adapter);
|
||||||
EdgeToEdgeUtil.setup(this, recyclerView);
|
EdgeToEdgeUtil.setup(this, recyclerView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
adapter.onDestroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
package com.thebrokenrail.mtudining.activity.list;
|
package com.thebrokenrail.mtudining.activity.list;
|
||||||
|
|
||||||
import android.util.TypedValue;
|
import android.content.Intent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.LinearLayout;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.widget.AppCompatTextView;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
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.Task;
|
||||||
import com.thebrokenrail.mtudining.activity.task.TaskAdapter;
|
import com.thebrokenrail.mtudining.activity.task.TaskAdapter;
|
||||||
import com.thebrokenrail.mtudining.widget.CategoryView;
|
import com.thebrokenrail.mtudining.widget.CategoryView;
|
||||||
@ -17,60 +15,40 @@ import com.thebrokenrail.mtudining.widget.CategoryView;
|
|||||||
/**
|
/**
|
||||||
* Adapter for listing dining halls.
|
* Adapter for listing dining halls.
|
||||||
*/
|
*/
|
||||||
public class ListAdapter extends TaskAdapter<ListData, ListAdapter.ViewHolder> {
|
class ListAdapter extends TaskAdapter<ListData> {
|
||||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
ListAdapter(Task<ListData> task) {
|
||||||
private final CategoryView view;
|
|
||||||
private ViewHolder(@NonNull View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
view = (CategoryView) itemView;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ListAdapter(Task<ListData> task) {
|
|
||||||
super(task);
|
super(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ViewHolder createItemViewHolder(@NonNull ViewGroup parent) {
|
protected View createItemView(@NonNull ViewGroup parent) {
|
||||||
// Create View
|
// Create View
|
||||||
CategoryView category = new CategoryView(parent.getContext(), null);
|
CategoryView category = new CategoryView(parent.getContext(), null);
|
||||||
RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT);
|
RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT);
|
||||||
category.setLayoutParams(layoutParams);
|
category.setLayoutParams(layoutParams);
|
||||||
return new ViewHolder(category);
|
return category;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void bindDataViewHolder(@NonNull ViewHolder holder, int position) {
|
protected void bindItemView(View view, int position) {
|
||||||
ListData.Category data = getResult().categories.get(position);
|
ListData.Category data = getResult().categories.get(position - getFirstElementPosition());
|
||||||
// Setup View
|
// Setup View
|
||||||
holder.view.setup(data.isOpen, data.name, () -> {
|
CategoryView category = (CategoryView) view;
|
||||||
|
category.setup(data.isOpen, data.name, () -> {
|
||||||
// Open/Close Category
|
// Open/Close Category
|
||||||
data.isOpen = !data.isOpen;
|
data.isOpen = !data.isOpen;
|
||||||
notifyItemChanged(getResult().categories.indexOf(data));
|
notifyItemChanged(getResult().categories.indexOf(data) + getFirstElementPosition());
|
||||||
});
|
});
|
||||||
// Add Locations
|
// Add Locations
|
||||||
holder.view.children.removeAllViews();
|
category.clearItems();
|
||||||
for (ListData.Element location : data.locations) {
|
for (ListData.Category.Element location : data.locations) {
|
||||||
AppCompatTextView item = new AppCompatTextView(holder.view.getContext());
|
category.addItem(location.name, () -> {
|
||||||
// Text
|
// Open Menu
|
||||||
item.setText(location.name);
|
Intent intent = new Intent(category.getContext(), MenuActivity.class);
|
||||||
// Text Size
|
intent.putExtra(MenuActivity.ID_EXTRA, location.id);
|
||||||
item.setTextSize(TypedValue.COMPLEX_UNIT_PX, item.getResources().getDimension(R.dimen.item_font_size));
|
intent.putExtra(MenuActivity.NAME_EXTRA, location.name);
|
||||||
// Padding
|
category.getContext().startActivity(intent);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,18 +6,18 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* Data to be displayed in {@link ListActivity}.
|
* Data to be displayed in {@link ListActivity}.
|
||||||
*/
|
*/
|
||||||
public class ListData {
|
class ListData {
|
||||||
public static class Element {
|
|
||||||
public final String id;
|
|
||||||
public final String name;
|
|
||||||
|
|
||||||
public Element(String id, String name) {
|
|
||||||
this.id = id;
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Category {
|
public static class Category {
|
||||||
|
public static class Element {
|
||||||
|
public final String id;
|
||||||
|
public final String name;
|
||||||
|
|
||||||
|
public Element(String id, String name) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final String name;
|
public final String name;
|
||||||
public boolean isOpen = true;
|
public boolean isOpen = true;
|
||||||
public final List<Element> locations = new ArrayList<>();
|
public final List<Element> locations = new ArrayList<>();
|
||||||
@ -30,7 +30,7 @@ public class ListData {
|
|||||||
public final String siteId;
|
public final String siteId;
|
||||||
public final List<Category> categories = new ArrayList<>();
|
public final List<Category> categories = new ArrayList<>();
|
||||||
|
|
||||||
public ListData(String siteId) {
|
ListData(String siteId) {
|
||||||
this.siteId = 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.activity.task.Task;
|
||||||
import com.thebrokenrail.mtudining.api.Connection;
|
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}.
|
* Data preserved between screen rotations for {@link ListActivity}.
|
||||||
*/
|
*/
|
||||||
public class ListViewModel extends ViewModel {
|
public class ListViewModel extends ViewModel {
|
||||||
private final Connection connection = new Connection();
|
private final Connection connection = new Connection();
|
||||||
public final Task<ListData> task = new Task<ListData>() {
|
public final Task<ListData> task = new ListTask(connection);
|
||||||
@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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
private Status status = Status.NOT_STARTED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last time when {@link #start()} was called.
|
||||||
|
*/
|
||||||
|
private long lastStart;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start task.
|
* Start task.
|
||||||
*/
|
*/
|
||||||
public void start() {
|
public void start() {
|
||||||
|
lastStart = System.currentTimeMillis();
|
||||||
status = Status.IN_PROGRESS;
|
status = Status.IN_PROGRESS;
|
||||||
startImpl();
|
startImpl(lastStart);
|
||||||
// Update Listeners
|
// Update Listeners
|
||||||
sendDataToListeners();
|
sendDataToListeners();
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Implementation of {@link #start()}.
|
* 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).
|
* Currently stored result (or null if none).
|
||||||
@ -39,9 +46,16 @@ public abstract class Task<E> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Store result.
|
* Store result.
|
||||||
|
* @param id ID from {@link #startImpl(long)}
|
||||||
* @param obj Result (or null if error)
|
* @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;
|
result = obj;
|
||||||
// Update Status
|
// Update Status
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
package com.thebrokenrail.mtudining.activity.task;
|
package com.thebrokenrail.mtudining.activity.task;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.thebrokenrail.mtudining.R;
|
||||||
import com.thebrokenrail.mtudining.widget.LoaderView;
|
import com.thebrokenrail.mtudining.widget.LoaderView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RecyclerView adapter for a given {@link Task}.
|
* 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 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.
|
* 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
|
* @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
|
@NonNull
|
||||||
@Override
|
@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);
|
RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT);
|
||||||
loader.setLayoutParams(layoutParams);
|
loader.setLayoutParams(layoutParams);
|
||||||
return new RecyclerView.ViewHolder(loader) {};
|
return new RecyclerView.ViewHolder(loader) {};
|
||||||
} else if (viewType == DATA_TYPE) {
|
} else if (viewType == ITEM_TYPE) {
|
||||||
// Data
|
// Data
|
||||||
return createItemViewHolder(parent);
|
return new RecyclerView.ViewHolder(createItemView(parent)) {};
|
||||||
|
} else if (viewType == HEADER_TYPE) {
|
||||||
|
// Header
|
||||||
|
return new RecyclerView.ViewHolder(createHeader(parent)) {};
|
||||||
} else {
|
} else {
|
||||||
// Unknown
|
// Unknown
|
||||||
throw new RuntimeException();
|
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.
|
* Bind view for item in task's result.
|
||||||
* @param holder The view holder
|
* @param view The view
|
||||||
* @param position The item's position
|
* @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
|
@Override
|
||||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||||
int type = getItemViewType(position);
|
int type = getItemViewType(position);
|
||||||
if (type == LOADER_TYPE) {
|
if (type == LOADER_TYPE) {
|
||||||
// Loader
|
// Loader
|
||||||
((LoaderView) holder.itemView).setup(currentStatus == Task.Status.ERROR, task::start);
|
((LoaderView) holder.itemView).setup(currentStatus == Task.Status.ERROR, task::start);
|
||||||
} else if (type == DATA_TYPE) {
|
} else if (type == ITEM_TYPE) {
|
||||||
// Data
|
// 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
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
|
int size;
|
||||||
if (currentStatus != Task.Status.DONE) {
|
if (currentStatus != Task.Status.DONE) {
|
||||||
// Only Loader
|
// Only Loader
|
||||||
return 1;
|
size = 1;
|
||||||
} else {
|
} else {
|
||||||
// Data Size
|
// 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
|
@Override
|
||||||
public int getItemViewType(int position) {
|
public int getItemViewType(int position) {
|
||||||
if (currentStatus != Task.Status.DONE && position == 0) {
|
if (currentStatus != Task.Status.DONE && position == getFirstElementPosition()) {
|
||||||
// Loader
|
// Loader
|
||||||
return LOADER_TYPE;
|
return LOADER_TYPE;
|
||||||
|
} else if (position == 0) {
|
||||||
|
// Header
|
||||||
|
return HEADER_TYPE;
|
||||||
} else {
|
} else {
|
||||||
// Data
|
// Item
|
||||||
return DATA_TYPE;
|
return ITEM_TYPE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,4 +205,12 @@ public abstract class TaskAdapter<E, VH extends RecyclerView.ViewHolder> extends
|
|||||||
// Add Items
|
// Add Items
|
||||||
notifyItemRangeInserted(0, getItemCount());
|
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 {
|
public class CategoryView extends FrameLayout {
|
||||||
private final MaterialCardView card;
|
private final MaterialCardView card;
|
||||||
public final LinearLayout children;
|
private final LinearLayout children;
|
||||||
private final AppCompatTextView title;
|
private final AppCompatTextView title;
|
||||||
|
|
||||||
public CategoryView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
public CategoryView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||||
@ -31,7 +31,7 @@ public class CategoryView extends FrameLayout {
|
|||||||
// Set Margin
|
// Set Margin
|
||||||
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||||
int margin = getResources().getDimensionPixelSize(R.dimen.margin);
|
int margin = getResources().getDimensionPixelSize(R.dimen.margin);
|
||||||
layoutParams.setMargins(margin, margin, margin, margin);
|
layoutParams.setMargins(margin, 0, margin, margin);
|
||||||
inner.setLayoutParams(layoutParams);
|
inner.setLayoutParams(layoutParams);
|
||||||
|
|
||||||
// Add Title
|
// Add Title
|
||||||
@ -74,4 +74,40 @@ public class CategoryView extends FrameLayout {
|
|||||||
card.setVisibility(isOpen ? VISIBLE : GONE);
|
card.setVisibility(isOpen ? VISIBLE : GONE);
|
||||||
title.setOnClickListener(v -> onClickTitle.run());
|
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
|
// Set Margin
|
||||||
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||||
int margin = getResources().getDimensionPixelSize(R.dimen.margin);
|
int margin = getResources().getDimensionPixelSize(R.dimen.margin);
|
||||||
layoutParams.setMargins(margin, margin, margin, margin);
|
layoutParams.setMargins(margin, 0, margin, margin);
|
||||||
inner.setLayoutParams(layoutParams);
|
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="app_name">Dining[MTU]</string>
|
||||||
<string name="retry">Retry</string>
|
<string name="retry">Retry</string>
|
||||||
<string name="load_error">Unable to load data!</string>
|
<string name="load_error">Unable to load data!</string>
|
||||||
|
<string name="date">Date</string>
|
||||||
|
<string name="meal">Meal</string>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in New Issue
Block a user