package edu.mit.mobile.android.locast.data;
/*
* Copyright (C) 2011 MIT Mobile Experience Lab
*
* This program 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; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import java.io.IOException;
import java.net.URLConnection;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.ActivityNotFoundException;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;
import android.widget.Toast;
import edu.mit.mobile.android.content.ProviderUtils;
import edu.mit.mobile.android.locast.net.NetworkProtocolException;
import edu.mit.mobile.android.locast.ver2.R;
public class CastMedia extends JsonSyncableItem {
private static final String TAG = CastMedia.class.getSimpleName();
public final static String
_AUTHOR = "author",
_AUTHOR_URI = "author_uri",
_TITLE = "title",
_DESCRIPTION = "description",
_LANGUAGE = "language",
_MEDIA_URL = "url", // the body of the object
_LOCAL_URI = "local_uri", // any local copy of the main media
_MIME_TYPE = "mimetype", // type of the media
_DURATION = "duration",
_THUMBNAIL = "thumbnail",
_KEEP_OFFLINE = "offline",
_THUMB_LOCAL = "local_thumb" // filename of the local thumbnail
;
public final static String PATH = "media";
public final static String SERVER_PATH = "media/";
//public final static Uri CONTENT_URI = Uri.parse("content://"+MediaProvider.AUTHORITY+"/"+PATH);
public final static String[] PROJECTION = {
_ID,
_PUBLIC_URI,
_MODIFIED_DATE,
_CREATED_DATE,
_AUTHOR,
_AUTHOR_URI,
_TITLE,
_DESCRIPTION,
_LANGUAGE,
_MEDIA_URL,
_LOCAL_URI,
_MIME_TYPE,
_DURATION,
_THUMBNAIL,
_THUMB_LOCAL,
_KEEP_OFFLINE
};
public static final String
MIMETYPE_HTML = "text/html",
MIMETYPE_3GPP = "video/3gpp",
MIMETYPE_MPEG4 = "video/mpeg4";
@Override
public Uri getContentUri() {
return null;
}
@Override
public String[] getFullProjection() {
return PROJECTION;
}
@Override
public SyncMap getSyncMap() {
return SYNC_MAP;
}
public static Uri getCast(Uri castMediaUri){
return ProviderUtils.removeLastPathSegments(castMediaUri, 2);
}
public static Uri getMedia(Cursor c, int mediaCol, int mediaLocalCol){
Uri media;
if (! c.isNull(mediaLocalCol)){
media = Uri.parse(c.getString(mediaLocalCol));
}else if (! c.isNull(mediaCol)){
media = Uri.parse(c.getString(mediaCol));
}else{
media = null;
}
return media;
}
public static void showMedia(Context context, Cursor c, Uri castMediaUri){
final String mediaString = c.getString(c
.getColumnIndex(CastMedia._MEDIA_URL));
final String locMediaString = c.getString(c
.getColumnIndex(CastMedia._LOCAL_URI));
String mimeType = null;
Uri media;
if (locMediaString != null) {
media = Uri.parse(locMediaString);
if ("file".equals(media.getScheme())) {
mimeType = c.getString(c.getColumnIndex(CastMedia._MIME_TYPE));
}
} else if (mediaString != null) {
media = Uri.parse(mediaString);
mimeType = c.getString(c.getColumnIndex(CastMedia._MIME_TYPE));
// we strip this because we don't really want to force them to go to the browser.
if ("text/html".equals(mimeType)){
mimeType = null;
}
} else {
Log.e(TAG, "asked to show media for "+ castMediaUri + " but there was nothing to show");
return;
}
final Intent i = new Intent(Intent.ACTION_VIEW);
i.setDataAndType(media, mimeType);
if (mimeType != null && mimeType.startsWith("video/")){
context.startActivity(new Intent(Intent.ACTION_VIEW, ContentUris.withAppendedId(castMediaUri, c.getLong(c.getColumnIndex(CastMedia._ID)))));
}else{
// setting the MIME type for URLs doesn't work.
try {
context.startActivity(i);
}catch (final ActivityNotFoundException e){
// try it again, but without a mime type.
if (mimeType != null){
i.setDataAndType(media, null);
}
try {
context.startActivity(i);
}catch (final ActivityNotFoundException e2){
Toast.makeText(context, R.string.error_cast_media_no_activities, Toast.LENGTH_LONG).show();
}
}
}
}
public static Uri getThumbnail(Cursor c, int thumbCol, int thumbLocalCol){
Uri thumbnail;
if (! c.isNull(thumbLocalCol)){
thumbnail = Uri.parse(c.getString(thumbLocalCol));
}else if (! c.isNull(thumbCol)){
thumbnail = Uri.parse(c.getString(thumbCol));
}else{
thumbnail = null;
}
return thumbnail;
}
/**
* Guesses the mime type from the URL
*
* @param url
* @return the inferred mime type based on the file extension or null if it can't determine one
*/
public static String guessMimeTypeFromUrl(String url){
// this was improved in Gingerbread
// http://code.google.com/p/android/issues/detail?id=10100
// so we have some pre-defined types here so we can make sure to return SOMETHING.
String mimeType = URLConnection.guessContentTypeFromName(url);
if (mimeType != null){
return mimeType;
}
if (url.endsWith(".jpg") || url.endsWith(".jpeg")){
mimeType = "image/jpeg";
}else if (url.endsWith(".3gp")){
mimeType = "video/3gpp";
}else if (url.endsWith(".mp4") || url.endsWith(".mpeg4")){
mimeType = "video/mp4";
}else if (url.endsWith(".png")){
mimeType = "image/png";
}
return mimeType;
}
public final static ItemSyncMap SYNC_MAP = new ItemSyncMap();
public static class ItemSyncMap extends JsonSyncableItem.ItemSyncMap {
/**
*
*/
private static final long serialVersionUID = 8477549708016150941L;
public ItemSyncMap() {
super();
put(_TITLE, new SyncFieldMap("title", SyncFieldMap.STRING, SyncFieldMap.FLAG_OPTIONAL));
put(_DESCRIPTION, new SyncFieldMap("description", SyncFieldMap.STRING, SyncFieldMap.FLAG_OPTIONAL));
put(_LANGUAGE, new SyncFieldMap("language", SyncFieldMap.STRING));
put(_AUTHOR, new SyncChildField(_AUTHOR, "author", "display_name", SyncFieldMap.STRING, SyncFieldMap.FLAG_OPTIONAL));
put(_AUTHOR_URI, new SyncChildField(_AUTHOR_URI, "author", "uri", SyncFieldMap.STRING, SyncFieldMap.FLAG_OPTIONAL));
put("_resources", new SyncCustom("resources", SyncCustom.SYNC_FROM|SyncCustom.FLAG_OPTIONAL) {
@Override
public Object toJSON(Context context, Uri localItem, Cursor c, String lProp)
throws JSONException, NetworkProtocolException, IOException {
return null;
}
@Override
public ContentValues fromJSON(Context context, Uri localItem,
JSONObject item, String lProp) throws JSONException,
NetworkProtocolException, IOException {
final ContentValues cv = new ContentValues();
final JSONObject jo = item.getJSONObject(remoteKey);
if (jo.has("primary")){
final JSONObject primary = jo.getJSONObject("primary");
cv.put(_MIME_TYPE, primary.getString("mime_type"));
cv.put(_MEDIA_URL, primary.getString("url"));
}
if (jo.has("medium")){
final JSONObject screenshot = jo.getJSONObject("medium");
cv.put(_THUMBNAIL, screenshot.getString("url"));
}else if (jo.has("screenshot")){
final JSONObject screenshot = jo.getJSONObject("screenshot");
cv.put(_THUMBNAIL, screenshot.getString("url"));
}
return cv;
}
});
// no MIME type is passed with a link media type, so we need to add one in.
put("_content_type", new SyncCustom("content_type", SyncItem.SYNC_BOTH) {
@Override
public Object toJSON(Context context, Uri localItem, Cursor c, String lProp)
throws JSONException, NetworkProtocolException, IOException {
String mimeType = c.getString(c.getColumnIndex(_MIME_TYPE));
final String localUri = c.getString(c.getColumnIndex(_LOCAL_URI));
if (mimeType == null && localUri != null){
mimeType = guessMimeTypeFromUrl(localUri);
if (mimeType != null){
Log.d(TAG, "guessed MIME type from uri: " + localUri + ": " + mimeType);
}
}
if (mimeType == null){
return null;
}
if (mimeType.startsWith("video/")){
return "videomedia";
}else if (mimeType.startsWith("image/")){
return "imagemedia";
}else{
return null;
}
}
@Override
public ContentValues fromJSON(Context context, Uri localItem,
JSONObject item, String lProp) throws JSONException,
NetworkProtocolException, IOException {
final ContentValues cv = new ContentValues();
final String content_type = item.getString(remoteKey);
if ("linkedmedia".equals(content_type)){
cv.put(_MIME_TYPE, MIMETYPE_HTML);
}
return cv;
}
});
// the media URL can come from either the flattened "url" attribute or the expanded "resources" structure above.
put(_MEDIA_URL, new SyncFieldMap("url", SyncFieldMap.STRING, SyncFieldMap.SYNC_FROM|SyncItem.FLAG_OPTIONAL));
put(_MIME_TYPE, new SyncFieldMap("mime_type", SyncFieldMap.STRING, SyncItem.FLAG_OPTIONAL));
put(_DURATION, new SyncFieldMap("duration", SyncFieldMap.DURATION, SyncItem.FLAG_OPTIONAL));
put(_THUMBNAIL, new SyncFieldMap("preview_image", SyncFieldMap.STRING, SyncFieldMap.SYNC_FROM|SyncItem.FLAG_OPTIONAL));
}
@Override
public void onPostSyncItem(Context context, Uri uri, JSONObject item,
boolean updated) throws SyncException, IOException {
super.onPostSyncItem(context, uri, item, updated);
if (uri != null){
Log.d(TAG, "Starting media sync for " + uri);
context.startService(new Intent(MediaSync.ACTION_SYNC_RESOURCES, uri));
}
}
}
}