/*
* 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.ByteArrayOutputStream;
import java.io.FileDescriptor;
import java.io.IOException;
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.Base64;
import mobisocial.musubi.util.CommonLayouts;
import mobisocial.socialkit.Obj;
import mobisocial.socialkit.musubi.DbObj;
import mobisocial.socialkit.obj.MemObj;
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.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Typeface;
import android.media.MediaMetadataRetriever;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.provider.MediaStore;
import android.util.Log;
import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
public class VideoObj extends DbEntryHandler implements FeedRenderer, Activator {
public static final String TAG = "VideoObj";
public static final String TYPE = "video";
public static final String DATA = "data";
@Override
public String getType() {
return TYPE;
}
public static Obj from(Context context, Uri videoUri, String mimeType) throws IOException {
// Query gallery for camera picture via
// Android ContentResolver interface
Bitmap curThumb = null;
ContentResolver cr = context.getContentResolver();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
options.inInputShareable = true;
options.inSampleSize = 1;
int targetSize = 200;
if (videoUri.getScheme().equals("content")) {
long videoId = Long.parseLong(videoUri.getLastPathSegment());
curThumb = MediaStore.Video.Thumbnails.getThumbnail(cr, videoId,
MediaStore.Video.Thumbnails.MINI_KIND, options);
int width = curThumb.getWidth();
int height = curThumb.getHeight();
int cropSize = Math.min(width, height);
float scaleSize = ((float) targetSize) / cropSize;
Matrix matrix = new Matrix();
matrix.postScale(scaleSize, scaleSize);
curThumb = Bitmap.createBitmap(
curThumb, 0, 0,width, height, matrix, true);
} else {
curThumb = createVideoThumbnail(videoUri.getPath(), targetSize);
}
if (curThumb == null) {
throw new IOException("Could not fetch thumbnail for " + videoUri);
}
JSONObject base = new JSONObject();
try {
if (mimeType == null && videoUri.getScheme().equals("content")) {
mimeType = cr.getType(videoUri);
}
base.put(CorralDownloadClient.OBJ_LOCAL_URI, videoUri.toString());
base.put(CorralDownloadClient.OBJ_MIME_TYPE, mimeType);
} catch (JSONException e) {
Log.e(TAG, "impossible json error possible!");
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
curThumb.compress(Bitmap.CompressFormat.JPEG, 90, baos);
byte[] data = baos.toByteArray();
return new MemObj(TYPE, base, data);
}
@Override
public View createView(Context context, ViewGroup frame) {
LinearLayout inner = new LinearLayout(context);
inner.setLayoutParams(CommonLayouts.FULL_WIDTH);
inner.setOrientation(LinearLayout.HORIZONTAL);
ImageView imageView = new ImageView(context);
imageView.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
ImageView iconView = new ImageView(context);
iconView.setImageResource(R.drawable.play);
iconView.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
inner.addView(imageView);
inner.addView(iconView);
return inner;
}
@Override
public void render(Context context, View view, DbObjCursor obj, boolean allowInteractions) {
LinearLayout inner = (LinearLayout)view;
ImageView imageView = (ImageView)inner.getChildAt(0);
FileDescriptor fd = obj.getFileDescriptorForRaw();
byte[] raw = null;
if (fd == null) {
raw = obj.getRaw();
}
PictureObj.bindImageToView(context, imageView, raw, fd);
}
public Pair<JSONObject, byte[]> handleUnprocessed(Context context,
JSONObject msg) {
byte[] bytes = Base64.decode(msg.optString(DATA));
msg.remove(DATA);
return new Pair<JSONObject, byte[]>(msg, bytes);
}
@Override
public void activate(final Context context, final DbObj obj) {
final CorralDownloadClient client = CorralDownloadClient.getInstance(context);
Log.d(TAG, "Corraling video");
if (client.fileAvailableLocally(obj)) {
Uri contentUri = client.getAvailableContentUri(obj);
startViewer(context, contentUri);
} else {
final CorralDownloadFuture future = CorralDownloadHandler.startOrFetchDownload(context, obj);
if (context instanceof MusubiBaseActivity) {
VideoDownloadDialogFragment f = new VideoDownloadDialogFragment(future);
((MusubiBaseActivity)context).showDialog(f, false);
} else {
new FetchAndPlayVideoTask(context, future).execute();
}
}
}
public class VideoDownloadDialogFragment extends BackgroundableDownloadDialogFragment {
public VideoDownloadDialogFragment(CorralDownloadFuture future) {
super(future);
}
@Override
public void onProgress(DownloadState state, DownloadChannel channel, int progress) {
super.onProgress(state, channel, progress);
if (state == DownloadState.TRANSFER_COMPLETE) {
if (progress == DownloadProgressCallback.SUCCESS) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
startViewer(getActivity(), getResult());
}
});
} else {
Toast.makeText(getActivity(), "Error fetching video", Toast.LENGTH_SHORT).show();
}
}
}
}
class FetchAndPlayVideoTask extends AsyncTask<Void, Void, Uri> {
final Context mContext;
final CorralDownloadFuture mFuture;
public FetchAndPlayVideoTask(Context context, CorralDownloadFuture future) {
mContext = context;
mFuture = future;
}
@Override
protected Uri doInBackground(Void... params) {
try {
return mFuture.getResult();
} catch (InterruptedException e) {
return null;
}
}
@Override
protected void onPostExecute(Uri result) {
if (result != null) {
startViewer(mContext, result);
}
}
}
private void startViewer(Context context, Uri contentUri) {
Log.d(TAG, "Launching viewer for " + contentUri);
Intent i = new Intent(Intent.ACTION_VIEW);
if (!(context instanceof Activity)) {
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
i.setDataAndType(contentUri, "video/*");
context.startActivity(i);
}
private static Bitmap createVideoThumbnail(String filePath, int targetWidth) {
Bitmap bitmap = null;
if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD_MR1) {
// Available before API 10 but unsupported and without thumbnail api.
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
retriever.setDataSource(filePath);
bitmap = retriever.getFrameAtTime(-1);
} catch (IllegalArgumentException ex) {
// Assume this is a corrupt video file
} catch (RuntimeException ex) {
// Assume this is a corrupt video file.
} finally {
try {
retriever.release();
} catch (RuntimeException ex) {
// Ignore failures while cleaning up.
}
}
} else if (filePath != null) {
bitmap = ThumbnailUtils.createVideoThumbnail(filePath,
MediaStore.Images.Thumbnails.MINI_KIND);
}
if (bitmap == null) return null;
// Scale down the bitmap if it is bigger than we need.
int width = bitmap.getWidth();
int height = bitmap.getHeight();
if (width > targetWidth) {
float scale = (float) targetWidth / width;
int w = Math.round(scale * width);
int h = Math.round(scale * height);
bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true);
}
return bitmap;
}
@Override
public void getSummaryText(Context context, TextView view, FeedSummary summary) {
view.setTypeface(null, Typeface.ITALIC);
view.setText(summary.getSender() + " posted a video");
}
}