/* * Copyright 2012 The Stanford MobiSocial Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package mobisocial.musubi.objects; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.Date; import mobisocial.musubi.R; import mobisocial.musubi.feed.iface.Activator; import mobisocial.musubi.feed.iface.DbEntryHandler; import mobisocial.musubi.feed.iface.FeedRenderer; import mobisocial.musubi.ui.MusubiBaseActivity; import mobisocial.musubi.ui.fragments.FeedListFragment.FeedSummary; import mobisocial.musubi.ui.widget.DbObjCursorAdapter.DbObjCursor; import mobisocial.musubi.util.CommonLayouts; import mobisocial.socialkit.Obj; import mobisocial.socialkit.musubi.DbObj; import mobisocial.socialkit.obj.MemObj; import org.apache.commons.io.IOUtils; import org.json.JSONException; import org.json.JSONObject; import org.mobisocial.corral.BackgroundableDownloadDialogFragment; import org.mobisocial.corral.CorralDownloadClient; import org.mobisocial.corral.CorralDownloadHandler; import org.mobisocial.corral.CorralDownloadHandler.CorralDownloadFuture; import org.mobisocial.corral.CorralHelper.DownloadProgressCallback; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.graphics.Typeface; import android.net.Uri; import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.webkit.MimeTypeMap; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; //TODO: add thumbnails //Add all the fields you need, hook it up, etc public class FileObj extends DbEntryHandler implements FeedRenderer, Activator { public static final long EMBED_SIZE_LIMIT = 200 * 1024; public static final long CORRAL_SIZE_LIMIT = 30 * 1024 * 1024; public static final String TAG = "FileObj"; public static final String TYPE = "file"; //TODO: larger files need to be sent differently public static final String CORRAL_URI = "corral_uri"; //thumbnail could be embedded as the __html magic public static final String OBJ_FILENAME = "filename"; public static final String OBJ_FILESIZE = "filesize"; private static final SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd"); @Override public String getType() { return TYPE; } static MemObj from(String mime_type, String filename, long size, byte[] data) { if(data.length > EMBED_SIZE_LIMIT) throw new RuntimeException("file too large for push"); JSONObject json = new JSONObject(); try { json.put(OBJ_FILENAME, filename); json.put(OBJ_FILESIZE, size); json.put(CorralDownloadClient.OBJ_MIME_TYPE, mime_type); } catch(JSONException e) { throw new RuntimeException("json encode failed", e); } return new MemObj(TYPE, json, data); } public static Obj from(Context context, Uri dataUri) throws IOException { //TODO: is this the proper way to do it? if (dataUri == null) { throw new NullPointerException(); } ContentResolver cr = context.getContentResolver(); InputStream in = cr.openInputStream(dataUri); long length = in.available(); String ext; String mimeType; String filename; MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton(); if ("content".equals(dataUri.getScheme())) { ContentResolver resolver = context.getContentResolver(); mimeType = resolver.getType(dataUri); ext = mimeTypeMap.getExtensionFromMimeType(mimeType); filename = "Musubi-" + sDateFormat.format(new Date()); } else { ext = MimeTypeMap.getFileExtensionFromUrl(dataUri.toString()); mimeType = mimeTypeMap.getMimeTypeFromExtension(ext); filename = Uri.parse(dataUri.toString()).getLastPathSegment(); if (filename == null) { filename = "Musubi-" + sDateFormat.format(new Date()); } } if (mimeType == null || mimeType.isEmpty()) { throw new IOException("Unidentified mime type"); } if (ext == null || ext.isEmpty()) { ext = mimeTypeMap.getExtensionFromMimeType(mimeType); } if (!ext.isEmpty() && !filename.endsWith(ext)) { filename = filename + "." + ext; } if (mimeType.startsWith("video/")) { return VideoObj.from(context, dataUri, mimeType); } else if (mimeType.startsWith("image/")) { return PictureObj.from(context, dataUri, true); } if (length > EMBED_SIZE_LIMIT) { if (length > CORRAL_SIZE_LIMIT) { throw new IOException("file too large for push"); } else { return fromCorral(context, mimeType, filename, length, dataUri); } } else { in = cr.openInputStream(dataUri); return from(mimeType, filename, length, IOUtils.toByteArray(in)); } } public static MemObj fromCorral(Context context, String mime_type, String filename, long length, Uri dataUri){ JSONObject json = new JSONObject(); try { json.put(OBJ_FILENAME, filename); json.put(CorralDownloadClient.OBJ_MIME_TYPE, mime_type); json.put(CorralDownloadClient.OBJ_LOCAL_URI, dataUri); json.put(OBJ_FILESIZE, length); } catch(JSONException e) { throw new RuntimeException("json encode failed", e); } return new MemObj(TYPE, json, null); } @Override public View createView(Context context, ViewGroup frame) { LinearLayout container = new LinearLayout(context); container.setLayoutParams(CommonLayouts.FULL_WIDTH); container.setOrientation(LinearLayout.HORIZONTAL); container.setGravity(Gravity.CENTER); ImageView imageView = new ImageView(context); imageView.setLayoutParams(new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)); TextView valueTV = new TextView(context); valueTV.setLayoutParams(new LinearLayout.LayoutParams( LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); valueTV.setGravity(Gravity.BOTTOM | Gravity.LEFT); valueTV.setPadding(4, 0, 0, 0); container.addView(imageView); container.addView(valueTV); return container; } @Override public void render(Context context, View frame, DbObjCursor obj, boolean allowInteractions) { JSONObject json = obj.getJson(); String fname = ""; String mimetype = "application/octet-stream"; int iconid = R.drawable.icon_default; try { fname = json.getString(OBJ_FILENAME); mimetype = json.getString(CorralDownloadClient.OBJ_MIME_TYPE); iconid = getIconid(fname, mimetype); } catch (JSONException e) { e.printStackTrace(); } ImageView imageView = (ImageView)((ViewGroup)frame).getChildAt(0); TextView valueTV = (TextView)((ViewGroup)frame).getChildAt(1); imageView.setImageResource(iconid); String ftype = getDisplayFileType(fname, mimetype); valueTV.setText(ftype+" File: \""+fname+"\"."); } @Override synchronized public void activate(final Context context, final DbObj obj) { CorralDownloadClient client = CorralDownloadClient.getInstance(context); JSONObject json = obj.getJson(); final String mimeType; try { mimeType = json.getString(CorralDownloadClient.OBJ_MIME_TYPE); } catch (JSONException e) { Toast.makeText(context, "Could not view this file.", Toast.LENGTH_SHORT); Log.e(TAG, "Error viewing file", e); return; } if (client.fileAvailableLocally(obj)) { Uri uri = client.getAvailableContentUri(obj); viewfile(context, uri, mimeType); return; } if (obj.getRaw() != null) { // in-place file delivery. How convenient. File cacheFile = CorralDownloadClient.localFileForContent(obj, false); try { FileOutputStream output = new FileOutputStream(cacheFile); output.write(obj.getRaw()); output.close(); viewfile(context, Uri.fromFile(cacheFile), mimeType); } catch (IOException e) { Toast.makeText(context, "Error loading this file.", Toast.LENGTH_SHORT).show(); } return; } // attempt a download. CorralDownloadFuture future = CorralDownloadHandler.startOrFetchDownload(context, obj); FileDownloadDialogFragment f = new FileDownloadDialogFragment(future, mimeType); future.registerCallback(f); ((MusubiBaseActivity)context).showDialog(f, false); } public class FileDownloadDialogFragment extends BackgroundableDownloadDialogFragment { private final String mMimeType; public FileDownloadDialogFragment(CorralDownloadFuture future, String mimeType) { super(future); mMimeType = mimeType; } @Override public void onProgress(DownloadState state, DownloadChannel channel, int progress) { super.onProgress(state, channel, progress); if (state == DownloadState.TRANSFER_COMPLETE) { if (progress == DownloadProgressCallback.SUCCESS) { final Activity me = getActivity(); me.runOnUiThread(new Runnable() { @Override public void run() { viewfile(me, getResult(), mMimeType); } }); } else { Toast.makeText(getActivity(), "Error fetching video", Toast.LENGTH_SHORT).show(); } } } } public static void viewfile(Context context, Uri contentUri, String mimetype){ Intent intent = new Intent(Intent.ACTION_VIEW); if(mimetype==null || mimetype.isEmpty()){ return; } try{ intent.setDataAndType(contentUri, mimetype); context.startActivity(intent); }catch (ActivityNotFoundException anfe){ // TODO redirect Google Play to download viewer app for each filetype return; } } private static int getIconid(String filename, String mimetype){ int iconid = R.drawable.icon_default; String ext = getSuffix(filename); if(ext!=null){ if(ext.equalsIgnoreCase("ppt") || ext.equalsIgnoreCase("pptx")){ iconid = R.drawable.icon_powerpoint; }else if(ext.equalsIgnoreCase("xls") || ext.equalsIgnoreCase("xlsx") || ext.equalsIgnoreCase("csv")){ iconid = R.drawable.icon_excel; }else if(ext.equalsIgnoreCase("doc") || ext.equalsIgnoreCase("docx")){ iconid = R.drawable.icon_word; }else if(ext.equalsIgnoreCase("pdf")){ iconid = R.drawable.icon_pdf; }else if(ext.equalsIgnoreCase("mov")){ iconid = R.drawable.icon_mov; }else if(ext.equalsIgnoreCase("xml")){ iconid = R.drawable.icon_xml; }else if(ext.equalsIgnoreCase("html")){ iconid = R.drawable.icon_html; }else if(ext.equalsIgnoreCase("png")){ iconid = R.drawable.icon_png; }else if(ext.equalsIgnoreCase("jpg") || ext.equalsIgnoreCase("jpeg")){ iconid = R.drawable.icon_jpg; }else if(ext.equalsIgnoreCase("zip") || ext.equalsIgnoreCase("rar")){ iconid = R.drawable.icon_compress; }else if(mimetype.startsWith("image/")){ iconid = R.drawable.icon_image; }else if(mimetype.startsWith("video/")){ iconid = R.drawable.icon_movie; }else if(mimetype.startsWith("audio/")){ iconid = R.drawable.icon_music; }else if(mimetype.startsWith("text/")){ iconid = R.drawable.icon_text; } } return iconid; } private static String getDisplayFileType(String filename, String mimetype){ String ext = getSuffix(filename); if(ext!=null){ if(ext.equalsIgnoreCase("ppt") || ext.equalsIgnoreCase("pptx")){ return "Powerpoint"; }else if(ext.equalsIgnoreCase("xls") || ext.equalsIgnoreCase("xlsx") || ext.equalsIgnoreCase("csv")){ return "Excel"; }else if(ext.equalsIgnoreCase("doc") || ext.equalsIgnoreCase("docx")){ return "Word"; }else if(ext.equalsIgnoreCase("pdf")){ return "PDF"; }else if(ext.equalsIgnoreCase("mov")){ return "Video"; }else if(ext.equalsIgnoreCase("xml")){ return "XML"; }else if(ext.equalsIgnoreCase("html")){ return "HTML"; }else if(ext.equalsIgnoreCase("png")){ return "Image"; }else if(ext.equalsIgnoreCase("jpg") || ext.equalsIgnoreCase("jpeg")){ return "Image"; }else if(ext.equalsIgnoreCase("zip") || ext.equalsIgnoreCase("rar")){ return "Compressed"; }else if(mimetype.startsWith("image/")){ return "Image"; }else if(mimetype.startsWith("video/")){ return "Video"; }else if(mimetype.startsWith("audio/")){ return "Audio"; }else if(mimetype.startsWith("text/")){ return "Text"; } } return ""; } private static String getSuffix(String filename) { if (filename == null) return null; int point = filename.lastIndexOf("."); if (point != -1) { return filename.substring(point + 1); } return filename; } @Override public void getSummaryText(Context context, TextView view, FeedSummary summary) { JSONObject obj = summary.getJson(); view.setTypeface(null, Typeface.ITALIC); view.setText(summary.getSender() + " posted a new file: " + obj.optString(FileObj.OBJ_FILENAME)); } //TODO: how to handle the edit? }