package io.github.memfis19.cadar.view;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.annotation.WorkerThread;
import android.support.v4.util.Pair;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import io.github.memfis19.cadar.CalendarController;
import io.github.memfis19.cadar.data.entity.Event;
import io.github.memfis19.cadar.event.CalendarPrepareCallback;
import io.github.memfis19.cadar.event.DisplayEventCallback;
import io.github.memfis19.cadar.event.OnDayChangeListener;
import io.github.memfis19.cadar.event.OnEventClickListener;
import io.github.memfis19.cadar.event.OnMonthChangeListener;
import io.github.memfis19.cadar.internal.ui.list.adapter.ListAdapter;
import io.github.memfis19.cadar.internal.ui.list.adapter.model.ListItemModel;
import io.github.memfis19.cadar.internal.utils.CalendarHelper;
import io.github.memfis19.cadar.internal.utils.DateUtils;
import io.github.memfis19.cadar.settings.ListCalendarConfiguration;
/**
* Created by memfis on 9/5/16.
*/
public class ListCalendar extends RecyclerView implements CalendarController<ListCalendarConfiguration, Pair<Calendar, Calendar>> {
private static final String TAG = "ListCalendar";
protected HandlerThread backgroundThread;
protected Handler backgroundHandler;
protected Handler uiHandler = new Handler(Looper.getMainLooper());
private List<Event> eventList;
private ListAdapter listAdapter;
private OnSetLayoutManagerListener onSetLayoutManagerListener;
private ListCalendarConfiguration configuration;
private OnMonthChangeListener monthChangeListener;
private OnDayChangeListener dayChangeListener;
private OnEventClickListener onEventClickListener;
private Calendar selectedDay = DateUtils.getCalendarInstance();
public CalendarPrepareCallback calendarPrepareCallback;
public ListCalendar(Context context) {
super(context);
}
public ListCalendar(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public void prepareCalendar(final ListCalendarConfiguration configuration) {
this.configuration = configuration;
backgroundThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
backgroundThread.start();
backgroundHandler = new Handler(backgroundThread.getLooper());
backgroundHandler.post(new Runnable() {
@Override
public void run() {
prepareInitialData();
uiHandler.post(new Runnable() {
@Override
public void run() {
initListWithAdapter();
}
});
}
});
}
@WorkerThread
private void prepareInitialData() {
Calendar calendar = DateUtils.getCalendarInstance();
calendar = DateUtils.setTimeToMidnight(calendar);
final List<ListItemModel> listItemModels = new ArrayList<>();
Calendar startPeriod = DateUtils.setTimeToMonthStart((Calendar) calendar.clone());
startPeriod.set(configuration.getPeriodType(), calendar.get(configuration.getPeriodType()) - configuration.getPeriodValue());
Calendar endPeriod = DateUtils.setTimeToMonthStart((Calendar) calendar.clone());
endPeriod.set(configuration.getPeriodType(), calendar.get(configuration.getPeriodType()) + configuration.getPeriodValue());
int capacityMonth = DateUtils.monthBetweenPure(startPeriod.getTime(), endPeriod.getTime());
CalendarHelper.prepareListItems(listItemModels, startPeriod, capacityMonth);
listAdapter = new ListAdapter(
configuration,
ListCalendar.this,
listItemModels,
eventList,
startPeriod,
endPeriod,
backgroundHandler,
uiHandler,
monthChangeListener,
dayChangeListener);
listAdapter.setOnEventClickListener(onEventClickListener);
onSetLayoutManagerListener = listAdapter;
}
@UiThread
private void initListWithAdapter() {
setLayoutManager(new CalendarLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false));
setAdapter(listAdapter);
scrollToPosition(listAdapter.getDatePosition(DateUtils.setTimeToMidnight(selectedDay)));
if (calendarPrepareCallback != null)
calendarPrepareCallback.onCalendarReady(this);
}
public void setCalendarPrepareCallback(CalendarPrepareCallback calendarPrepareCallback) {
this.calendarPrepareCallback = calendarPrepareCallback;
}
public void setOnEventClickListener(OnEventClickListener onEventClickListener) {
this.onEventClickListener = onEventClickListener;
}
public interface OnSetLayoutManagerListener {
void onSetLayoutManager(LayoutManager layout);
}
@Override
public void setLayoutManager(LayoutManager layout) {
super.setLayoutManager(layout);
onSetLayoutManagerListener.onSetLayoutManager(layout);
}
@Override
public void releaseCalendar() {
backgroundThread.quit();
listAdapter.release();
}
@Override
public void setSelectedDay(Calendar selectedDay, boolean scrollToSelectedDay) {
this.selectedDay = selectedDay;
if (listAdapter != null)
listAdapter.setSelectedDay(selectedDay);
}
@Override
public void displayEvents(List<Event> list, DisplayEventCallback<Pair<Calendar, Calendar>> callback) {
this.eventList = list;
if (listAdapter != null) listAdapter.displayEvents(list, callback);
}
public void refresh() {
if (listAdapter != null) listAdapter.displayEvents();
}
public void addEvent(Event event) {
listAdapter.addEvent(event);
}
public void addEvents(List<Event> events) {
listAdapter.addEvents(events);
}
public void editEvent(Event event) {
listAdapter.editEvent(event);
}
public void removeEvent(Event event) {
listAdapter.removeEvent(event);
}
public void setOnMonthChangeListener(OnMonthChangeListener monthChangeListener) {
this.monthChangeListener = monthChangeListener;
}
public void setOnDayChangeListener(OnDayChangeListener dayChangeListener) {
this.dayChangeListener = dayChangeListener;
}
public void setSelectedMonth(Calendar selectedMonth) {
listAdapter.setSelectedMonth(selectedMonth);
}
/**
* Check if this view can be scrolled horizontally in a certain direction.
*
* @param direction Negative to check scrolling left, positive to check scrolling right.
* @return true if this view can be scrolled in the specified direction, false otherwise.
*/
public boolean canScrollHorizontally(int direction) {
final int offset = computeHorizontalScrollOffset();
final int range = computeHorizontalScrollRange() - computeHorizontalScrollExtent();
if (range == 0) return false;
if (direction < 0) {
return offset > 0;
} else {
return offset < range - 1;
}
}
/**
* Check if this view can be scrolled vertically in a certain direction.
*
* @param direction Negative to check scrolling up, positive to check scrolling down.
* @return true if this view can be scrolled in the specified direction, false otherwise.
*/
public boolean canScrollVertically(int direction) {
final int offset = computeVerticalScrollOffset();
final int range = computeVerticalScrollRange() - computeVerticalScrollExtent();
if (range == 0) return false;
if (direction < 0) {
return offset > 0;
} else {
return offset < range - 1;
}
}
private class CalendarLayoutManager extends LinearLayoutManager {
public CalendarLayoutManager(Context context) {
super(context);
}
public CalendarLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
public CalendarLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
RecyclerView.State state) {
try {
int value = super.scrollVerticallyBy(dy, recycler, state);
Log.i("", "");
return value;
} catch (Exception error) {
Log.e(ListCalendar.TAG, "IndexOutOfBoundsException in RecyclerView happens", error);
}
return 0;
}
@Override
public boolean supportsPredictiveItemAnimations() {
return false;
}
/***
* Workaround for know RV issue
* @link https://code.google.com/p/android/issues/detail?id=77846#c10
*/
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
try {
super.onLayoutChildren(recycler, state);
} catch (IndexOutOfBoundsException e) {
Log.e(ListCalendar.TAG, "IndexOutOfBoundsException in RecyclerView happens");
}
}
}
}