package kr.kdev.dg1s.biowiki.util; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.database.Cursor; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Environment; import android.provider.MediaStore; import android.support.v4.app.Fragment; import android.support.v4.content.CursorLoader; import android.text.TextUtils; import android.webkit.MimeTypeMap; import org.wordpress.passcodelock.AppLockManager; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import kr.kdev.dg1s.biowiki.BioWiki; import kr.kdev.dg1s.biowiki.R; import kr.kdev.dg1s.biowiki.models.MediaFile; public class MediaUtils { public static boolean isValidImage(String url) { if (url == null) return false; if (url.endsWith(".png") || url.endsWith(".jpg") || url.endsWith(".jpeg") || url.endsWith(".gif")) return true; return false; } private static boolean isDocument(String url) { if (url == null) return false; if (url.endsWith(".doc") || url.endsWith(".docx") || url.endsWith(".odt") || url.endsWith(".pdf")) return true; return false; } private static boolean isPowerpoint(String url) { if (url == null) return false; if (url.endsWith(".ppt") || url.endsWith(".pptx") || url.endsWith(".pps") || url.endsWith(".ppsx") || url.endsWith(".key")) return true; return false; } private static boolean isSpreadsheet(String url) { if (url == null) return false; if (url.endsWith(".xls") || url.endsWith(".xlsx")) return true; return false; } private static boolean isVideo(String url) { if (url == null) return false; if (url.endsWith(".ogv") || url.endsWith(".mp4") || url.endsWith(".m4v") || url.endsWith(".mov") || url.endsWith(".wmv") || url.endsWith(".avi") || url.endsWith(".mpg") || url.endsWith(".3gp") || url.endsWith(".3g2")) return true; return false; } public static int getPlaceholder(String url) { if (isValidImage(url)) return R.drawable.media_image_placeholder; else if (isDocument(url)) return R.drawable.media_document; else if (isPowerpoint(url)) return R.drawable.media_powerpoint; else if (isSpreadsheet(url)) return R.drawable.media_spreadsheet; else if (isVideo(url)) return R.drawable.media_movieclip; return 0; } /** * E.g. Jul 2, 2013 @ 21:57 * */ public static String getDate(long ms) { Date date = new Date(ms); SimpleDateFormat sdf = new SimpleDateFormat("MMM d, yyyy '@' HH:mm", Locale.ENGLISH); // The timezone on the website is at GMT sdf.setTimeZone(TimeZone.getTimeZone("GMT")); return sdf.format(date); } public static void launchPictureLibrary(Fragment fragment) { Intent intent = new Intent(Intent.ACTION_PICK); intent.setType("image/*"); intent.setAction(Intent.ACTION_GET_CONTENT); AppLockManager.getInstance().setExtendedTimeout(); fragment.startActivityForResult(Intent.createChooser(intent, fragment.getString(R.string.pick_photo)), RequestCode.ACTIVITY_REQUEST_CODE_PICTURE_LIBRARY); } public static void launchCamera(Fragment fragment, LaunchCameraCallback callback) { String state = android.os.Environment.getExternalStorageState(); if (!state.equals(android.os.Environment.MEDIA_MOUNTED)) { showSDCardRequiredDialog(fragment.getActivity()); } else { Intent intent = prepareLaunchCameraIntent(callback); fragment.startActivityForResult(intent, RequestCode.ACTIVITY_REQUEST_CODE_TAKE_PHOTO); AppLockManager.getInstance().setExtendedTimeout(); } } private static Intent prepareLaunchCameraIntent(LaunchCameraCallback callback) { File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM); String mediaCapturePath = path + File.separator + "Camera" + File.separator + "wp-" + System.currentTimeMillis() + ".jpg"; Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(mediaCapturePath))); if (callback != null) { callback.onMediaCapturePathReady(mediaCapturePath); } // make sure the directory we plan to store the recording in exists File directory = new File(mediaCapturePath).getParentFile(); if (!directory.exists() && !directory.mkdirs()) { try { throw new IOException("Path to file could not be created."); } catch (IOException e) { AppLog.e(AppLog.T.POSTS, e); } } return intent; } private static void showSDCardRequiredDialog(Activity activity) { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(activity); dialogBuilder.setTitle(activity.getResources().getText(R.string.sdcard_title)); dialogBuilder.setMessage(activity.getResources().getText(R.string.sdcard_message)); dialogBuilder.setPositiveButton(activity.getString(R.string.ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { dialog.dismiss(); } }); dialogBuilder.setCancelable(true); dialogBuilder.create().show(); } public static void launchVideoLibrary(Fragment fragment) { Intent intent = new Intent(Intent.ACTION_PICK); intent.setType("video/*"); intent.setAction(Intent.ACTION_GET_CONTENT); AppLockManager.getInstance().setExtendedTimeout(); fragment.startActivityForResult(Intent.createChooser(intent, fragment.getString(R.string.pick_video)), RequestCode.ACTIVITY_REQUEST_CODE_PICTURE_LIBRARY); } public static void launchVideoCamera(Fragment fragment) { Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); fragment.startActivityForResult(intent, RequestCode.ACTIVITY_REQUEST_CODE_TAKE_VIDEO); AppLockManager.getInstance().setExtendedTimeout(); } public static boolean isLocalFile(String state) { if (state == null) return false; if (state.equals("queued") || state.equals("uploading") || state.equals("retry") || state.equals("failed")) return true; return false; } public static Uri getLastRecordedVideoUri(Activity activity) { String[] proj = {MediaStore.Video.Media._ID}; Uri contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; String sortOrder = MediaStore.Video.VideoColumns.DATE_TAKEN + " DESC"; CursorLoader loader = new CursorLoader(activity, contentUri, proj, null, null, sortOrder); Cursor cursor = loader.loadInBackground(); cursor.moveToFirst(); return Uri.parse(contentUri.toString() + "/" + cursor.getLong(0)); } /** * This is a workaround for WP3.4.2 that deletes the media from the server when editing media properties within the app. * See: https://github.com/wordpress-mobile/WordPress-Android/issues/204 * * @return */ public static boolean isWordPressVersionWithMediaEditingCapabilities() { if (BioWiki.currentBlog == null) return false; if (BioWiki.currentBlog.getWpVersion() == null) return true; if (BioWiki.currentBlog.isDotcomFlag()) return true; Version minVersion; Version currentVersion; try { minVersion = new Version("3.5.2"); currentVersion = new Version(BioWiki.currentBlog.getWpVersion()); if (currentVersion.compareTo(minVersion) == -1) return false; } catch (IllegalArgumentException e) { AppLog.e(AppLog.T.POSTS, e); } return true; } public static boolean canDeleteMedia(String blogId, String mediaID) { Cursor cursor = BioWiki.wpDB.getMediaFile(blogId, mediaID); if (!cursor.moveToFirst()) { cursor.close(); return false; } String state = cursor.getString(cursor.getColumnIndex("uploadState")); cursor.close(); if (state != null && state.equals("uploading")) { return false; } return true; } public static BWImageSpan prepareWPImageSpan(Context context, String blogId, final String mediaId) { Cursor cursor = BioWiki.wpDB.getMediaFile(blogId, mediaId); if (cursor == null || !cursor.moveToFirst()) { if (cursor != null) cursor.close(); return null; } String url = cursor.getString(cursor.getColumnIndex("fileURL")); if (url == null) { cursor.close(); return null; } String mimeType = cursor.getString(cursor.getColumnIndex("mimeType")); boolean isVideo = mimeType != null && mimeType.contains("video"); Uri uri = Uri.parse(url); BWImageSpan imageSpan = new BWImageSpan(context, isVideo ? R.drawable.media_movieclip : R.drawable.remote_image, uri); MediaFile mediaFile = imageSpan.getMediaFile(); mediaFile.setMediaId(mediaId); mediaFile.setBlogId(blogId); mediaFile.setCaption(cursor.getString(cursor.getColumnIndex("caption"))); mediaFile.setDescription(cursor.getString(cursor.getColumnIndex("description"))); mediaFile.setTitle(cursor.getString(cursor.getColumnIndex("title"))); mediaFile.setWidth(cursor.getInt(cursor.getColumnIndex("width"))); mediaFile.setHeight(cursor.getInt(cursor.getColumnIndex("height"))); mediaFile.setMimeType(mimeType); mediaFile.setFileName(cursor.getString(cursor.getColumnIndex("fileName"))); mediaFile.setThumbnailURL(cursor.getString(cursor.getColumnIndex("thumbnailURL"))); mediaFile.setDateCreatedGMT(cursor.getLong(cursor.getColumnIndex("date_created_gmt"))); mediaFile.setVideoPressShortCode(cursor.getString(cursor.getColumnIndex("videoPressShortcode"))); mediaFile.setFileURL(cursor.getString(cursor.getColumnIndex("fileURL"))); mediaFile.setVideo(isVideo); mediaFile.save(); cursor.close(); return imageSpan; } // Calculate the minimun width between the blog setting and picture real width public static int getMinimumImageWidth(Context context, Uri curStream) { String imageWidth = BioWiki.getCurrentBlog().getMaxImageWidth(); int imageWidthBlogSetting = Integer.MAX_VALUE; if (!imageWidth.equals("Original Size")) { try { imageWidthBlogSetting = Integer.valueOf(imageWidth); } catch (NumberFormatException e) { AppLog.e(AppLog.T.POSTS, e); } } int[] dimensions = ImageHelper.getImageSize(curStream, context); int imageWidthPictureSetting = dimensions[0] == 0 ? Integer.MAX_VALUE : dimensions[0]; if (Math.min(imageWidthPictureSetting, imageWidthBlogSetting) == Integer.MAX_VALUE) { //Default value in case of errors reading the picture size and the blog settings is set to Original size return 1024; } else { return Math.min(imageWidthPictureSetting, imageWidthBlogSetting); } } public static void setWPImageSpanWidth(Context context, Uri curStream, BWImageSpan is) { MediaFile mediaFile = is.getMediaFile(); if (mediaFile != null) mediaFile.setWidth(getMinimumImageWidth(context, curStream)); } public static boolean isInMediaStore(Uri mediaUri) { // Check if the image is externally hosted (Picasa/Google Photos for example) if (mediaUri != null && mediaUri.toString().startsWith("content://media/")) { return true; } else { return false; } } public static Uri downloadExternalMedia(Context context, Uri imageUri) { if (context == null || imageUri == null) return null; File cacheDir; String mimeType = context.getContentResolver().getType(imageUri); boolean isVideo = (mimeType != null && mimeType.contains("video")); // If the device has an SD card if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) { String mediaFolder = isVideo ? "video" : "images"; cacheDir = new File(android.os.Environment.getExternalStorageDirectory() + "/BioWiki/" + mediaFolder); } else { // If no SD card cacheDir = context.getApplicationContext().getCacheDir(); } if (!cacheDir.exists()) cacheDir.mkdirs(); try { InputStream input; // Download the file if (imageUri.toString().startsWith("content://")) { input = context.getContentResolver().openInputStream(imageUri); } else { input = new URL(imageUri.toString()).openStream(); } String fileName = "wp-" + System.currentTimeMillis(); if (isVideo) fileName += "." + MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType); File f = new File(cacheDir, fileName); OutputStream output = new FileOutputStream(f); byte data[] = new byte[1024]; int count; while ((count = input.read(data)) != -1) { output.write(data, 0, count); } output.flush(); output.close(); input.close(); return Uri.fromFile(f); } catch (FileNotFoundException e) { AppLog.e(AppLog.T.UTILS, e); } catch (MalformedURLException e) { AppLog.e(AppLog.T.UTILS, e); } catch (IOException e) { AppLog.e(AppLog.T.UTILS, e); } return null; } public static String getMimeTypeOfInputStream(InputStream stream) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(stream, null, options); return options.outMimeType; } public static String getMediaFileMimeType(File mediaFile) { String originalFileName = mediaFile.getName().toLowerCase(); String mimeType = UrlUtils.getUrlMimeType(originalFileName); if (TextUtils.isEmpty(mimeType)) { try { String filePathForGuessingMime = mediaFile.getPath().contains("://") ? mediaFile.getPath() : "file://" + mediaFile.getPath(); URL urlForGuessingMime = new URL(filePathForGuessingMime); URLConnection uc = urlForGuessingMime.openConnection(); String guessedContentType = uc.getContentType(); //internally calls guessContentTypeFromName(url.getFile()); and guessContentTypeFromStream(is); // check if returned "content/unknown" if (!TextUtils.isEmpty(guessedContentType) && !guessedContentType.equals("content/unknown")) { mimeType = guessedContentType; } } catch (MalformedURLException e) { AppLog.e(AppLog.T.API, "MalformedURLException while trying to guess the content type for the file here " + mediaFile.getPath() + " with URLConnection", e); } catch (IOException e) { AppLog.e(AppLog.T.API, "Error while trying to guess the content type for the file here " + mediaFile.getPath() + " with URLConnection", e); } } // No mimeType yet? Try to decode the image and get the mimeType from there if (TextUtils.isEmpty(mimeType)) { try { DataInputStream inputStream = new DataInputStream(new FileInputStream(mediaFile)); String mimeTypeFromStream = getMimeTypeOfInputStream(inputStream); if (!TextUtils.isEmpty(mimeTypeFromStream)) { mimeType = mimeTypeFromStream; } inputStream.close(); } catch (FileNotFoundException e) { AppLog.e(AppLog.T.API, "FileNotFoundException while trying to guess the content type for the file " + mediaFile.getPath(), e); } catch (IOException e) { AppLog.e(AppLog.T.API, "IOException while trying to guess the content type for the file " + mediaFile.getPath(), e); } } if (TextUtils.isEmpty(mimeType)) { mimeType = ""; } else { if (mimeType.equalsIgnoreCase("video/mp4v-es")) { //Fixes #533. See: http://tools.ietf.org/html/rfc3016 mimeType = "video/mp4"; } } return mimeType; } public static String getMediaFileName(File mediaFile, String mimeType) { String originalFileName = mediaFile.getName().toLowerCase(); String extension = MimeTypeMap.getFileExtensionFromUrl(originalFileName); if (!TextUtils.isEmpty(extension)) //File name already has the extension in it return originalFileName; if (!TextUtils.isEmpty(mimeType)) { //try to get the extension from mimeType String fileExtension = getExtensionForMimeType(mimeType); if (!TextUtils.isEmpty(fileExtension)) { originalFileName += "." + fileExtension; } } else { //No mimetype and no extension!! AppLog.e(AppLog.T.API, "No mimetype and no extension for " + mediaFile.getPath()); } return originalFileName; } public static String getExtensionForMimeType(String mimeType) { if (TextUtils.isEmpty(mimeType)) return ""; MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton(); String fileExtensionFromMimeType = mimeTypeMap.getExtensionFromMimeType(mimeType); if (TextUtils.isEmpty(fileExtensionFromMimeType)) { // We're still without an extension - split the mime type and retrieve it String[] split = mimeType.split("/"); fileExtensionFromMimeType = split.length > 1 ? split[1] : split[0]; } return fileExtensionFromMimeType.toLowerCase(); } public interface LaunchCameraCallback { public void onMediaCapturePathReady(String mediaCapturePath); } public class RequestCode { public static final int ACTIVITY_REQUEST_CODE_PICTURE_LIBRARY = 1000; public static final int ACTIVITY_REQUEST_CODE_TAKE_PHOTO = 1100; public static final int ACTIVITY_REQUEST_CODE_VIDEO_LIBRARY = 1200; public static final int ACTIVITY_REQUEST_CODE_TAKE_VIDEO = 1300; } }