/* * Copyright (C) 2012 Simon Robinson * * This file is part of Com-Me. * * Com-Me is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * Com-Me 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 Lesser General * Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with Com-Me. * If not, see <http://www.gnu.org/licenses/>. */ package ac.robinson.mediatablet.provider; import java.io.File; import java.util.ArrayList; import java.util.ListIterator; import ac.robinson.mediatablet.R; import ac.robinson.util.BitmapUtilities; import ac.robinson.util.BitmapUtilities.CacheTypeContainer; import ac.robinson.util.ImageCacheUtilities; import android.content.ContentResolver; import android.content.ContentValues; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.LightingColorFilter; import android.graphics.Paint; import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.net.Uri; import android.provider.BaseColumns; import android.util.TypedValue; import com.larvalabs.svgandroid.SVG; import com.larvalabs.svgandroid.SVGParser; public class HomesteadItem implements BaseColumns { public static final Uri CONTENT_URI = Uri.parse(MediaTabletProvider.URI_PREFIX + MediaTabletProvider.URI_AUTHORITY + MediaTabletProvider.URI_SEPARATOR + MediaTabletProvider.HOMESTEADS_LOCATION); public static final String[] PROJECTION_ALL = new String[] { HomesteadItem._ID, HomesteadItem.INTERNAL_ID, HomesteadItem.X_POSITION, HomesteadItem.Y_POSITION, HomesteadItem.COLOUR, MediaItem.DELETED }; public static final String INTERNAL_ID = "internal_id"; public static final String X_POSITION = "x_position"; public static final String Y_POSITION = "y_position"; public static final String COLOUR = "colour"; public static final String DELETED = "deleted"; public static final String DEFAULT_SORT_ORDER = X_POSITION + " ASC"; private String mInternalId; private int mXPosition; private int mYPosition; private int mColour; private int mDeleted; // for drawing only; not saved in database private boolean mSelected; private ColorFilter mColourFilter; private static final int DEFAULT_COLOUR = Color.WHITE; public HomesteadItem(String internalId, int xPosition, int yPosition) { mInternalId = internalId; mXPosition = xPosition; mYPosition = yPosition; mColour = DEFAULT_COLOUR; mDeleted = 0; mSelected = false; } public HomesteadItem(int xPosition, int yPosition) { this(MediaTabletProvider.getNewInternalId(), xPosition, yPosition); } public HomesteadItem() { this(0, 0); } public String getInternalId() { return mInternalId; } public int getXPosition() { return mXPosition; } public void setXPosition(int xPosition) { mXPosition = xPosition; } public int getYPosition() { return mYPosition; } public void setYPosition(int yPosition) { mYPosition = yPosition; } public int getColour() { return mColour; } public void setColour(int colour) { mColourFilter = null; mColour = colour; } public ColorFilter getColourFilter() { if (mColour != DEFAULT_COLOUR) { // no need for white (the default colour) if (mColourFilter == null) { mColourFilter = new LightingColorFilter(mColour, 1); } return mColourFilter; } return null; } public boolean getDeleted() { return mDeleted == 0 ? false : true; } public void setDeleted(boolean deleted) { mDeleted = deleted ? 1 : 0; } public String getCacheId() { return getCacheId(mInternalId); } public static String getCacheId(String internalId) { return internalId; } public static String getInternalIdFromCacheId(String cacheId) { return cacheId; } public boolean getSelected() { return mSelected; } public void setSelected(boolean selected) { mSelected = selected; } public Bitmap loadIcon(Resources resources, ContentResolver contentResolver, CacheTypeContainer cacheTypeContainer) { return loadIcon(resources, contentResolver, cacheTypeContainer, 0); } public Bitmap loadIcon(Resources resources, ContentResolver contentResolver, CacheTypeContainer cacheTypeContainer, int requestedSize) { // must always be PNG for transparent background cacheTypeContainer.type = Bitmap.CompressFormat.PNG; int totalSize = resources.getDimensionPixelSize(R.dimen.homestead_icon_total_size); int requestedTotalSizeRevised = requestedSize <= 0 ? totalSize : requestedSize; Bitmap homesteadBitmap = Bitmap.createBitmap(requestedTotalSizeRevised, requestedTotalSizeRevised, ImageCacheUtilities.mBitmapFactoryOptions.inPreferredConfig); Canvas homesteadBitmapCanvas = new Canvas(homesteadBitmap); int iconSize = resources.getDimensionPixelSize(R.dimen.homestead_icon_size); int requestedSizeRevised = requestedSize <= 0 ? iconSize : Math.round((iconSize / (float) totalSize) * requestedSize); int iconStart = Math.round((requestedTotalSizeRevised - requestedSizeRevised) / 2f); Rect drawRect = new Rect(iconStart, iconStart, iconStart + requestedSizeRevised, iconStart + requestedSizeRevised); // using SVG so that we don't need resolution-specific icons SVG audioSVG = SVGParser.getSVGFromResource(resources, R.raw.ic_homestead); homesteadBitmapCanvas.drawPicture(audioSVG.getPicture(), drawRect); ArrayList<PersonItem> homesteadPeople = PersonManager.findPeopleByParentId(contentResolver, mInternalId); int numPeople = homesteadPeople.size(); if (numPeople > 0) { Bitmap personBitmap; Bitmap unknownPersonBitmap = null; int strokeWidth = resources.getDimensionPixelSize(R.dimen.icon_border_width); Paint personPaint = BitmapUtilities.getPaint(resources.getColor(R.color.icon_person_border), strokeWidth); personPaint.setStyle(Paint.Style.STROKE); Canvas clipCanvas; // make sure we can fit all the images in float scaleFactor = 1; TypedValue resourceValue = new TypedValue(); resources.getValue(R.attr.homestead_icon_person_maximum_diameter_factor, resourceValue, true); int personIconSize = Math.round(resourceValue.getFloat() * requestedTotalSizeRevised); if (personIconSize * numPeople > 360) { scaleFactor = 360f / numPeople / personIconSize; personIconSize = Math.round(personIconSize * scaleFactor); resources.getValue(R.attr.homestead_icon_person_minimum_diameter_factor, resourceValue, true); int minimumSize = Math.round(resourceValue.getFloat() * requestedTotalSizeRevised); if (personIconSize < minimumSize) { personIconSize = minimumSize; } } // set up for drawing outlined person icons int halfPersonIconSize = personIconSize / 2; int halfStrokeWidth = (int) Math.floor(strokeWidth / 2f); Point centrePoint = new Point(Math.round(homesteadBitmap.getWidth() / 2f), Math.round(homesteadBitmap .getHeight() / 2f)); Point startPoint = new Point(Math.round(homesteadBitmap.getWidth() - halfPersonIconSize - halfStrokeWidth), Math.round(homesteadBitmap.getHeight() / 2f)); double currentAngle = (numPeople == 2 ? 0 : 2 * Math.PI * 7 / 8d); // make 2 people look better Point drawPoint = rotatePoint(new Point(startPoint.x, startPoint.y), startPoint, centrePoint, currentAngle); double angleIncrement = (Math.PI * 2) / numPeople; // set up the clipped bitmap for outlining person icons - see: http://stackoverflow.com/a/1718738 Bitmap roundedBitmap = Bitmap.createBitmap(personIconSize, personIconSize, ImageCacheUtilities.mBitmapFactoryOptions.inPreferredConfig); Canvas roundedCanvas = new Canvas(roundedBitmap); Paint clipPaint = new Paint(Paint.ANTI_ALIAS_FLAG); clipPaint.setColor(Color.BLACK); roundedCanvas.drawCircle(halfPersonIconSize, halfPersonIconSize, halfPersonIconSize, clipPaint); clipPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); // finally, draw the actual icons // for (PersonItem person : homesteadPeople) { //instead, reverse the list to get items in better draw order for (ListIterator<PersonItem> it = homesteadPeople.listIterator(homesteadPeople.size()); it.hasPrevious();) { final PersonItem person = it.previous(); final File pictureFile = person.getProfilePictureFile(); if (pictureFile.exists()) { personBitmap = BitmapUtilities.loadAndCreateScaledBitmap(pictureFile.getAbsolutePath(), personIconSize, personIconSize, BitmapUtilities.ScalingLogic.CROP, true); } else { if (unknownPersonBitmap == null) { unknownPersonBitmap = Bitmap.createBitmap(personIconSize, personIconSize, ImageCacheUtilities.mBitmapFactoryOptions.inPreferredConfig); Rect unknownPersonRect = new Rect(0, 0, personIconSize, personIconSize); SVG personSVG = SVGParser.getSVGFromResource(resources, PersonItem.UNKNOWN_PERSON_ICON); Canvas unknownPersonCanvas = new Canvas(unknownPersonBitmap); unknownPersonCanvas.drawColor(Color.DKGRAY); unknownPersonCanvas.drawPicture(personSVG.getPicture(), unknownPersonRect); } personBitmap = unknownPersonBitmap; } currentAngle -= angleIncrement; // because we're drawing in reverse drawPoint = rotatePoint(drawPoint, startPoint, centrePoint, currentAngle); // clip the bitmap clipCanvas = new Canvas(personBitmap); clipCanvas.drawBitmap(roundedBitmap, 0, 0, clipPaint); // draw the clipped bitmap and an outline homesteadBitmapCanvas.drawBitmap(personBitmap, drawPoint.x - halfPersonIconSize, drawPoint.y - halfPersonIconSize, personPaint); homesteadBitmapCanvas.drawCircle(drawPoint.x, drawPoint.y, halfPersonIconSize - halfStrokeWidth + 1, personPaint); } } return homesteadBitmap; } private Point rotatePoint(Point drawPoint, Point startPoint, Point centrePoint, double angle) { double cosT = Math.cos(angle); double sinT = Math.sin(angle); drawPoint.x = (int) Math.round(cosT * (startPoint.x - centrePoint.x) - sinT * (startPoint.y - centrePoint.y) + centrePoint.x); drawPoint.y = (int) Math.round(sinT * (startPoint.x - centrePoint.x) + cosT * (startPoint.y - centrePoint.y) + centrePoint.y); return drawPoint; } public ContentValues getContentValues() { final ContentValues values = new ContentValues(); values.put(INTERNAL_ID, mInternalId); values.put(X_POSITION, mXPosition); values.put(Y_POSITION, mYPosition); values.put(COLOUR, mColour); values.put(DELETED, mDeleted); return values; } public static HomesteadItem fromCursor(Cursor c) { final HomesteadItem homestead = new HomesteadItem(); homestead.mInternalId = c.getString(c.getColumnIndexOrThrow(INTERNAL_ID)); homestead.mXPosition = c.getInt(c.getColumnIndexOrThrow(X_POSITION)); homestead.mYPosition = c.getInt(c.getColumnIndexOrThrow(Y_POSITION)); homestead.mColour = c.getInt(c.getColumnIndexOrThrow(COLOUR)); homestead.mDeleted = c.getInt(c.getColumnIndexOrThrow(DELETED)); return homestead; } @Override public String toString() { return "HomesteadItem[" + mInternalId + "," + mXPosition + "," + mYPosition + "," + mColour + "," + mDeleted + "]"; } }