/*
* 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.Locale;
import ac.robinson.mediatablet.MediaTablet;
import ac.robinson.mediatablet.R;
import ac.robinson.mediautilities.FrameMediaContainer;
import ac.robinson.mediautilities.MediaUtilities;
import ac.robinson.mediautilities.SMILUtilities;
import ac.robinson.util.BitmapUtilities;
import ac.robinson.util.BitmapUtilities.CacheTypeContainer;
import ac.robinson.util.IOUtilities;
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.Paint;
import android.graphics.Rect;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.provider.BaseColumns;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.TypedValue;
import com.larvalabs.svgandroid.SVG;
import com.larvalabs.svgandroid.SVGParser;
public class MediaItem implements BaseColumns {
public static final Uri CONTENT_URI = Uri.parse(MediaTabletProvider.URI_PREFIX + MediaTabletProvider.URI_AUTHORITY
+ MediaTabletProvider.URI_SEPARATOR + MediaTabletProvider.MEDIA_LOCATION);
public static final String[] PROJECTION_ALL = new String[] { MediaItem._ID, MediaItem.INTERNAL_ID,
MediaItem.PARENT_ID, MediaItem.DATE_CREATED, MediaItem.FILE_EXTENSION, MediaItem.MEDIA_EXTRA,
MediaItem.TYPE, MediaItem.VISIBILITY, MediaItem.DELETED };
public static final String INTERNAL_ID = "internal_id";
public static final String PARENT_ID = "parent_id";
public static final String DATE_CREATED = "date_created";
public static final String FILE_EXTENSION = "file_name";
public static final String MEDIA_EXTRA = "media_extra";
public static final String TYPE = "type";
public static final String VISIBILITY = "visibility";
public static final String DELETED = "deleted";
public static final int MEDIA_PRIVATE = 0;
public static final int MEDIA_PUBLIC = 1;
// added to filenames to distinguish between types
public static final String ICON_PUBLIC = "-p";
public static final String DEFAULT_SORT_ORDER = DATE_CREATED + " DESC";
private String mInternalId;
private String mParentId;
private long mCreationDate;
private String mFileExtension;
private String mMediaExtra;
private int mType;
private int mVisibility;
private int mDeleted;
public MediaItem(String internalId, String parentId, String originalFileName, int type, int visibility) {
mInternalId = internalId;
mParentId = parentId;
mCreationDate = System.currentTimeMillis();
setFileExtension(IOUtilities.getFileExtension(originalFileName));
setOriginalFileName(originalFileName);
mType = type;
mVisibility = visibility;
mDeleted = 0;
}
public MediaItem(String parentId, String originalFileName, int visibility) {
this(MediaTabletProvider.getNewInternalId(), parentId, originalFileName, MediaItem
.getMediaTypeFromFileName(originalFileName), visibility);
}
public MediaItem() {
this(null, null, MEDIA_PUBLIC);
}
public String getInternalId() {
return mInternalId;
}
public String getParentId() {
return mParentId;
}
public void setParentId(String parentId) {
mParentId = parentId;
}
public long getCreationDate() {
return mCreationDate;
}
public String getFileExtension() {
return mFileExtension;
}
public void setFileExtension(String fileExtension) {
mFileExtension = (fileExtension != null ? fileExtension.toLowerCase(Locale.ENGLISH) : null);
}
public String getOriginalFileName() {
return mMediaExtra;
}
public void setOriginalFileName(String mediaExtra) {
mMediaExtra = mediaExtra;
}
public void setTextExtra(String textExtra) {
mMediaExtra = textExtra;
}
public int getType() {
return mType;
}
public boolean isPubliclyShared() {
return (mVisibility == MEDIA_PUBLIC);
}
public void setPubliclySharedStatus(int sharedStatus) {
mVisibility = sharedStatus;
}
public File getFile() {
return getFile(mParentId, mInternalId, mFileExtension);
}
public static File getFile(String mediaParentId, String mediaInternalId, String mediaFileExtension) {
final File filePath = new File(PersonItem.getStorageDirectory(mediaParentId), mediaInternalId + "."
+ mediaFileExtension);
return filePath;
}
public static String getInternalId(String mediaFilePath) {
return IOUtilities.removeExtension(mediaFilePath);
}
public boolean getDeleted() {
return mDeleted == 0 ? false : true;
}
public void setDeleted(boolean deleted) {
mDeleted = deleted ? 1 : 0;
}
public String getCacheId(int visibility) {
return getCacheId(mInternalId, visibility);
}
public static String getCacheId(String internalId, int visibility) {
return (visibility == MEDIA_PRIVATE ? internalId : internalId + ICON_PUBLIC);
}
public static int getMediaTypeFromFileName(String fileName) {
if (fileName == null) {
return MediaTabletProvider.TYPE_UNKNOWN;
}
fileName = fileName.toLowerCase(Locale.ENGLISH);
if (fileName.endsWith(MediaUtilities.SYNC_FILE_EXTENSION)) { // note use of sync, rather than smil
return MediaTabletProvider.TYPE_NARRATIVE;
}
for (String extension : MediaTablet.TYPE_IMAGE_EXTENSIONS) {
if (fileName.endsWith(extension)) {
return MediaTabletProvider.TYPE_IMAGE_BACK;
}
}
for (String extension : MediaTablet.TYPE_VIDEO_EXTENSIONS) {
if (fileName.endsWith(extension)) {
return MediaTabletProvider.TYPE_VIDEO;
}
}
for (String extension : MediaTablet.TYPE_AUDIO_EXTENSIONS) {
if (fileName.endsWith(extension)) {
return MediaTabletProvider.TYPE_AUDIO;
}
}
for (String extension : MediaTablet.TYPE_TEXT_EXTENSIONS) {
if (fileName.endsWith(extension)) {
return MediaTabletProvider.TYPE_TEXT;
}
}
return MediaTabletProvider.TYPE_UNKNOWN;
}
/**
* contentResolver may be null if parentOverlayId is null
*
* @param resources
* @param cacheTypeContainer
* @param contentResolver
* @param parentOverlayId
* @return
*/
public Bitmap loadIcon(Resources resources, CacheTypeContainer cacheTypeContainer, ContentResolver contentResolver,
String parentOverlayId) {
int iconWidth = resources.getDimensionPixelSize(R.dimen.media_icon_width);
int iconHeight = resources.getDimensionPixelSize(R.dimen.media_icon_height);
Bitmap iconBitmap;
Bitmap mediaBitmap = Bitmap.createBitmap(iconWidth, iconHeight,
ImageCacheUtilities.mBitmapFactoryOptions.inPreferredConfig);
Canvas mediaCanvas = new Canvas(mediaBitmap);
Paint mediaPaint = BitmapUtilities.getPaint(Color.BLACK, 1);
mediaCanvas.drawColor(resources.getColor(R.color.icon_background));
boolean noMediaIcon = false;
if (getFile() != null && getFile().exists()) {
String mediaPath = getFile().getAbsolutePath();
switch (mType) {
case MediaTabletProvider.TYPE_IMAGE_BACK:
case MediaTabletProvider.TYPE_IMAGE_FRONT:
iconBitmap = BitmapUtilities.loadAndCreateScaledBitmap(mediaPath, iconWidth, iconHeight,
BitmapUtilities.ScalingLogic.CROP, true);
if (iconBitmap != null) {
mediaCanvas.drawBitmap(iconBitmap, (iconWidth - iconBitmap.getWidth()) / 2,
(iconHeight - iconBitmap.getHeight()) / 2, mediaPaint);
} else {
noMediaIcon = true;
}
break;
case MediaTabletProvider.TYPE_VIDEO:
// MINI_KIND: 512 x 384 thumbnail MICRO_KIND: 96 x 96 thumbnail
// TODO: this will never work if the video is in a non-public location...
iconBitmap = ThumbnailUtils.createVideoThumbnail(mediaPath, MediaStore.Video.Thumbnails.MINI_KIND);
if (iconBitmap != null) {
BitmapUtilities.scaleBitmap(iconBitmap, iconWidth, iconHeight,
BitmapUtilities.ScalingLogic.CROP);
mediaCanvas.drawBitmap(iconBitmap, (iconWidth - iconBitmap.getWidth()) / 2,
(iconHeight - iconBitmap.getHeight()) / 2, mediaPaint);
} else {
noMediaIcon = true;
}
break;
case MediaTabletProvider.TYPE_AUDIO:
noMediaIcon = true; // just show the icon
break;
case MediaTabletProvider.TYPE_TEXT:
BitmapUtilities.drawScaledText(mMediaExtra, mediaCanvas, mediaPaint,
resources.getColor(R.color.icon_text_no_image), 0,
resources.getDimensionPixelSize(R.dimen.icon_text_padding), 0, false, 0, false, iconHeight,
resources.getDimensionPixelSize(R.dimen.icon_maximum_text_size),
resources.getInteger(R.integer.icon_maximum_text_characters_per_line));
break;
case MediaTabletProvider.TYPE_NARRATIVE:
// draw the first frame of the narrative, or story icon if not present
// add audio overlay if present
ArrayList<FrameMediaContainer> narrativeFirstFrame = SMILUtilities.getSMILFrameList(getFile(), 1,
false, 1, false);
TypedValue resourceValue = new TypedValue();
if (narrativeFirstFrame != null && narrativeFirstFrame.size() == 1) {
FrameMediaContainer firstFrame = narrativeFirstFrame.get(0);
boolean hasImage = false;
if (firstFrame.mImagePath != null) {
iconBitmap = BitmapUtilities.loadAndCreateScaledBitmap(firstFrame.mImagePath, iconWidth,
iconHeight, BitmapUtilities.ScalingLogic.CROP, true);
if (iconBitmap != null) {
mediaCanvas.drawBitmap(iconBitmap, (iconWidth - iconBitmap.getWidth()) / 2,
(iconHeight - iconBitmap.getHeight()) / 2, mediaPaint);
hasImage = true;
}
}
boolean hasText = !TextUtils.isEmpty(firstFrame.mTextContent);
if (hasText) {
int textSpacing = resources.getDimensionPixelSize(R.dimen.icon_text_padding);
int textCornerRadius = resources.getDimensionPixelSize(R.dimen.icon_text_corner_radius);
BitmapUtilities.drawScaledText(
firstFrame.mTextContent,
mediaCanvas,
mediaPaint,
(hasImage ? resources.getColor(R.color.icon_text_with_image) : resources
.getColor(R.color.icon_text_no_image)),
(hasImage ? resources.getColor(R.color.icon_text_background) : 0),
resources.getDimensionPixelSize(R.dimen.icon_text_padding),
textCornerRadius,
hasImage,
0,
false,
(hasImage ? resources
.getDimensionPixelSize(R.dimen.icon_maximum_text_height_with_image)
- textSpacing : iconHeight - textSpacing), resources
.getDimensionPixelSize(R.dimen.icon_maximum_text_size), resources
.getInteger(R.integer.icon_maximum_text_characters_per_line));
}
boolean hasAudio = firstFrame.mAudioPaths.size() > 0;
resources.getValue(R.attr.icon_overlay_spacing_factor, resourceValue, true);
float spacingFactor = resourceValue.getFloat();
int iconSpacingHorizontal = Math.round(iconWidth * spacingFactor);
int iconSpacingVertical = Math.round(iconHeight * spacingFactor);
resources.getValue(R.attr.icon_overlay_svg_scale_factor, resourceValue, true);
float scaleFactor = resourceValue.getFloat();
Rect drawRect = null;
if (hasAudio) {
int resourceId = 0;
if (!(hasImage || hasText)) {
drawRect = new Rect(0, 0, iconWidth, iconHeight);
resourceId = R.raw.ic_audio_playback;
} else {
resourceId = R.raw.overlay_audio;
drawRect = new Rect(iconWidth - Math.round(iconWidth * scaleFactor)
- iconSpacingHorizontal, iconHeight - Math.round(iconHeight * scaleFactor)
- iconSpacingVertical, iconWidth - iconSpacingHorizontal, iconHeight
- iconSpacingVertical);
}
// using SVG so that we don't need resolution-specific icons
SVG audioSVG = SVGParser.getSVGFromResource(resources, resourceId);
mediaCanvas.drawPicture(audioSVG.getPicture(), drawRect);
}
if (hasImage || hasAudio || hasText) {
drawRect = new Rect(
iconWidth - Math.round(iconWidth * scaleFactor) - iconSpacingHorizontal,
iconSpacingVertical, iconWidth - iconSpacingHorizontal, iconSpacingVertical
+ Math.round(iconHeight * scaleFactor));
// using SVG so that we don't need resolution-specific icons
SVG narrativeSVG = SVGParser.getSVGFromResource(resources, R.raw.ic_narrative);
mediaCanvas.drawPicture(narrativeSVG.getPicture(), drawRect);
} else {
noMediaIcon = true;
}
} else {
noMediaIcon = true;
}
break;
case MediaTabletProvider.TYPE_UNKNOWN:
default:
noMediaIcon = true;
break;
}
}
if (noMediaIcon) {
int iconId = R.raw.ic_unknown_media;
switch (mType) {
case MediaTabletProvider.TYPE_IMAGE_BACK:
case MediaTabletProvider.TYPE_IMAGE_FRONT:
iconId = R.raw.ic_unknown_image;
break;
case MediaTabletProvider.TYPE_AUDIO:
iconId = R.raw.ic_unknown_audio;
break;
case MediaTabletProvider.TYPE_VIDEO:
iconId = R.raw.ic_unknown_video;
break;
case MediaTabletProvider.TYPE_NARRATIVE:
iconId = R.raw.ic_narrative;
break;
case MediaTabletProvider.TYPE_TEXT:
case MediaTabletProvider.TYPE_UNKNOWN:
default:
break;
}
// using SVG so that we don't need resolution-specific icons
Rect drawRect = new Rect(0, 0, iconWidth, iconHeight);
SVG noMediaSVG = SVGParser.getSVGFromResource(resources, iconId);
mediaCanvas.drawPicture(noMediaSVG.getPicture(), drawRect);
cacheTypeContainer.type = Bitmap.CompressFormat.PNG;
}
if ((noMediaIcon || mType == MediaTabletProvider.TYPE_AUDIO) && getOriginalFileName() != null) {
BitmapUtilities.drawScaledText(getOriginalFileName(), mediaCanvas, mediaPaint,
resources.getColor(R.color.icon_text_with_image), resources.getColor(R.color.icon_text_background),
resources.getDimensionPixelSize(R.dimen.icon_text_padding), 0, true, 0, false, iconHeight / 3,
resources.getDimensionPixelSize(R.dimen.icon_maximum_text_size),
resources.getInteger(R.integer.icon_maximum_text_characters_per_line));
}
// add the parent overlay if appropriate
if (parentOverlayId != null) {
TypedValue resourceValue = new TypedValue();
resources.getValue(R.attr.media_icon_person_size_factor, resourceValue, true);
float scaleFactor = resourceValue.getFloat();
int personWidth = Math.round(iconWidth * scaleFactor);
int personHeight = Math.round(iconHeight * scaleFactor);
File personFile = new File(MediaTablet.DIRECTORY_THUMBS, PersonItem.getCacheId(parentOverlayId));
if (!personFile.exists() && MediaTablet.DIRECTORY_THUMBS != null) {
// create unknown person icon for unknown public media (bad place, but not many other places are better)
if (PersonItem.UNKNOWN_PERSON_ID.equals(parentOverlayId)) {
Bitmap personBitmap = Bitmap.createBitmap(personWidth, personHeight,
ImageCacheUtilities.mBitmapFactoryOptions.inPreferredConfig);
Canvas personCanvas = new Canvas(personBitmap);
personCanvas.drawColor(resources.getColor(R.color.icon_background));
Rect drawRect = new Rect(0, 0, personWidth, personHeight);
SVG personSVG = SVGParser.getSVGFromResource(resources, PersonItem.UNKNOWN_PERSON_ICON);
personCanvas.drawPicture(personSVG.getPicture(), drawRect);
BitmapUtilities.saveBitmap(personBitmap, Bitmap.CompressFormat.PNG, 100, personFile);
personCanvas = null;
} else {
PersonManager.reloadPersonIcon(resources, contentResolver, parentOverlayId);
}
}
if (personFile.exists()) {
resources.getValue(R.attr.icon_overlay_spacing_factor, resourceValue, true);
float spacingFactor = resourceValue.getFloat();
int iconSpacingLeft = Math.round(iconWidth * spacingFactor);
int iconSpacingTop = Math.round(iconHeight * spacingFactor);
Bitmap personBitmap = BitmapUtilities.loadAndCreateScaledBitmap(personFile.getAbsolutePath(),
personWidth, personHeight, BitmapUtilities.ScalingLogic.CROP, true);
if (personBitmap != null) {
Rect drawRect = new Rect(iconSpacingLeft, iconSpacingTop, iconSpacingLeft + personWidth,
iconSpacingTop + personHeight);
mediaCanvas.drawBitmap(personBitmap, null, drawRect, mediaPaint);
mediaPaint.setColor(resources.getColor(R.color.icon_person_border));
mediaPaint.setStyle(Paint.Style.STROKE);
mediaCanvas.drawRect(drawRect, mediaPaint);
}
}
}
return mediaBitmap;
}
public static Bitmap loadTemporaryIcon(Resources res, boolean addBorder) {
int iconWidth = res.getDimensionPixelSize(R.dimen.media_icon_width);
int iconHeight = res.getDimensionPixelSize(R.dimen.media_icon_height);
Bitmap tempBitmap = Bitmap.createBitmap(iconWidth, iconHeight,
ImageCacheUtilities.mBitmapFactoryOptions.inPreferredConfig);
if (addBorder) {
int borderWidth = res.getDimensionPixelSize(R.dimen.icon_border_width);
Canvas tempBitmapCanvas = new Canvas(tempBitmap);
Paint tempBitmapPaint = BitmapUtilities.getPaint(0, 1);
tempBitmapCanvas.drawColor(res.getColor(R.color.icon_background));
BitmapUtilities
.addBorder(tempBitmapCanvas, tempBitmapPaint, borderWidth, res.getColor(R.color.icon_border));
} else {
tempBitmap.eraseColor(res.getColor(R.color.icon_background));
}
return tempBitmap;
}
public ContentValues getContentValues() {
final ContentValues values = new ContentValues();
values.put(INTERNAL_ID, mInternalId);
values.put(PARENT_ID, mParentId);
values.put(DATE_CREATED, mCreationDate);
values.put(FILE_EXTENSION, mFileExtension);
values.put(MEDIA_EXTRA, mMediaExtra);
values.put(TYPE, mType);
values.put(VISIBILITY, mVisibility);
values.put(DELETED, mDeleted);
return values;
}
public static MediaItem fromExisting(MediaItem existing, String newInternalId, String newParentId,
long newCreationDate) {
final MediaItem media = new MediaItem();
media.mInternalId = newInternalId;
media.mParentId = newParentId;
media.mCreationDate = newCreationDate;
media.mFileExtension = existing.mFileExtension;
media.mMediaExtra = existing.mMediaExtra;
media.mType = existing.mType;
media.mVisibility = existing.mVisibility;
media.mDeleted = existing.mDeleted;
return media;
}
public static MediaItem fromCursor(Cursor c) {
final MediaItem media = new MediaItem();
media.mInternalId = c.getString(c.getColumnIndexOrThrow(INTERNAL_ID));
media.mParentId = c.getString(c.getColumnIndexOrThrow(PARENT_ID));
media.mCreationDate = c.getLong(c.getColumnIndexOrThrow(DATE_CREATED));
media.mFileExtension = c.getString(c.getColumnIndexOrThrow(FILE_EXTENSION));
media.mMediaExtra = c.getString(c.getColumnIndexOrThrow(MEDIA_EXTRA));
media.mType = c.getInt(c.getColumnIndexOrThrow(TYPE));
media.mVisibility = c.getInt(c.getColumnIndexOrThrow(VISIBILITY));
media.mDeleted = c.getInt(c.getColumnIndexOrThrow(DELETED));
return media;
}
@Override
public String toString() {
return "MediaItem[" + mInternalId + "," + mParentId + "," + mCreationDate + "," + mFileExtension + ","
+ mMediaExtra + "," + mType + "," + mVisibility + "," + mDeleted + "]";
}
}