package io.github.memfis19.cadar.view; import android.content.Context; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.os.Handler; import android.os.HandlerThread; import android.os.Process; import android.support.annotation.IntDef; import android.support.v4.content.ContextCompat; import android.text.Spannable; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; import android.util.AttributeSet; import android.view.View; import android.widget.ArrayAdapter; import android.widget.RelativeLayout; import android.widget.TextView; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import io.github.memfis19.cadar.CalendarController; import io.github.memfis19.cadar.R; import io.github.memfis19.cadar.data.entity.Event; import io.github.memfis19.cadar.event.CalendarPrepareCallback; import io.github.memfis19.cadar.event.OnDayChangeListener; import io.github.memfis19.cadar.event.OnMonthChangeListener; import io.github.memfis19.cadar.internal.ui.events.TimeOutClickListener; import io.github.memfis19.cadar.internal.utils.DateUtils; import io.github.memfis19.cadar.internal.utils.ViewUtils; import io.github.memfis19.cadar.internal.utils.WordUtils; import io.github.memfis19.cadar.settings.MonthCalendarConfiguration; /** * Created by serg on 19.10.15. */ public class ExtendedMonthCalendar extends RelativeLayout { public static final int STATE_NOT_READY = 0; public static final int STATE_READY = 1; public static final int STATE_PREPARE = 2; private int currentCalendarState = STATE_NOT_READY; private static final String DAY_FORMAT = "d"; private static final String MONTH_FORMAT = "MMMM"; private static final String YEAR_FORMAT = "yyyy"; private static final String THREAD_NAME = "StpMonthCalendarExtended2.process_text_thread"; private final static int BORDER_WIDTH = 1; private Context context; private int colorTheme; private int startOfWeekDay; private Calendar selectedDay; private Calendar pointingDay; private Calendar currentMonth; private List<Event> eventList = new ArrayList<>(); private Drawable arrowUpDrawable; private Drawable arrowDownDrawable; private TextView monthYearLabel; private MonthCalendar monthView; private TextView todayTextView; private View bottomLine; private boolean isCalendarCollapsed = false; private boolean isCanCollapse = true; private MonthCalendarExtendedListener listener; private Handler uiHandler = new Handler(); private TextProcessor handlerThread; private TextTask textProcessingTask; @IntDef({STATE_NOT_READY, STATE_PREPARE, STATE_READY}) @Retention(RetentionPolicy.SOURCE) public @interface States { } public interface MonthCalendarExtendedListener { void onCollapse(); void onExpand(); void onDaySelected(Calendar selectedDay); void onMonthChanged(Calendar calendar); } public ExtendedMonthCalendar(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; setClickable(true); initView(); } public ExtendedMonthCalendar(Context context) { this(context, null); } public void setup(MonthCalendarConfiguration monthCalendarConfiguration, Calendar pointingDay, int startOfWeekDay, List<Event> events, int colorTheme) { this.pointingDay = pointingDay; if (selectedDay == null) this.selectedDay = DateUtils.getCalendarInstance(); this.currentMonth = DateUtils.getCalendarInstance(); this.startOfWeekDay = startOfWeekDay; this.eventList = events; this.colorTheme = colorTheme; handlerThread = new TextProcessor(THREAD_NAME); handlerThread.start(); handlerThread.prepareHandler(); textProcessingTask = new TextTask(); init(monthCalendarConfiguration); } public void update(List<Event> events) { this.eventList = events; monthView.displayEvents(eventList, null); } public void release() { monthView.releaseCalendar(); handlerThread.quit(); } public void isCanCollapse(boolean isCanCollapse) { this.isCanCollapse = isCanCollapse; } private void initView() { inflate(context, R.layout.extended_month_calendar_layout, this); todayTextView = (TextView) findViewById(R.id.today_day_label); findViewById(R.id.today_day_label_container).setOnClickListener(new TimeOutClickListener() { @Override public void onViewClick(View view) { selectedDay = DateUtils.getCalendarInstance(); monthView.setSelectedDay(selectedDay, true); updateMonthAndYearTitle(selectedDay); if (listener != null) listener.onDaySelected(selectedDay); } }); monthYearLabel = (TextView) findViewById(R.id.month_year_label); monthView = (MonthCalendar) findViewById(R.id.month_calendar); bottomLine = findViewById(R.id.monthViewBottomLine); } public @States int getCurrentCalendarState() { return currentCalendarState; } private void init(MonthCalendarConfiguration monthCalendarConfiguration) { monthView.prepareCalendar(monthCalendarConfiguration); currentCalendarState = STATE_PREPARE; todayTextView.setText(DateUtils.dateToString(pointingDay.getTime(), DAY_FORMAT)); Drawable drawable = todayTextView.getBackground(); if (drawable instanceof GradientDrawable) { ((GradientDrawable) drawable).setStroke(ViewUtils.convertDipToPixels(BORDER_WIDTH, context), colorTheme); } ArrayAdapter<CharSequence> viewMenuAdapter = ArrayAdapter .createFromResource(context, R.array.calendar_view_options, R.layout.extended_month_calendar_view_option_spinner_item); viewMenuAdapter.setDropDownViewResource(R.layout.extended_month_calendar_view_option_spinner_dropdown_item); arrowUpDrawable = ViewUtils.getDrawable(context, R.drawable.ic_arrow_drop_up_white_24dp); arrowUpDrawable.setColorFilter(ContextCompat.getColor(getContext(), R.color.month_title_color), PorterDuff.Mode.MULTIPLY); arrowUpDrawable.setBounds(0, 0, arrowUpDrawable.getIntrinsicWidth(), arrowUpDrawable.getIntrinsicHeight()); arrowDownDrawable = ViewUtils.getDrawable(context, R.drawable.ic_arrow_drop_down_white_24dp); arrowDownDrawable.setColorFilter(ContextCompat.getColor(getContext(), R.color.month_title_color), PorterDuff.Mode.MULTIPLY); arrowDownDrawable.setBounds(0, 0, arrowDownDrawable.getIntrinsicWidth(), arrowDownDrawable.getIntrinsicHeight()); updateMonthAndYearTitle(selectedDay); monthYearLabel.setOnClickListener(new TimeOutClickListener() { @Override public void onViewClick(View view) { if (!isCanCollapse) return; if (isCalendarCollapsed) { expand(); } else { collapse(); } } }); if (isCanCollapse) { if (isCalendarCollapsed) collapse(); else expand(); } else { monthYearLabel.setCompoundDrawables(null, null, null, null); bottomLine.setVisibility(INVISIBLE); } monthView.setCalendarPrepareCallback(new CalendarPrepareCallback() { @Override public void onCalendarReady(CalendarController calendar) { currentCalendarState = STATE_READY; monthView.setSelectedDay(selectedDay, true); monthView.setOnDayChangeListener(new OnDayChangeListener() { @Override public void onDayChanged(Calendar calendar) { if (currentMonth.get(Calendar.MONTH) != calendar.get(Calendar.MONTH)) { currentMonth = calendar; monthView.setSelectedDay(calendar, true); updateMonthAndYearTitle(calendar); } selectedDay = calendar; if (listener != null) listener.onDaySelected(selectedDay); } }); monthView.setOnMonthChangeListener(new OnMonthChangeListener() { @Override public void onMonthChanged(Calendar calendar) { currentMonth = calendar; updateMonthAndYearTitle(calendar); if (listener != null) listener.onMonthChanged(calendar); } }); } }); } private void updateMonthAndYearTitle(final Calendar calendar) { textProcessingTask.setDay(calendar); handlerThread.postTask(textProcessingTask); } public void setInitialDay(Calendar selectedDay) { this.selectedDay = selectedDay; } public void setSelectedDay(Calendar selectedDay, boolean scrollTo) { this.selectedDay = selectedDay; monthView.setSelectedDay(selectedDay, scrollTo); updateMonthAndYearTitle(selectedDay); } public void expand() { if (monthView != null && isCanCollapse) { monthView.setVisibility(VISIBLE); monthYearLabel.setCompoundDrawables(null, null, arrowUpDrawable, null); isCalendarCollapsed = false; if (listener != null) listener.onExpand(); } } public void collapse() { if (monthView != null && isCanCollapse) { monthView.setVisibility(GONE); monthYearLabel.setCompoundDrawables(null, null, arrowDownDrawable, null); isCalendarCollapsed = true; if (listener != null) listener.onCollapse(); } } public boolean isCollapsed() { return this.isCalendarCollapsed; } public void setIsCalendarCollapsed(boolean isCalendarCollapsed) { this.isCalendarCollapsed = isCalendarCollapsed; } public void setMonthCalendarExtendedListener(MonthCalendarExtendedListener listener) { this.listener = listener; } private class TextProcessor extends HandlerThread { private Handler handler; TextProcessor(String name) { super(name, Process.THREAD_PRIORITY_BACKGROUND); } void postTask(Runnable task) { handler.post(task); } void prepareHandler() { handler = new Handler(getLooper()); } } private class TextTask implements Runnable { private Calendar day; public void setDay(Calendar day) { this.day = day; } @Override public void run() { String month = WordUtils.capitalize(DateUtils.dateToString(day.getTime(), MONTH_FORMAT)); int monthLength = month.length(); Spannable monthSpannableText = new SpannableString(month + " " + DateUtils.dateToString(day.getTime(), YEAR_FORMAT)); int wholeLength = monthSpannableText.length(); monthSpannableText.setSpan(new ForegroundColorSpan(ContextCompat.getColor(getContext(), R.color.month_title_color)), 0, monthLength, 0); monthSpannableText.setSpan(new ForegroundColorSpan(ContextCompat.getColor(getContext(), R.color.month_title_color)), monthLength, wholeLength, 0); final Spannable resultText = monthSpannableText; uiHandler.post(new Runnable() { @Override public void run() { monthYearLabel.setText(resultText); } }); } } }