/* * Copyright 2014 Bevbot LLC <info@bevbot.com> * * This file is part of the Kegtab package from the Kegbot project. For * more information on Kegtab or Kegbot, see <http://kegbot.org/>. * * Kegtab 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, version 2. * * Kegtab 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 Kegtab. If not, see <http://www.gnu.org/licenses/>. */ package org.kegbot.app; import android.app.Activity; import android.app.ListFragment; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.text.Html; import android.text.format.DateUtils; import android.util.Log; import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.AnimationSet; import android.view.animation.LayoutAnimationController; import android.view.animation.TranslateAnimation; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.squareup.otto.Subscribe; import org.kegbot.app.config.AppConfiguration; import org.kegbot.app.event.SystemEventListUpdateEvent; import org.kegbot.app.util.ImageDownloader; import org.kegbot.app.util.Units; import org.kegbot.core.KegbotCore; import org.kegbot.core.SyncManager; import org.kegbot.proto.Models; import org.kegbot.proto.Models.Drink; import org.kegbot.proto.Models.SystemEvent; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; /** * Lists recent events. * * @author mike wakerly (opensource@hoho.com) */ public class EventListFragment extends ListFragment { private static final String TAG = EventListFragment.class.getSimpleName(); /** Maximum number of events to show/retain. */ private static final int MAX_EVENTS = 20; private static final long REFRESH_INTERVAL_MILLIS = TimeUnit.MINUTES.toMillis(1); private View mView; private ArrayAdapter<SystemEvent> mAdapter; private KegbotCore mCore; private ImageDownloader mImageDownloader; private final Handler mHandler = new Handler(Looper.getMainLooper()); private final List<SystemEvent> mCachedEvents = Lists.newArrayList(); /** Refreshes event timestamps by invalidating all ListView children periodically. */ private final Runnable mTimeUpdateRunnable = new Runnable() { @Override public void run() { ListView v = getListView(); if (v != null) { v.invalidateViews(); } mHandler.postDelayed(this, REFRESH_INTERVAL_MILLIS); } }; /** * */ private class EventListArrayAdapter extends ArrayAdapter<SystemEvent> { private EventListArrayAdapter(Context context, int resource, int textViewResourceId) { super(context, resource, textViewResourceId); } @Override public View getView(int position, View convertView, ViewGroup parent) { final View view = super.getView(position, convertView, parent); final SystemEvent eventDetail = getItem(position); try { formatEvent(eventDetail, view); } catch (Throwable e) { Log.wtf(TAG, "UNCAUGHT EXCEPTION", e); } return view; } private void formatEvent(SystemEvent event, View view) { // Common: image. final ImageView icon = (ImageView) view.findViewById(R.id.eventIcon); icon.setVisibility(View.GONE); if (event.hasImage()) { final String imageUrl = event.getImage().getThumbnailUrl(); if (!Strings.isNullOrEmpty(imageUrl)) { icon.setVisibility(View.VISIBLE); icon.setImageBitmap(null); // Default to unknown drinker, may be immediately replaced by downloader. icon.setBackgroundResource(R.drawable.unknown_drinker); mImageDownloader.download(imageUrl, icon); } } final String eventTextContent; final String userNameString = getUsernameForEvent(event); if (userNameString != null) { eventTextContent = "<b>" + userNameString + "</b> " + getTitle(event); } else { eventTextContent = getTitle(event); } final TextView eventText = (TextView) view.findViewById(R.id.eventText); eventText.setText(Html.fromHtml(eventTextContent)); // Date and time. TextView dateView = (TextView) view.findViewById(R.id.eventDate); String isoTime = event.getTime(); try { long time = org.kegbot.app.util.DateUtils.dateFromIso8601String(isoTime); CharSequence relTime = DateUtils.getRelativeDateTimeString(getContext(), time, 60 * 1000, 7 * 24 * 60 * 60 * 100, 0); dateView.setText(relTime); dateView.setVisibility(View.VISIBLE); } catch (IllegalArgumentException e) { dateView.setVisibility(View.GONE); } } private String getTitle(SystemEvent eventDetail) { final String kind = eventDetail.getKind(); final Drink drink = eventDetail.getDrink(); String result; if ("keg_ended".equals(kind)) { result = "ended"; } else if ("keg_tapped".equals(kind)) { result = "tapped"; } else if ("drink_poured".equals(kind)) { final AppConfiguration appConfig = KegbotCore.getInstance(getContext()).getConfiguration(); Pair<String, String> qty = Units.localize(appConfig, drink.getVolumeMl()); result = String.format("poured %s %s", qty.first, qty.second); } else if ("session_joined".equals(kind)) { result = "started drinking"; } else if ("session_started".equals(kind)) { result = "started a new session"; } else { result = "Unknown event"; } return result; } private String getUsernameForEvent(SystemEvent eventDetail) { final String userName; if (!eventDetail.hasUser()) { userName = "a guest"; } else { final Models.User user = eventDetail.getUser(); if (user.hasDisplayName()) { userName = user.getDisplayName(); } else { userName = user.getUsername(); } } final String kind = eventDetail.getKind(); if ("drink_poured".equals(kind)) { return userName; } else if ("session_joined".equals(kind)) { return userName; } else if ("session_started".equals(kind)) { return userName; } else if ("keg_ended".equals(kind) || "keg_tapped".equals(kind)) { if (eventDetail.hasKeg()) { return "Keg " + eventDetail.getKeg().getId(); } } return null; } } @Override public void onAttach(Activity activity) { super.onAttach(activity); mImageDownloader = KegbotCore.getInstance(activity).getImageDownloader(); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mAdapter = new EventListArrayAdapter(getActivity(), R.layout.event_list_item, R.id.eventText); AnimationSet set = new AnimationSet(true); Animation animation = new AlphaAnimation(0.0f, 1.0f); animation.setDuration(300); set.addAnimation(animation); animation = new TranslateAnimation( Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, -1.0f, Animation.RELATIVE_TO_SELF, 0.0f ); animation.setDuration(300); set.addAnimation(animation); LayoutAnimationController controller = new LayoutAnimationController(set, 0.3f); getListView().setLayoutAnimation(controller); setListAdapter(mAdapter); } @Override public void onResume() { super.onResume(); mHandler.postDelayed(mTimeUpdateRunnable, REFRESH_INTERVAL_MILLIS); mCore = KegbotCore.getInstance(getActivity()); mCore.getBus().register(this); } @Override public void onPause() { mCore.getBus().unregister(this); mHandler.removeCallbacks(mTimeUpdateRunnable); super.onPause(); } @Subscribe public void onEventListUpdate(SystemEventListUpdateEvent event) { List<SystemEvent> newEvents = event.getEvents(); if (!newEvents.isEmpty()) { Log.d(TAG, "Events updated: " + newEvents.size()); for (SystemEvent e : newEvents) { if (!mCachedEvents.contains(e)) { mCachedEvents.add(e); } } Collections.sort(mCachedEvents, SyncManager.EVENTS_DESCENDING); while (mCachedEvents.size() > MAX_EVENTS) { mCachedEvents.remove(mCachedEvents.size() - 1); } mAdapter.clear(); mAdapter.addAll(mCachedEvents); } if (mView != null) { if (mCachedEvents.isEmpty()) { mView.setVisibility(View.GONE); } else { mView.setVisibility(View.VISIBLE); } } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mView = inflater.inflate(R.layout.event_list_fragment_layout, container, false); return mView; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } }