/**
* Copyright 2008 Joe LaPenna
*/
package com.joelapenna.foursquared.widget;
import com.joelapenna.foursquare.types.Category;
import com.joelapenna.foursquare.types.Group;
import com.joelapenna.foursquare.types.Stats;
import com.joelapenna.foursquare.types.Venue;
import com.joelapenna.foursquared.FoursquaredSettings;
import com.joelapenna.foursquared.R;
import com.joelapenna.foursquared.util.RemoteResourceManager;
import com.joelapenna.foursquared.util.StringFormatters;
import com.joelapenna.foursquared.util.VenueUtils;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.io.File;
import java.io.IOException;
import java.util.Observable;
import java.util.Observer;
/**
* @author Joe LaPenna (joe@joelapenna.com)
*/
public class VenueListAdapter extends BaseVenueAdapter implements ObservableAdapter {
private static final String TAG = "VenueListAdapter";
private static final boolean DEBUG = FoursquaredSettings.DEBUG;
private LayoutInflater mInflater;
private RemoteResourceManager mRrm;
private Handler mHandler;
private RemoteResourceManagerObserver mResourcesObserver;
public VenueListAdapter(Context context, RemoteResourceManager rrm) {
super(context);
mInflater = LayoutInflater.from(context);
mHandler = new Handler();
mRrm = rrm;
mResourcesObserver = new RemoteResourceManagerObserver();
mRrm.addObserver(mResourcesObserver);
}
public void removeObserver() {
mRrm.deleteObserver(mResourcesObserver);
}
/**
* Make a view to hold each row.
*
* @see android.widget.ListAdapter#getView(int, android.view.View,
* android.view.ViewGroup)
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unnecessary
// calls to findViewById() on each row.
ViewHolder holder;
// When convertView is not null, we can reuse it directly, there is no
// need to re-inflate it. We only inflate a new View when the
// convertView supplied by ListView is null.
if (convertView == null) {
convertView = mInflater.inflate(R.layout.venue_list_item, null);
// Creates a ViewHolder and store references to the two children
// views we want to bind data to.
holder = new ViewHolder();
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
holder.venueName = (TextView) convertView.findViewById(R.id.venueName);
holder.locationLine1 = (TextView) convertView.findViewById(R.id.venueLocationLine1);
holder.iconSpecial = (ImageView) convertView.findViewById(R.id.iconSpecialHere);
holder.venueDistance = (TextView) convertView.findViewById(R.id.venueDistance);
holder.iconTrending = (ImageView) convertView.findViewById(R.id.iconTrending);
holder.venueCheckinCount = (TextView) convertView.findViewById(R.id.venueCheckinCount);
holder.todoHere = (ImageView) convertView.findViewById(R.id.venueTodoCorner);
convertView.setTag(holder);
} else {
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
holder = (ViewHolder) convertView.getTag();
}
// Check if the venue category icon exists on disk, if not default to a
// venue pin icon.
Venue venue = (Venue) getItem(position);
Category category = venue.getCategory();
if (category != null) {
Uri photoUri = Uri.parse(category.getIconUrl());
try {
Bitmap bitmap = BitmapFactory.decodeStream(mRrm.getInputStream(photoUri));
holder.icon.setImageBitmap(bitmap);
} catch (IOException e) {
setDefaultVenueCategoryIcon(venue, holder);
}
} else {
// If there is no category for this venue, fall back to the original
// method of the
// blue/grey pin depending on if the user has been there or not.
setDefaultVenueCategoryIcon(venue, holder);
}
// Venue name.
holder.venueName.setText(venue.getName());
// Venue street address (cross streets | city, state zip).
if (!TextUtils.isEmpty(venue.getAddress())) {
holder.locationLine1.setText(StringFormatters.getVenueLocationFull(venue));
} else {
holder.locationLine1.setText("");
}
// If there's a special here, show the special here icon.
if (VenueUtils.getSpecialHere(venue)) {
holder.iconSpecial.setVisibility(View.VISIBLE);
} else {
holder.iconSpecial.setVisibility(View.GONE);
}
// Show venue distance.
if (venue.getDistance() != null) {
holder.venueDistance.setText(venue.getDistance() + " meters");
} else {
holder.venueDistance.setText("");
}
// If more than two people here, then show trending text.
Stats stats = venue.getStats();
if (stats != null &&
!stats.getHereNow().equals("0") &&
!stats.getHereNow().equals("1") &&
!stats.getHereNow().equals("2")) {
holder.iconTrending.setVisibility(View.VISIBLE);
holder.venueCheckinCount.setVisibility(View.VISIBLE);
holder.venueCheckinCount.setText(stats.getHereNow() + " people here");
} else {
holder.iconTrending.setVisibility(View.GONE);
holder.venueCheckinCount.setVisibility(View.GONE);
}
// If we have a todo here, show the corner folded over.
if (venue.getHasTodo()) {
holder.todoHere.setVisibility(View.VISIBLE);
} else {
holder.todoHere.setVisibility(View.INVISIBLE);
}
return convertView;
}
private void setDefaultVenueCategoryIcon(Venue venue, ViewHolder holder) {
holder.icon.setImageResource(R.drawable.category_none);
}
@Override
public void setGroup(Group<Venue> g) {
super.setGroup(g);
for (Venue it : g) {
// Start download of category icon if not already in the cache.
// At the same time, check the age of each of these images, if
// expired, delete and request a fresh copy. This should be
// removed once category icon set urls are versioned.
Category category = it.getCategory();
if (category != null) {
Uri photoUri = Uri.parse(category.getIconUrl());
File file = mRrm.getFile(photoUri);
if (file != null) {
if (System.currentTimeMillis() - file.lastModified() > FoursquaredSettings.CATEGORY_ICON_EXPIRATION) {
mRrm.invalidate(photoUri);
file = null;
}
}
if (file == null) {
mRrm.request(photoUri);
}
}
}
}
private class RemoteResourceManagerObserver implements Observer {
@Override
public void update(Observable observable, Object data) {
if (DEBUG) Log.d(TAG, "Fetcher got: " + data);
mHandler.post(new Runnable() {
@Override
public void run() {
notifyDataSetChanged();
}
});
}
}
private static class ViewHolder {
ImageView icon;
TextView venueName;
TextView locationLine1;
ImageView iconSpecial;
TextView venueDistance;
ImageView iconTrending;
TextView venueCheckinCount;
ImageView todoHere;
}
}