package com.joelapenna.foursquared.widget; import com.joelapenna.foursquare.types.Checkin; import com.joelapenna.foursquare.types.FoursquareType; import com.joelapenna.foursquare.types.Group; import com.joelapenna.foursquare.types.User; import com.joelapenna.foursquared.R; import com.joelapenna.foursquared.util.RemoteResourceManager; import com.joelapenna.foursquared.util.UserUtils; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.net.Uri; import android.util.AttributeSet; import android.view.View; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Observable; import java.util.Observer; /** * A single horizontal strip of user photo views. Expected to be used from * xml resource, needs more work to make this a robust and generic control. * * @date September 15, 2010 * @author Mark Wyszomierski (markww@gmail.com) */ public class PhotoStrip extends View implements ObservableAdapter { private int mPhotoSize; private int mPhotoSpacing; private int mPhotoBorder; private int mPhotoBorderStroke; private int mPhotoBorderColor; private int mPhotoBorderStrokeColor; private Group<User> mTypes; private Map<String, Bitmap> mCachedBitmaps = new HashMap<String, Bitmap>(); private RemoteResourceManager mRrm; private RemoteResourceManagerObserver mResourcesObserver; public PhotoStrip(Context context) { super(context); } public PhotoStrip(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PhotoStrip, 0, 0); mPhotoSize = a.getDimensionPixelSize(R.styleable.PhotoStrip_photoSize, 44); mPhotoSpacing = a.getDimensionPixelSize(R.styleable.PhotoStrip_photoSpacing, 10); mPhotoBorder = a.getDimensionPixelSize(R.styleable.PhotoStrip_photoBorder, 2); mPhotoBorderStroke = a.getDimensionPixelSize(R.styleable.PhotoStrip_photoBorderStroke, 0); mPhotoBorderColor = a.getColor(R.styleable.PhotoStrip_photoBorderColor, 0xFFFFFFFF); mPhotoBorderStrokeColor = a.getColor(R.styleable.PhotoStrip_photoBorderStrokeColor, 0xFFD0D0D); a.recycle(); } public void setUsersAndRemoteResourcesManager(Group<User> users, RemoteResourceManager rrm) { mTypes = users; mRrm = rrm; mResourcesObserver = new RemoteResourceManagerObserver(); mRrm.addObserver(mResourcesObserver); invalidate(); } public void setCheckinsAndRemoteResourcesManager(Group<Checkin> checkins, RemoteResourceManager rrm) { Group<User> users = new Group<User>(); for (Checkin it : checkins) { if (it.getUser() != null) { users.add(it.getUser()); } } setUsersAndRemoteResourcesManager(users, rrm); } public void removeObserver() { mRrm.deleteObserver(mResourcesObserver); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (mRrm != null && mResourcesObserver != null) { mRrm.deleteObserver(mResourcesObserver); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG); paint.setAntiAlias(true); int width = getWidth(); int sum = mPhotoSize + mPhotoSpacing; int index = 0; while (sum < width) { if (mTypes == null || index >= mTypes.size()) { break; } Rect rcDst = new Rect( index * (mPhotoSize + mPhotoSpacing), 0, index * (mPhotoSize + mPhotoSpacing) + mPhotoSize, mPhotoSize); paint.setColor(mPhotoBorderStrokeColor); canvas.drawRect(rcDst, paint); rcDst.inset(mPhotoBorderStroke, mPhotoBorderStroke); paint.setColor(mPhotoBorderColor); canvas.drawRect(rcDst, paint); rcDst.inset(mPhotoBorder, mPhotoBorder); FoursquareType type = mTypes.get(index); Bitmap bmp = fetchBitmapForUser(type); if (bmp != null) { Rect rcSrc = new Rect(0, 0, bmp.getWidth(), bmp.getHeight()); canvas.drawBitmap(bmp, rcSrc, rcDst, paint); } sum += (mPhotoSize + mPhotoSpacing); index++; } } private Bitmap fetchBitmapForUser(FoursquareType type) { User user = null; if (type instanceof User) { user = (User)type; } else if (type instanceof Checkin) { Checkin checkin = (Checkin)type; user = checkin.getUser(); if (user == null) { return null; } } else { throw new RuntimeException("PhotoStrip can only accept Users or Checkins."); } String photoUrl = user.getPhoto(); if (mCachedBitmaps.containsKey(photoUrl)) { return mCachedBitmaps.get(photoUrl); } Uri uriPhoto = Uri.parse(photoUrl); if (mRrm.exists(uriPhoto)) { try { Bitmap bitmap = BitmapFactory.decodeStream(mRrm.getInputStream(Uri.parse(photoUrl))); mCachedBitmaps.put(photoUrl, bitmap); return bitmap; } catch (IOException e) { } } else { mRrm.request(uriPhoto); } return BitmapFactory.decodeResource(getResources(), UserUtils.getDrawableByGenderForUserThumbnail(user)); } /** * @see android.view.View#measure(int, int) */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension( measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); } /** * Determines the width of this view * @param measureSpec A measureSpec packed into an int * @return The width of the view, honoring constraints from measureSpec */ private int measureWidth(int measureSpec) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { // We were told how big to be. result = specSize; } else { if (specMode == MeasureSpec.AT_MOST) { // Use result. } else { // Use result. } } return result; } private int measureHeight(int measureSpec) { // We should be exactly as high as the specified photo size. // An exception would be if we have zero photos to display, // we're not dealing with that at the moment. return mPhotoSize + getPaddingTop() + getPaddingBottom(); } private class RemoteResourceManagerObserver implements Observer { @Override public void update(Observable observable, Object data) { postInvalidate(); } } }