package com.jasonrobinson.racer.adapter; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; import android.os.CountDownTimer; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.BaseExpandableListAdapter; import android.widget.ImageView; import android.widget.PopupMenu; import android.widget.TextView; import com.jasonrobinson.racer.R; import com.jasonrobinson.racer.model.Race; import com.jasonrobinson.racer.util.AlarmUtils; import com.jasonrobinson.racer.util.CalendarUtils; import com.jasonrobinson.racer.util.RacerTimeUtils; import com.jasonrobinson.racer.util.RawTypeface; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Locale; import butterknife.ButterKnife; import butterknife.InjectView; public class RaceAdapter extends BaseExpandableListAdapter { private final DateFormat mDayOfWeekFormat = new SimpleDateFormat("EEEE", Locale.getDefault()); private final DateFormat mDateFormat; private final DateFormat mTimeFormat; private List<List<Race>> mDateRaces = new ArrayList<>(); private List<Race> mRaces; private OnRaceActionClickListener mOnActionClickListener; public RaceAdapter(Context context, List<Race> races, OnRaceActionClickListener onActionClickListener) { mRaces = races; mOnActionClickListener = onActionClickListener; mDateFormat = android.text.format.DateFormat.getDateFormat(context); mTimeFormat = android.text.format.DateFormat.getTimeFormat(context); init(); } private void init() { Date lastDate = null; for (Race race : mRaces) { Date startAt = race.getStartAt(); Calendar lastCal = Calendar.getInstance(); if (lastDate != null) { lastCal.setTime(lastDate); } Calendar cal = Calendar.getInstance(); cal.setTime(startAt); if (lastDate == null || cal.get(Calendar.YEAR) != lastCal.get(Calendar.YEAR) || cal.get(Calendar.DAY_OF_YEAR) != lastCal.get(Calendar.DAY_OF_YEAR)) { mDateRaces.add(new ArrayList<Race>()); lastDate = startAt; } mDateRaces.get(mDateRaces.size() - 1).add(race); } } @Override public int getGroupCount() { return mDateRaces.size(); } @Override public List<Race> getGroup(int groupPosition) { return mDateRaces.get(groupPosition); } @Override public long getGroupId(int groupPosition) { return groupPosition; } @Override public int getChildrenCount(int groupPosition) { return mDateRaces.get(groupPosition).size(); } @Override public Race getChild(int groupPosition, int childPosition) { return mDateRaces.get(groupPosition).get(childPosition); } @Override public long getChildId(int groupPosition, int childPosition) { return groupPosition * 1000 + childPosition; } @Override public boolean hasStableIds() { return false; } @Override public boolean isChildSelectable(int groupPosition, int childPosition) { return true; } @Override public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { Context context = parent.getContext(); View view = convertView; TextView textView1; TextView textView2; if (view == null) { view = LayoutInflater.from(context).inflate(android.R.layout.simple_expandable_list_item_2, parent, false); textView1 = (TextView) view.findViewById(android.R.id.text1); textView1.setTypeface(RawTypeface.obtain(context, R.raw.fontin_regular)); textView2 = (TextView) view.findViewById(android.R.id.text2); textView2.setTypeface(RawTypeface.obtain(context, R.raw.fontin_regular)); } else { textView1 = (TextView) view.findViewById(android.R.id.text1); textView2 = (TextView) view.findViewById(android.R.id.text2); } List<Race> races = getGroup(groupPosition); Date date = races.get(0).getStartAt(); textView1.setText(formatDayOfWeek(context, date) + " (" + races.size() + ")"); textView2.setText(formatDate(date)); return view; } @Override public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { Context context = parent.getContext(); View view = convertView; if (view == null) { view = LayoutInflater.from(context).inflate(R.layout.race_item, parent, false); final ViewHolder holder = new ViewHolder(view); holder.titleTextView.setTypeface(RawTypeface.obtain(context, R.raw.fontin_bold)); holder.popupMenu = new PopupMenu(context, holder.actionImageView); holder.actionImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { holder.popupMenu.show(); } }); view.setTag(holder); } final Race race = getChild(groupPosition, childPosition); final ViewHolder holder = (ViewHolder) view.getTag(); if (holder.timer != null) { holder.timer.cancel(); holder.timer = null; } setupActionMenu(context, holder.popupMenu, race); holder.titleTextView.setText(race.getRaceId()); Date startDate = race.getStartAt(); Date endDate = race.getEndAt(); if (startDate != null && endDate != null) { long millisNow = System.currentTimeMillis(); long millisTotal = (startDate.getTime() - millisNow) + (endDate.getTime() - startDate.getTime()); if (millisTotal > 0) { holder.timer = new RaceCountDownTimer(holder, race, millisTotal); holder.timer.start(); } else { updateTimeViews(holder, race); } } int backgroundId; if (AlarmUtils.isAlarmAdded(context, race)) { backgroundId = R.color.pastel_yellow; } else { TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{android.R.attr.listChoiceBackgroundIndicator}); backgroundId = a.getResourceId(0, android.R.drawable.list_selector_background); a.recycle(); } holder.rootView.setBackgroundResource(backgroundId); return view; } private void setupActionMenu(Context context, PopupMenu popupMenu, final Race race) { Menu menu = popupMenu.getMenu(); menu.clear(); popupMenu.getMenuInflater().inflate(R.menu.races_context_menu, menu); if (TextUtils.isEmpty(race.getUrl())) { menu.removeItem(R.id.menu_forum_post); } boolean alarmAdded = AlarmUtils.isAlarmAdded(context, race); if (alarmAdded || race.isInProgress() || race.isFinished()) { menu.removeItem(R.id.menu_add_notification); } if (!alarmAdded || race.isInProgress() || race.isFinished()) { menu.removeItem(R.id.menu_remove_notification); } popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.menu_forum_post: mOnActionClickListener.onForumPostClicked(race); break; case R.id.menu_add_notification: mOnActionClickListener.onAddNotificationClicked(race); break; case R.id.menu_remove_notification: mOnActionClickListener.onRemoveNotificationClicked(race); break; default: return false; } return true; } }); } private void updateTimeViews(ViewHolder holder, Race race) { CharSequence startAtTime; long millisNow = System.currentTimeMillis(); long millisUntil = race.getStartAt().getTime() - millisNow; long millisRemaining = race.getEndAt().getTime() - millisNow; if (millisUntil <= 3600000 && !race.isFinished()) { // <60 minutes until // start long millis; if (millisUntil <= 0) { // race in progress millis = millisRemaining; } else { millis = millisUntil; } startAtTime = RacerTimeUtils.formatElapsedTime(millis / 1000); } else { // >60 minutes until start or race finished startAtTime = formatTime(race.getStartAt()); } holder.timeTextView.setText(startAtTime); int color; int textResId; if (race.isInProgress()) { color = Color.GREEN; textResId = R.string.started; } else if (race.isRegistrationOpen()) { color = Color.GREEN; textResId = R.string.open; } else if (race.isFinished()) { color = Color.BLACK; textResId = R.string.finished; } else { color = Color.RED; textResId = R.string.closed; } holder.registerTextView.setTextColor(color); holder.registerTextView.setText(textResId); holder.registerTextView.setText(holder.registerTextView.getText().toString().toUpperCase(Locale.getDefault())); } private CharSequence formatDate(Date date) { return mDateFormat.format(date); } private CharSequence formatDayOfWeek(Context context, Date date) { Calendar time = Calendar.getInstance(Locale.getDefault()); time.setTime(date); if (CalendarUtils.isToday(time)) { return context.getString(R.string.today); } else if (CalendarUtils.isTomorrow(time)) { return context.getString(R.string.tomorrow); } else if (CalendarUtils.isYesterday(time)) { return context.getString(R.string.yesterday); } return mDayOfWeekFormat.format(date); } private CharSequence formatTime(Date date) { return mTimeFormat.format(date); } private class RaceCountDownTimer extends CountDownTimer { private ViewHolder mViewHolder; private Race mRace; public RaceCountDownTimer(ViewHolder viewHolder, Race race, long millisInFuture) { super(millisInFuture, 1000); mViewHolder = viewHolder; mRace = race; } @Override public void onFinish() { updateTimeViews(mViewHolder, mRace); } @Override public void onTick(long millisUntilFinished) { updateTimeViews(mViewHolder, mRace); } } public interface OnRaceActionClickListener { public void onForumPostClicked(Race race); public void onAddNotificationClicked(Race race); public void onRemoveNotificationClicked(Race race); } class ViewHolder { View rootView; @InjectView(R.id.title) TextView titleTextView; @InjectView(R.id.startTime) TextView timeTextView; @InjectView(R.id.register) TextView registerTextView; @InjectView(R.id.action_ImageView) ImageView actionImageView; PopupMenu popupMenu; public RaceCountDownTimer timer; public ViewHolder(View v) { this.rootView = v; ButterKnife.inject(this, v); } } }