/*
* Calendula - An assistant for personal medication management.
* Copyright (C) 2016 CITIUS - USC
*
* Calendula is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software. If not, see <http://www.gnu.org/licenses/>.
*/
package es.usc.citius.servando.calendula;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.preference.PreferenceManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.mikepenz.community_material_typeface_library.CommunityMaterial;
import com.mikepenz.iconics.IconicsDrawable;
import com.mikepenz.iconics.typeface.IIcon;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import es.usc.citius.servando.calendula.database.DB;
import es.usc.citius.servando.calendula.persistence.DailyScheduleItem;
import es.usc.citius.servando.calendula.persistence.Routine;
import es.usc.citius.servando.calendula.persistence.Schedule;
import es.usc.citius.servando.calendula.persistence.ScheduleItem;
import es.usc.citius.servando.calendula.scheduling.AlarmScheduler;
import es.usc.citius.servando.calendula.util.AvatarMgr;
import es.usc.citius.servando.calendula.util.DailyAgendaItemStub;
import es.usc.citius.servando.calendula.util.DailyAgendaItemStub.DailyAgendaItemStubElement;
import es.usc.citius.servando.calendula.util.ScreenUtils;
import es.usc.citius.servando.calendula.util.view.ParallaxImageView;
/**
* Created by joseangel.pineiro on 11/6/15.
*/
public class DailyAgendaRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final String TAG = "DailyAgendaAdapter";
private final long window;
private final int SPACER = 1;
private final int EMPTY = 2;
private final int NORMAL = 3;
List<DailyAgendaItemStub> items;
private boolean expanded = false;
private int parallaxHeight;
private int emptyItemHeight;
private boolean enableParallax = true;
private EventListener listener;
public DailyAgendaRecyclerAdapter(List<DailyAgendaItemStub> items, final RecyclerView rv, final LinearLayoutManager llm, Activity ctx) {
this.items = items;
emptyItemHeight = ScreenUtils.dpToPx(ctx.getResources(), 45);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
String delayMinutesStr = prefs.getString("alarm_reminder_window", "60");
window = Long.parseLong(delayMinutesStr);
Display display = ctx.getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
parallaxHeight = size.y * 2;
if (enableParallax) {
rv.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
updateParallax(llm, recyclerView);
}
});
}
}
public void setListener(EventListener listener) {
this.listener = listener;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v;
switch (viewType) {
case NORMAL:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.daily_view_intake, parent, false);
return new NormalItemViewHolder(v);
case SPACER:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.daily_view_empty_dayspacer, parent, false);
return new SpacerItemViewHolder(v);
default:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.daily_view_empty_hour, parent, false);
return new EmptyItemViewHolder(v);
}
}
@Override
public int getItemViewType(int position) {
DailyAgendaItemStub item = items.get(position);
int type = EMPTY;
if (item.hasEvents) {
type = NORMAL;
} else if (item.isSpacer) {
return SPACER;
}
return type;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final DailyAgendaItemStub item = items.get(position);
if (holder instanceof SpacerItemViewHolder) {
onBindViewSpacerItemViewHolder((SpacerItemViewHolder) holder, item, position);
} else if (holder instanceof NormalItemViewHolder) {
onBindNormalItemViewHolder((NormalItemViewHolder) holder, item, position);
} else {
onBindEmptyItemViewHolder((EmptyItemViewHolder) holder, item, position);
}
}
@Override
public int getItemCount() {
return items.size();
}
public void onBindViewSpacerItemViewHolder(SpacerItemViewHolder holder, DailyAgendaItemStub item, int position) {
if (expanded) {
int color = DB.patients().getActive(holder.itemView.getContext()).color();
// TODO: get from strings
String title;
if (item.date.equals(LocalDate.now())) {
title = "Hoy";
} else if (item.date.equals(LocalDate.now().minusDays(1))) {
title = "Ayer";
} else if (item.date.equals(LocalDate.now().plusDays(1))) {
title = "MaƱana";
} else {
title = item.date.toString("EEEE dd");
}
holder.dayBg.setBackgroundColor(color);
holder.day.setVisibility(View.VISIBLE);
holder.day.setText(title);
holder.parallax.updateParallax();
}
holder.itemView.setVisibility(expanded ? View.VISIBLE : View.GONE);
ViewGroup.LayoutParams params = holder.itemView.getLayoutParams();
int newHeight = expanded ? ScreenUtils.dpToPx(holder.itemView.getResources(), 80) : 0;
if (params.height != newHeight) {
params.height = newHeight;
holder.itemView.setLayoutParams(params);
}
}
public void onBindEmptyItemViewHolder(EmptyItemViewHolder viewHolder, DailyAgendaItemStub item, int position) {
viewHolder.stub = item;
if (expanded) {
LocalDate d = viewHolder.stub.date;
if (d.equals(DateTime.now().toLocalDate())) {
viewHolder.hourText.setText(item.time != null ? item.time.toString("kk:mm") : "--");
} else {
viewHolder.hourText.setText(item.dateTime().toString("kk:mm"));
}
}
viewHolder.itemView.setVisibility(expanded ? View.VISIBLE : View.GONE);
ViewGroup.LayoutParams params = viewHolder.container.getLayoutParams();
int newHeight = expanded ? emptyItemHeight : 0;
if (params.height != newHeight) {
params.height = newHeight;
viewHolder.container.setLayoutParams(params);
}
}
public void onBindNormalItemViewHolder(NormalItemViewHolder viewHolder, DailyAgendaItemStub item, int i) {
viewHolder.stub = item;
item.displayable = isDisplayable(item);
if (item.displayable) {
if (!item.isRoutine) {
viewHolder.itemTypeIcon.setImageResource(R.drawable.ic_history_black_48dp);
} else {
viewHolder.itemTypeIcon.setImageResource(R.drawable.ic_alarm_black_48dp);
}
if (item.patient != null) {
viewHolder.avatarIcon.setImageResource(AvatarMgr.res(item.patient.avatar()));
viewHolder.patientIndicatorBand.setBackgroundColor(item.patient.color());
}
viewHolder.title.setText(item.title);
viewHolder.hour.setText(item.time.toString("kk") + ":");
viewHolder.minute.setText(item.time.toString("mm"));
boolean allTaken = addMeds(viewHolder, item);
if (allTaken) {
viewHolder.takenOverlay.setVisibility(View.VISIBLE);
viewHolder.actionsView.setVisibility(View.GONE);
} else {
viewHolder.takenOverlay.setVisibility(View.GONE);
if (isAvailable(item)) {
viewHolder.actionsView.animate().alpha(1).scaleX(1f).scaleY(1f).setStartDelay(500);
viewHolder.actionsView.setVisibility(View.VISIBLE);
} else {
viewHolder.actionsView.setVisibility(View.GONE);
}
}
}
viewHolder.itemView.setVisibility(item.displayable ? View.VISIBLE : View.GONE);
ViewGroup.LayoutParams params = viewHolder.itemView.getLayoutParams();
int newHeight = item.displayable ? ViewGroup.LayoutParams.WRAP_CONTENT : 0;
if (params.height != newHeight) {
params.height = newHeight;
viewHolder.itemView.setLayoutParams(params);
}
}
public boolean isExpanded() {
return expanded;
}
public boolean isShowingSomething() {
Log.d(TAG, "isShowingSomething, expanded: " + expanded);
if (expanded && items.size() > 0)
return true;
boolean result = false;
for (DailyAgendaItemStub item : items) {
if (isDisplayable(item)) {
Log.d(TAG, "Item is displayable: " + item.toString());
result = true;
}
}
return result;
}
public void toggleCollapseMode() {
Log.d("RVAdapter", "toggleCollapseMode");
expanded = !expanded;
boolean willSHowSomething = isShowingSomething();
if (listener != null) {
listener.onBeforeToggleCollapse(expanded, willSHowSomething);
}
for (int i = 0; i < items.size(); i++) {
notifyItemChanged(i);
}
if (listener != null) {
listener.onAfterToggleCollapse(expanded, willSHowSomething);
}
}
public void updatePosition(int position) {
updateItem(position);
}
boolean isAvailable(DailyAgendaItemStub stub) {
return isAvailable(stub.dateTime());
}
boolean isDisplayable(DailyAgendaItemStub stub) {
DateTime t = stub.dateTime();
DateTime midnight = DateTime.now().withTimeAtStartOfDay().plusDays(1);
return stub.hasEvents && (isAvailable(stub) || expanded || (t.isAfterNow() && t.isBefore(midnight)));
}
boolean isAvailable(DateTime time) {
DateTime now = DateTime.now();
return time.isBefore(now) && time.plusMillis((int) window * 60 * 1000).isAfter(now);
}
void updateParallax(LinearLayoutManager lm, RecyclerView rv) {
if (!expanded) {
return;
}
int start = lm.findFirstVisibleItemPosition();
int end = lm.findLastVisibleItemPosition();
for (int i = start; i < end; i++) {
RecyclerView.ViewHolder h = rv.findViewHolderForAdapterPosition(i);
if (h instanceof SpacerItemViewHolder) {
((SpacerItemViewHolder) h).parallax.updateParallax();
}
}
}
private boolean addMeds(NormalItemViewHolder viewHolder, DailyAgendaItemStub item) {
boolean allTaken = true;
viewHolder.medList.removeAllViews();
for (DailyAgendaItemStub.DailyAgendaItemStubElement element : item.meds) {
View intakeView = viewHolder.inflater.inflate(R.layout.daily_view_intake_med, null);
TextView medName = (TextView) intakeView.findViewById(R.id.med_item_name);
TextView medDose = (TextView) intakeView.findViewById(R.id.med_item_dose);
ImageView image = (ImageView) intakeView.findViewById(R.id.imageView);
String units = element.presentation.units(viewHolder.context.getResources());
image.setImageDrawable(medIcon(element.presentation.icon(), intakeView.getContext()));
medDose.setText(element.displayDose + " " + units + (element.dose > 1 ? "s" : ""));
medName.setText(element.medName);
if (element.taken) {
intakeView.findViewById(R.id.ic_done).setVisibility(View.VISIBLE);
} else {
allTaken = false;
intakeView.findViewById(R.id.ic_done).setVisibility(View.INVISIBLE);
}
viewHolder.medList.addView(intakeView);
}
return allTaken;
}
private Drawable medIcon(IIcon icon, Context ctx) {
return new IconicsDrawable(ctx)
.icon(icon)
.colorRes(R.color.white)
.sizeDp(24)
.paddingDp(0);
}
private void updateStub(DailyAgendaItemStub stub) {
if (!stub.isRoutine) {
Schedule s = DB.schedules().findById(stub.id);
DailyScheduleItem dsi = DB.dailyScheduleItems().findBy(s, stub.date, stub.time);
stub.meds.get(0).taken = dsi.takenToday();
} else {
for (DailyAgendaItemStubElement el : stub.meds) {
ScheduleItem si = DB.scheduleItems().findById(el.scheduleItemId);
DailyScheduleItem dsi = DB.dailyScheduleItems().findByScheduleItemAndDate(si, stub.date);
el.taken = dsi.takenToday();
}
}
}
private void updateItem(int position) {
DailyAgendaItemStub stub = items.get(position);
updateStub(stub);
notifyItemChanged(position);
}
public interface EventListener {
void onItemClick(View v, DailyAgendaItemStub item, int position);
void onBeforeToggleCollapse(boolean expanded, boolean somethingVisible);
void onAfterToggleCollapse(boolean expanded, boolean somethingVisible);
}
public static class EmptyItemViewHolder extends RecyclerView.ViewHolder {
RelativeLayout container;
TextView hourText;
DailyAgendaItemStub stub;
EmptyItemViewHolder(View itemView) {
super(itemView);
hourText = (TextView) itemView.findViewById(R.id.hour_text);
container = (RelativeLayout) itemView.findViewById(R.id.container);
}
}
public class SpacerItemViewHolder extends RecyclerView.ViewHolder {
TextView day;
ImageView dayBg;
View container;
ParallaxImageView parallax;
SpacerItemViewHolder(View itemView) {
super(itemView);
day = (TextView) itemView.findViewById(R.id.day_text);
dayBg = (ImageView) itemView.findViewById(R.id.day_bg);
container = itemView.findViewById(R.id.container);
parallax = (ParallaxImageView) itemView.findViewById(R.id.parallax_bg);
ViewGroup.LayoutParams layoutParams = parallax.getLayoutParams();
layoutParams.height = parallaxHeight;
parallax.setLayoutParams(layoutParams);
}
}
public class NormalItemViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
Context context;
DailyAgendaItemStub stub;
LayoutInflater inflater;
LinearLayout medList;
ImageView itemTypeIcon;
ImageView avatarIcon;
ImageView patientIndicatorBand;
TextView title;
TextView hour;
TextView minute;
View arrow;
View top;
View bottom;
View takenOverlay;
View actionsView;
ImageButton checkAll;
public NormalItemViewHolder(View itemView) {
super(itemView);
this.context = itemView.getContext();
this.inflater = LayoutInflater.from(itemView.getContext());
this.medList = (LinearLayout) itemView.findViewById(R.id.med_item_list);
this.itemTypeIcon = (ImageView) itemView.findViewById(R.id.imageButton2);
this.avatarIcon = (ImageView) itemView.findViewById(R.id.patient_avatar);
this.title = (TextView) itemView.findViewById(R.id.routines_list_item_name);
this.hour = (TextView) itemView.findViewById(R.id.routines_list_item_hour);
this.minute = (TextView) itemView.findViewById(R.id.routines_list_item_minute);
this.arrow = itemView.findViewById(R.id.count_container);
this.top = itemView.findViewById(R.id.routine_list_item_container);
this.bottom = itemView.findViewById(R.id.bottom);
this.patientIndicatorBand = (ImageView) itemView.findViewById(R.id.patient_indicator_band);
this.takenOverlay = itemView.findViewById(R.id.taken_overlay);
this.actionsView = itemView.findViewById(R.id.action_container);
this.checkAll = (ImageButton) itemView.findViewById(R.id.check_all_button);
this.checkAll.setImageDrawable(new IconicsDrawable(context)
.colorRes(R.color.white) //agenda_item_title
.icon(CommunityMaterial.Icon.cmd_check_all) //cmd_arrow_right_bold
.paddingDp(0)
.sizeDp(28)
);
actionsView.setOnClickListener(this);
top.setOnClickListener(this);
arrow.setOnClickListener(this);
itemView.setOnClickListener(this);
checkAll.setOnClickListener(this);
}
@Override
public void onClick(View view) {
Log.d("Recycler", "Click row, listener is null? " + (listener == null));
if (view.getId() == R.id.check_all_button || view.getId() == R.id.action_container) {
List<DailyScheduleItem> dailyScheduleItems = new ArrayList<>();
if (stub.isRoutine) {
List<ScheduleItem> rsi = Routine.findById(stub.id).scheduleItems();
for (ScheduleItem si : rsi) {
DailyScheduleItem dsi = DB.dailyScheduleItems().findByScheduleItemAndDate(si, stub.date);
if (dsi != null)
dailyScheduleItems.add(dsi);
}
} else {
Schedule s = Schedule.findById(stub.id);
dailyScheduleItems.add(DB.dailyScheduleItems().findBy(s, stub.date, stub.time));
}
for (DailyScheduleItem item : dailyScheduleItems) {
item.setTakenToday(true);
item.save();
}
if (stub.isRoutine) {
AlarmScheduler.instance().onIntakeCompleted(DB.routines().findById(stub.id), stub.date, context);
} else {
AlarmScheduler.instance().onIntakeCompleted(DB.schedules().findById(stub.id), stub.time, stub.date, context);
}
actionsView.animate().alpha(0).scaleX(0.5f).scaleY(0.5f).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
updateItem(getAdapterPosition());
}
});
} else if (listener != null) {
listener.onItemClick(view, stub, getAdapterPosition());
}
}
}
}