MTU-Dining/app/src/main/java/com/thebrokenrail/mtudining/activity/task/TaskAdapter.java
2024-02-17 16:51:16 -05:00

220 lines
6.0 KiB
Java

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> extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements Task.Listener<E> {
public static final int LOADER_TYPE = 1;
public static final int ITEM_TYPE = 0;
public static final int HEADER_TYPE = 2;
/**
* The task.
*/
private final Task<E> task;
/**
* Current task status.
*/
private Task.Status currentStatus;
/**
* Task result (or null if none).
*/
private E result;
/**
* Get task result.
* @return Task result
*/
protected E getResult() {
return result;
}
/**
* Create adapter.
* @param task The task
*/
public TaskAdapter(Task<E> task) {
// Setup Event Handling
this.task = task;
task.addListener(this);
}
/**
* Call when activity is destroyed.
*/
public void onDestroy() {
// Stop Handling Events
task.removeListener(this);
}
/**
* Create view for item in task's result.
* @param parent The view parent
* @return The new view
*/
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
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == LOADER_TYPE) {
// Loader
LoaderView loader = new LoaderView(parent.getContext(), null);
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 == ITEM_TYPE) {
// Data
return new RecyclerView.ViewHolder(createItemView(parent)) {};
} else if (viewType == HEADER_TYPE) {
// Header
return new RecyclerView.ViewHolder(createHeader(parent)) {};
} else {
// Unknown
throw new RuntimeException();
}
}
/**
* Bind view for item in task's result.
* @param view The view
* @param position The item's position
*/
protected abstract void bindItemView(View view, int position);
/**
* Bind view for header.
* @param view The view
*/
protected void bindHeader(View view) {
}
@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 == ITEM_TYPE) {
// Data
bindItemView(holder.itemView, position);
} else if (type == HEADER_TYPE) {
// Header
bindHeader(holder.itemView);
}
}
/**
* Get size of task's dataset.
* @return The size of the dataset
*/
protected abstract int getDataSize();
@Override
public int getItemCount() {
int size;
if (currentStatus != Task.Status.DONE) {
// Only Loader
size = 1;
} else {
// Data Size
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 == getFirstElementPosition()) {
// Loader
return LOADER_TYPE;
} else if (position == 0) {
// Header
return HEADER_TYPE;
} else {
// Item
return ITEM_TYPE;
}
}
@Override
public void update(Task.Status status, E result) {
int oldItemCount = getItemCount();
// Store
this.currentStatus = status;
this.result = result;
// Change UI
if (status == Task.Status.IN_PROGRESS || status == Task.Status.NOT_STARTED) {
// Show Progress Circle
reloadUI(oldItemCount);
// Start Task If Needed
if (status == Task.Status.NOT_STARTED) {
task.start();
}
} else if (status == Task.Status.ERROR) {
// Show Error
reloadUI(oldItemCount);
} else {
// Done
reloadUI(oldItemCount);
}
}
/**
* Reload UI.
* @param oldItemCount Amount of visible items before UI change
*/
protected void reloadUI(int oldItemCount) {
// Reload Header Without Animation
notifyItemChanged(0, new Object()); // https://stackoverflow.com/a/45590003
// Remove Existing Items
notifyItemRangeRemoved(1, oldItemCount - 1);
// Add Items
notifyItemRangeInserted(1, getItemCount() - 1);
}
/**
* Get task.
* @return The task
*/
public Task<E> getTask() {
return task;
}
}