package com.kickstarter.ui.adapters;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.kickstarter.BuildConfig;
import com.kickstarter.libs.utils.ExceptionUtils;
import com.kickstarter.ui.viewholders.KSViewHolder;
import com.trello.rxlifecycle.ActivityEvent;
import net.hockeyapp.android.ExceptionHandler;
import java.util.ArrayList;
import java.util.List;
public abstract class KSAdapter extends RecyclerView.Adapter<KSViewHolder> {
private List<List<Object>> sections = new ArrayList<>();
public List<List<Object>> sections() {
return sections;
}
public void clearSections() {
sections.clear();
}
public <T> void addSection(final @NonNull List<T> section) {
sections.add(new ArrayList<>(section));
}
public <T> void addSections(final @NonNull List<List<T>> sections) {
for (final List<T> section : sections) {
addSection(section);
}
}
public <T> void setSection(final int location, final @NonNull List<T> section) {
sections.set(location, new ArrayList<>(section));
}
public <T> void insertSection(final int location, final @NonNull List<T> section) {
sections.add(location, new ArrayList<>(section));
}
/**
* Fetch the layout id associated with a sectionRow.
*/
protected abstract int layout(final @NonNull SectionRow sectionRow);
/**
* Returns a new KSViewHolder given a layout and view.
*/
protected abstract @NonNull KSViewHolder viewHolder(final @LayoutRes int layout, final @NonNull View view);
@Override
public void onViewDetachedFromWindow(final @NonNull KSViewHolder holder) {
super.onViewDetachedFromWindow(holder);
// View holders are "stopped" when they are detached from the window for recycling
holder.lifecycleEvent(ActivityEvent.STOP);
// View holders are "destroy" when they are detached from the window and no adapter is listening
// to events, so ostensibly the view holder is being deallocated.
if (!hasObservers()) {
holder.lifecycleEvent(ActivityEvent.DESTROY);
}
}
@Override
public void onViewAttachedToWindow(final @NonNull KSViewHolder holder) {
super.onViewAttachedToWindow(holder);
// View holders are "started" when they are attached to the new window because this means
// it has been recycled.
holder.lifecycleEvent(ActivityEvent.START);
}
@Override
public final @NonNull KSViewHolder onCreateViewHolder(final @NonNull ViewGroup viewGroup, final @LayoutRes int layout) {
final View view = inflateView(viewGroup, layout);
final KSViewHolder viewHolder = viewHolder(layout, view);
viewHolder.lifecycleEvent(ActivityEvent.CREATE);
return viewHolder;
}
@Override
public final void onBindViewHolder(final @NonNull KSViewHolder viewHolder, final int position) {
final Object data = objectFromPosition(position);
try {
viewHolder.bindData(data);
viewHolder.onBind();
} catch (final Exception e) {
if (BuildConfig.DEBUG) {
ExceptionUtils.rethrowAsRuntimeException(e);
} else {
// TODO: alter the exception message to say we are just reporting it and it's not a real crash.
ExceptionHandler.saveException(e, null);
}
}
}
@Override
public final int getItemViewType(final int position) {
return layout(sectionRowFromPosition(position));
}
@Override
public final int getItemCount() {
int itemCount = 0;
for (final List<?> section : sections) {
itemCount += section.size();
}
return itemCount;
}
/**
* Gets the data object associated with a sectionRow.
*/
protected Object objectFromSectionRow(final @NonNull SectionRow sectionRow) {
return sections.get(sectionRow.section()).get(sectionRow.row());
}
protected int sectionCount(final int section) {
if (section > sections().size() - 1) {
return 0;
}
return sections().get(section).size();
}
/**
* Gets the data object associated with a position.
*/
protected Object objectFromPosition(final int position) {
return objectFromSectionRow(sectionRowFromPosition(position));
}
private @NonNull SectionRow sectionRowFromPosition(final int position) {
final SectionRow sectionRow = new SectionRow();
int cursor = 0;
for (final List<?> section : sections) {
for (final Object __ : section) {
if (cursor == position) {
return sectionRow;
}
cursor++;
sectionRow.nextRow();
}
sectionRow.nextSection();
}
throw new RuntimeException("Position " + position + " not found in sections");
}
private @NonNull View inflateView(final @NonNull ViewGroup viewGroup, final @LayoutRes int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(viewGroup.getContext());
return layoutInflater.inflate(viewType, viewGroup, false);
}
/**
* SectionRows allow RecyclerViews to be structured into sections of rows.
*/
protected class SectionRow {
private int section;
private int row;
public SectionRow() {
section = 0;
row = 0;
}
public SectionRow(final int section, final int row) {
this.section = section;
this.row = row;
}
public int section() {
return section;
}
public int row() {
return row;
}
protected void nextRow() {
row++;
}
protected void nextSection() {
section++;
row = 0;
}
}
}