Java tutorial
/* * Copyright (c) 2013, WeSawIt Inc. * Author: celwell * Some parts originally from Cordova Project and under Apache License */ package com.phonegap.plugins.wsiCameraLauncher; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import org.apache.commons.codec.binary.Base64; import org.apache.cordova.ExifHelper; import org.apache.cordova.FileUtils; import org.apache.cordova.api.CallbackContext; import org.apache.cordova.api.CordovaInterface; import org.apache.cordova.api.CordovaPlugin; import org.apache.cordova.api.LOG; import org.apache.cordova.api.PluginResult; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.CannedAccessControlList; import com.amazonaws.services.s3.model.PutObjectRequest; import com.amazonaws.services.s3.model.PutObjectResult; import com.phonegap.plugins.wsiCapture.WsiCapture; import android.app.Activity; import android.app.AlertDialog; import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.graphics.Bitmap.CompressFormat; import android.media.ExifInterface; import android.media.MediaScannerConnection; import android.media.ThumbnailUtils; import android.media.MediaScannerConnection.MediaScannerConnectionClient; import android.net.Uri; import android.os.AsyncTask; import android.os.Environment; import android.provider.MediaStore; import android.util.Log; import android.view.KeyEvent; /** * This class launches the camera view, allows the user to take a picture, * closes the camera view, and returns the captured image. When the camera view * is closed, the screen displayed before the camera view was shown is * redisplayed. */ public class WsiCameraLauncher extends CordovaPlugin implements MediaScannerConnectionClient { private static final int DATA_URL = 0; // Return base64 encoded string private static final int FILE_URI = 1; // Return file uri // (content://media/external/images/media/2 // for Android) private static final int PHOTOLIBRARY = 0; // Choose image from picture // library (same as // SAVEDPHOTOALBUM for Android) private static final int CAMERA = 1; // Take picture from camera private static final int SAVEDPHOTOALBUM = 2; // Choose image from picture // library (same as // PHOTOLIBRARY for Android) private static final int PICTURE = 0; // allow selection of still pictures // only. DEFAULT. Will return format // specified via DestinationType private static final int VIDEO = 1; // allow selection of video only, ONLY // RETURNS URL private static final int ALLMEDIA = 2; // allow selection from all media // types private static final int JPEG = 0; // Take a picture of type JPEG private static final int PNG = 1; // Take a picture of type PNG private static final String GET_PICTURE = "Get Picture"; private static final String GET_VIDEO = "Get Video"; private static final String GET_All = "Get All"; private static final String LOG_TAG = "WsiCameraLauncher"; private int mQuality; // Compression quality hint (0-100: 0=low quality & // high compression, 100=compress of max quality) private int targetWidth; // desired width of the image private int targetHeight; // desired height of the image private Uri imageUri; // Uri of captured image private int encodingType; // Type of encoding to use private int mediaType; // What type of media to retrieve private boolean saveToPhotoAlbum; // Should the picture be saved to the // device's photo album private boolean correctOrientation; // Should the pictures orientation be // corrected // private boolean allowEdit; // Should we allow the user to crop the image. // UNUSED. public CallbackContext callbackContext; private int numPics; private MediaScannerConnection conn; // Used to update gallery app with // newly-written files private Uri scanMe; // Uri of image to be added to content store // This should never be null! // private CordovaInterface cordova; /** * Constructor. */ public WsiCameraLauncher() { } // public void setContext(CordovaInterface mCtx) { // super.setContext(mCtx); // if (CordovaInterface.class.isInstance(mCtx)) // cordova = (CordovaInterface) mCtx; // else // LOG.d(LOG_TAG, // "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity"); // } /** * Executes the request and returns PluginResult. * * @param action * The action to execute. * @param args * JSONArry of arguments for the plugin. * @param callbackContext * The callback id used when calling back into JavaScript. * @return A PluginResult object with a status and message. */ public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { this.callbackContext = callbackContext; Log.d(LOG_TAG, "wsiCameraLauncher execute called"); if (action.equals("takePicture")) { int srcType = CAMERA; int destType = FILE_URI; this.saveToPhotoAlbum = false; this.targetHeight = 0; this.targetWidth = 0; this.encodingType = JPEG; this.mediaType = PICTURE; this.mQuality = 80; this.mQuality = args.getInt(0); destType = args.getInt(1); srcType = args.getInt(2); this.targetWidth = args.getInt(3); this.targetHeight = args.getInt(4); this.encodingType = args.getInt(5); this.mediaType = args.getInt(6); // this.allowEdit = args.getBoolean(7); // This field is unused. this.correctOrientation = args.getBoolean(8); this.saveToPhotoAlbum = args.getBoolean(9); // If the user specifies a 0 or smaller width/height // make it -1 so later comparisons succeed if (this.targetWidth < 1) { this.targetWidth = -1; } if (this.targetHeight < 1) { this.targetHeight = -1; } if (srcType == CAMERA) { this.takePicture(destType, encodingType); } else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) { this.getImage(srcType, destType); } PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT); r.setKeepCallback(true); callbackContext.sendPluginResult(r); return true; } return false; } // -------------------------------------------------------------------------- // LOCAL METHODS // -------------------------------------------------------------------------- /** * Take a picture with the camera. When an image is captured or the camera * view is cancelled, the result is returned in * CordovaActivity.onActivityResult, which forwards the result to * this.onActivityResult. * * The image can either be returned as a base64 string or a URI that points * to the file. To display base64 string in an img tag, set the source to: * img.src="data:image/jpeg;base64,"+result; or to display URI in an img tag * img.src=result; * * @param quality * Compression quality hint (0-100: 0=low quality & high * compression, 100=compress of max quality) * @param returnType * Set the type of image to return. */ public void takePicture(int returnType, int encodingType) { // Save the number of images currently on disk for later this.numPics = queryImgDB(whichContentStore()).getCount(); // Display camera Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); // Specify file so that large image is captured and returned File photo = createCaptureFile(encodingType); intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo)); this.imageUri = Uri.fromFile(photo); if (this.cordova != null) { this.cordova.startActivityForResult((CordovaPlugin) this, intent, (CAMERA + 1) * 16 + returnType + 1); } // else // LOG.d(LOG_TAG, // "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity"); } private String getTempDirectoryPath(Context ctx) { File cache = null; // SD Card Mounted if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { cache = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + ctx.getPackageName() + "/cache/"); } // Use internal storage else { cache = ctx.getCacheDir(); } // Create the cache directory if it doesn't exist if (!cache.exists()) { cache.mkdirs(); } return cache.getAbsolutePath(); } /** * Create a file in the applications temporary directory based upon the * supplied encoding. * * @param encodingType * of the image to be taken * @return a File object pointing to the temporary picture */ private File createCaptureFile(int encodingType) { File photo = null; if (encodingType == JPEG) { photo = new File(this.getTempDirectoryPath(this.cordova.getActivity()), ".Pic.jpg"); } else if (encodingType == PNG) { photo = new File(this.getTempDirectoryPath(this.cordova.getActivity()), ".Pic.png"); } else { throw new IllegalArgumentException("Invalid Encoding Type: " + encodingType); } return photo; } /** * Get image from photo library. * * @param quality * Compression quality hint (0-100: 0=low quality & high * compression, 100=compress of max quality) * @param srcType * The album to get image from. * @param returnType * Set the type of image to return. */ // TODO: Images selected from SDCARD don't display correctly, but from // CAMERA ALBUM do! public void getImage(int srcType, int returnType) { final int srcTypeFinal = srcType; final int returnTypeFinal = returnType; String[] choices = { "Upload a Photo", "Upload a Video" }; AlertDialog.Builder builder = new AlertDialog.Builder(this.cordova.getActivity()); builder.setItems(choices, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { Log.d(LOG_TAG, "Index #" + which + " chosen."); Intent intent = new Intent(); if (which == 0) { // set up photo intent WsiCameraLauncher.this.mediaType = PICTURE; intent.setType("image/*"); } else if (which == 1) { // set up video intent WsiCameraLauncher.this.mediaType = VIDEO; intent.setType("video/*"); } else { WsiCameraLauncher.this.failPicture("Selection cancelled."); return; } intent.setAction(Intent.ACTION_GET_CONTENT); intent.addCategory(Intent.CATEGORY_OPENABLE); if (WsiCameraLauncher.this.cordova != null) { WsiCameraLauncher.this.cordova.startActivityForResult((CordovaPlugin) WsiCameraLauncher.this, Intent.createChooser(intent, new String("Pick")), (srcTypeFinal + 1) * 16 + returnTypeFinal + 1); } } }); builder.setOnKeyListener(new DialogInterface.OnKeyListener() { @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP && !event.isCanceled()) { dialog.cancel(); WsiCameraLauncher.this.failPicture("Selection cancelled."); return true; } return false; } }); builder.show(); } private String getRealPathFromURI(Uri contentUri, CordovaInterface cordova) { final String scheme = contentUri.getScheme(); if (scheme.compareTo("content") == 0) { String[] proj = { "_data" }; Cursor cursor = cordova.getActivity().managedQuery(contentUri, proj, null, null, null); int column_index = cursor.getColumnIndexOrThrow("_data"); cursor.moveToFirst(); return cursor.getString(column_index); } else if (scheme.compareTo("file") == 0) { return contentUri.getPath(); } else { return contentUri.toString(); } } private class UploadFilesToS3Task extends AsyncTask<Object, Void, PutObjectResult> { private Exception exception; private CallbackContext callbackContext; private String mid; private JSONObject mediaFile; protected PutObjectResult doInBackground(Object... params) { try { Log.d(LOG_TAG, "Inside doInBackground."); File fileToUpload = (File) params[0]; File fileToUploadMedium = (File) params[1]; File fileToUploadThumb = (File) params[2]; this.callbackContext = (CallbackContext) params[3]; this.mid = (String) params[4]; this.mediaFile = (JSONObject) params[5]; AmazonS3Client s3Client = new AmazonS3Client( new BasicAWSCredentials("---AWS KEY REMOVED---", "---AWS SECRET KEY REMOVED---")); Log.d(LOG_TAG, "mid = " + mid); PutObjectRequest por = new PutObjectRequest("---AWS BUCKET NAME REMOVED---", "econ_" + mid + ".jpg", fileToUpload); por.setCannedAcl(CannedAccessControlList.PublicRead); Log.d(LOG_TAG, "about to PUT"); PutObjectResult result = s3Client.putObject(por); Log.d(LOG_TAG, "After PUT"); PutObjectRequest porMedium = new PutObjectRequest("---AWS BUCKET NAME REMOVED---", "medium_" + mid + ".jpg", fileToUploadMedium); porMedium.setCannedAcl(CannedAccessControlList.PublicRead); Log.d(LOG_TAG, "about to PUT porMedium"); PutObjectResult resultMedium = s3Client.putObject(porMedium); Log.d(LOG_TAG, "After PUT porMedium"); PutObjectRequest porThumb = new PutObjectRequest("---AWS BUCKET NAME REMOVED---", "thumb_" + mid + ".jpg", fileToUploadThumb); porThumb.setCannedAcl(CannedAccessControlList.PublicRead); Log.d(LOG_TAG, "about to PUT porThumb"); PutObjectResult resultThumb = s3Client.putObject(porThumb); Log.d(LOG_TAG, "After PUT porThumb"); return result; } catch (Exception e) { this.exception = e; Log.d(LOG_TAG, "exception in doInBackground catch: " + this.exception.toString()); return null; } } protected void onPostExecute(PutObjectResult result) { Log.d(LOG_TAG, "Inside onPostExecute."); if (result != null) { // was successful upload Log.d(LOG_TAG, "result of put: " + result.toString()); try { mediaFile.put("status", "loaded"); mediaFile.put("statusMedium", "loaded"); mediaFile.put("statusThumb", "loaded"); mediaFile.put("typeOfPluginResult", "success"); PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, (new JSONArray()).put(mediaFile)); pluginResult.setKeepCallback(false); this.callbackContext.sendPluginResult(pluginResult); } catch (JSONException e) { Log.d(LOG_TAG, "error: " + e.getStackTrace().toString()); } } else { if (this.exception != null) { Log.d(LOG_TAG, "exception in asynctask if any: " + this.exception.toString()); } } } } private class UploadVideoToS3Task extends AsyncTask<Object, Void, PutObjectResult> { private Exception exception; private CallbackContext callbackContext; private String mid; private JSONObject mediaFile; protected PutObjectResult doInBackground(Object... params) { try { File fileToUpload = (File) params[0]; this.callbackContext = (CallbackContext) params[1]; this.mid = (String) params[2]; this.mediaFile = (JSONObject) params[3]; AmazonS3Client s3Client = new AmazonS3Client( new BasicAWSCredentials("---AWS KEY REMOVED---", "---AWS SECRET KEY REMOVED---")); PutObjectRequest por = new PutObjectRequest("---AWS BUCKET NAME REMOVED---", "" + mid + "." + mediaFile.getString("fileExt"), fileToUpload); por.setCannedAcl(CannedAccessControlList.PublicRead); Log.d(LOG_TAG, "about to PUT video"); PutObjectResult result = s3Client.putObject(por); Log.d(LOG_TAG, "After PUT video"); return result; } catch (Exception e) { this.exception = e; Log.d(LOG_TAG, "exception in doInBackground catch: " + this.exception.toString()); return null; } } protected void onPostExecute(PutObjectResult result) { Log.d(LOG_TAG, "Inside onPostExecute."); if (result != null) { // was successful upload Log.d(LOG_TAG, "result of put: " + result.toString()); try { mediaFile.put("status", "loaded"); mediaFile.put("typeOfPluginResult", "success"); PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, (new JSONArray()).put(mediaFile)); pluginResult.setKeepCallback(false); this.callbackContext.sendPluginResult(pluginResult); } catch (JSONException e) { Log.d(LOG_TAG, "error: " + e.getStackTrace().toString()); } } else { if (this.exception != null) { Log.d(LOG_TAG, "exception in asynctask if any: " + this.exception.toString()); } } } } private String generateRandomMid() { return "" + (100000000 + (int) (Math.random() * ((999999999 - 100000000) + 1))); } private Bitmap fitInsideSquare(Bitmap b, int sideLength) { int grabWidth = b.getWidth(); int grabHeight = b.getHeight(); float scaleX = ((float) sideLength) / grabWidth; float scaleY = ((float) sideLength) / grabHeight; float scale = Math.min(scaleX, scaleY); Matrix m = new Matrix(); m.postScale(scale, scale); return Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, true); } private Bitmap makeInsideSquare(Bitmap b, int sideLength) { int grabWidth = b.getWidth(); int grabHeight = b.getHeight(); if (grabWidth > grabHeight) { grabWidth = grabHeight; } else { grabHeight = grabWidth; } float scale = ((float) sideLength) / grabWidth; Matrix m = new Matrix(); m.postScale(scale, scale); return Bitmap.createBitmap(b, 0, 0, grabWidth, grabHeight, m, true); } /** * Called when the camera view exits. * * @param requestCode * The request code originally supplied to * startActivityForResult(), allowing you to identify who this * result came from. * @param resultCode * The integer result code returned by the child activity through * its setResult(). * @param intent * An Intent, which can return result data to the caller (various * data can be attached to Intent "extras"). */ public void onActivityResult(int requestCode, int resultCode, Intent intent) { // Get src and dest types from request code int srcType = (requestCode / 16) - 1; int destType = (requestCode % 16) - 1; int rotate = 0; Log.d(LOG_TAG, "-z"); // If retrieving photo from library if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) { Log.d(LOG_TAG, "-y"); if (resultCode == Activity.RESULT_OK) { Log.d(LOG_TAG, "-x"); Uri uri = intent.getData(); Log.d(LOG_TAG, "-w"); // If you ask for video or all media type you will automatically // get back a file URI // and there will be no attempt to resize any returned data if (this.mediaType != PICTURE) { Log.d(LOG_TAG, "mediaType not PICTURE, so must be Video"); String metadataDateTime = ""; ExifInterface exif; try { exif = new ExifInterface(this.getRealPathFromURI(uri, this.cordova)); if (exif.getAttribute(ExifInterface.TAG_DATETIME) != null) { Log.d(LOG_TAG, "z4a"); metadataDateTime = exif.getAttribute(ExifInterface.TAG_DATETIME).toString(); metadataDateTime = metadataDateTime.replaceFirst(":", "-"); metadataDateTime = metadataDateTime.replaceFirst(":", "-"); } } catch (IOException e2) { // TODO Auto-generated catch block e2.printStackTrace(); } Log.d(LOG_TAG, "before create thumbnail"); Bitmap bitmap = ThumbnailUtils.createVideoThumbnail( (new File(this.getRealPathFromURI(uri, this.cordova))).getAbsolutePath(), MediaStore.Images.Thumbnails.MINI_KIND); Log.d(LOG_TAG, "after create thumbnail"); String mid = generateRandomMid(); try { String filePathMedium = this.getTempDirectoryPath(this.cordova.getActivity()) + "/medium_" + mid + ".jpg"; FileOutputStream foMedium = new FileOutputStream(filePathMedium); bitmap.compress(CompressFormat.JPEG, 100, foMedium); foMedium.flush(); foMedium.close(); bitmap.recycle(); System.gc(); JSONObject mediaFile = new JSONObject(); try { mediaFile.put("mid", mid); mediaFile.put("mediaType", "video"); mediaFile.put("filePath", filePathMedium); mediaFile.put("filePathMedium", filePathMedium); mediaFile.put("filePathThumb", filePathMedium); mediaFile.put("typeOfPluginResult", "initialRecordInformer"); String absolutePath = (new File(this.getRealPathFromURI(uri, this.cordova))) .getAbsolutePath(); mediaFile.put("fileExt", absolutePath.substring(absolutePath.lastIndexOf(".") + 1)); if (metadataDateTime != "") { mediaFile.put("metadataDateTime", metadataDateTime); } } catch (JSONException e) { Log.d(LOG_TAG, "error: " + e.getStackTrace().toString()); } Log.d(LOG_TAG, "mediafile at 638" + mediaFile.toString()); PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, (new JSONArray()).put(mediaFile)); pluginResult.setKeepCallback(true); this.callbackContext.sendPluginResult(pluginResult); new UploadVideoToS3Task().execute(new File(this.getRealPathFromURI(uri, this.cordova)), this.callbackContext, mid, mediaFile); } catch (FileNotFoundException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } } else { String imagePath = this.getRealPathFromURI(uri, this.cordova); String mimeType = FileUtils.getMimeType(imagePath); // If we don't have a valid image so quit. if (imagePath == null || mimeType == null || !(mimeType.equalsIgnoreCase("image/jpeg") || mimeType.equalsIgnoreCase("image/png"))) { Log.d(LOG_TAG, "I either have a null image path or bitmap"); this.failPicture("Unable to retrieve path to picture!"); return; } String mid = generateRandomMid(); Log.d(LOG_TAG, "a"); JSONObject mediaFile = new JSONObject(); Log.d(LOG_TAG, "b"); try { FileInputStream fi = new FileInputStream(imagePath); Bitmap bitmap = BitmapFactory.decodeStream(fi); fi.close(); Log.d(LOG_TAG, "z1"); // try to get exif data ExifInterface exif = new ExifInterface(imagePath); Log.d(LOG_TAG, "z2"); JSONObject metadataJson = new JSONObject(); Log.d(LOG_TAG, "z3"); /* JSONObject latlng = new JSONObject(); String lat = "0"; String lng = "0"; if (exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE) != null) { lat = exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE); } if (exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE) != null) { lng = exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE); } latlng.put("lat", lat); latlng.put("lng", lng); Log.d(LOG_TAG, "z4"); metadataJson.put("locationData", latlng); */ String metadataDateTime = ""; if (exif.getAttribute(ExifInterface.TAG_DATETIME) != null) { Log.d(LOG_TAG, "z4a"); JSONObject exifWrapper = new JSONObject(); exifWrapper.put("DateTimeOriginal", exif.getAttribute(ExifInterface.TAG_DATETIME).toString()); exifWrapper.put("DateTimeDigitized", exif.getAttribute(ExifInterface.TAG_DATETIME).toString()); metadataDateTime = exif.getAttribute(ExifInterface.TAG_DATETIME).toString(); metadataDateTime = metadataDateTime.replaceFirst(":", "-"); metadataDateTime = metadataDateTime.replaceFirst(":", "-"); Log.d(LOG_TAG, "z5"); metadataJson.put("Exif", exifWrapper); } Log.d(LOG_TAG, "z6"); Log.d(LOG_TAG, "metadataJson: " + metadataJson.toString()); Log.d(LOG_TAG, "metadataDateTime: " + metadataDateTime.toString()); if (exif.getAttribute(ExifInterface.TAG_ORIENTATION) != null) { int o = Integer.parseInt(exif.getAttribute(ExifInterface.TAG_ORIENTATION)); Log.d(LOG_TAG, "z7"); if (o == ExifInterface.ORIENTATION_NORMAL) { rotate = 0; } else if (o == ExifInterface.ORIENTATION_ROTATE_90) { rotate = 90; } else if (o == ExifInterface.ORIENTATION_ROTATE_180) { rotate = 180; } else if (o == ExifInterface.ORIENTATION_ROTATE_270) { rotate = 270; } else { rotate = 0; } Log.d(LOG_TAG, "z8"); Log.d(LOG_TAG, "rotate: " + rotate); // try to correct orientation if (rotate != 0) { Matrix matrix = new Matrix(); Log.d(LOG_TAG, "z9"); matrix.setRotate(rotate); Log.d(LOG_TAG, "z10"); bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); Log.d(LOG_TAG, "z11"); } } Log.d(LOG_TAG, "c"); String filePath = this.getTempDirectoryPath(this.cordova.getActivity()) + "/econ_" + mid + ".jpg"; FileOutputStream foEcon = new FileOutputStream(filePath); fitInsideSquare(bitmap, 850).compress(CompressFormat.JPEG, 45, foEcon); foEcon.flush(); foEcon.close(); Log.d(LOG_TAG, "d"); String filePathMedium = this.getTempDirectoryPath(this.cordova.getActivity()) + "/medium_" + mid + ".jpg"; FileOutputStream foMedium = new FileOutputStream(filePathMedium); makeInsideSquare(bitmap, 320).compress(CompressFormat.JPEG, 55, foMedium); foMedium.flush(); foMedium.close(); Log.d(LOG_TAG, "e"); String filePathThumb = this.getTempDirectoryPath(this.cordova.getActivity()) + "/thumb_" + mid + ".jpg"; FileOutputStream foThumb = new FileOutputStream(filePathThumb); makeInsideSquare(bitmap, 175).compress(CompressFormat.JPEG, 55, foThumb); foThumb.flush(); foThumb.close(); bitmap.recycle(); System.gc(); Log.d(LOG_TAG, "f"); mediaFile.put("mid", mid); mediaFile.put("mediaType", "photo"); mediaFile.put("filePath", filePath); mediaFile.put("filePathMedium", filePath); mediaFile.put("filePathThumb", filePath); mediaFile.put("typeOfPluginResult", "initialRecordInformer"); //mediaFile.put("metadataJson", metadataJson); if (metadataDateTime != "") { mediaFile.put("metadataDateTime", metadataDateTime); } PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, (new JSONArray()).put(mediaFile)); pluginResult.setKeepCallback(true); this.callbackContext.sendPluginResult(pluginResult); Log.d(LOG_TAG, "g"); Log.d(LOG_TAG, "mediaFile " + mediaFile.toString()); new UploadFilesToS3Task().execute(new File(filePath), new File(filePathMedium), new File(filePathThumb), this.callbackContext, mid, mediaFile); Log.d(LOG_TAG, "h"); } catch (FileNotFoundException e) { Log.d(LOG_TAG, "error: " + e.getStackTrace().toString()); } catch (IOException e1) { // TODO Auto-generated catch block Log.d(LOG_TAG, "error: " + e1.getStackTrace().toString()); } catch (JSONException e2) { // TODO Auto-generated catch block Log.d(LOG_TAG, "error: " + e2.getStackTrace().toString()); } /* if (this.correctOrientation) { String[] cols = { MediaStore.Images.Media.ORIENTATION }; Cursor cursor = this.cordova .getActivity() .getContentResolver() .query(intent.getData(), cols, null, null, null); if (cursor != null) { cursor.moveToPosition(0); rotate = cursor.getInt(0); cursor.close(); } if (rotate != 0) { Matrix matrix = new Matrix(); matrix.setRotate(rotate); bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); } } // Create an ExifHelper to save the exif // data that is lost during compression String resizePath = this .getTempDirectoryPath(this.cordova .getActivity()) + "/resize.jpg"; ExifHelper exif = new ExifHelper(); try { if (this.encodingType == JPEG) { exif.createInFile(resizePath); exif.readExifData(); rotate = exif.getOrientation(); } } catch (IOException e) { e.printStackTrace(); } OutputStream os = new FileOutputStream( resizePath); bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os); os.close(); // Restore exif data to file if (this.encodingType == JPEG) { exif.createOutFile(this .getRealPathFromURI(uri, this.cordova)); exif.writeExifData(); } if (bitmap != null) { bitmap.recycle(); bitmap = null; } System.gc(); // The resized image is cached by the app in // order to get around this and not have to // delete your // application cache I'm adding the current // system time to the end of the file url. this.callbackContext.success("file://" + resizePath + "?" + System.currentTimeMillis()); */ } } else if (resultCode == Activity.RESULT_CANCELED) { this.failPicture("Selection cancelled."); } else { this.failPicture("Selection did not complete!"); } } } /** * Figure out if the bitmap should be rotated. For instance if the picture * was taken in portrait mode * * @param rotate * @param bitmap * @return rotated bitmap */ private Bitmap getRotatedBitmap(int rotate, Bitmap bitmap, ExifHelper exif) { Matrix matrix = new Matrix(); if (rotate == 180) { matrix.setRotate(rotate); } else { matrix.setRotate(rotate, (float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2); } bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); exif.resetOrientation(); return bitmap; } /** * In the special case where the default width, height and quality are * unchanged we just write the file out to disk saving the expensive * Bitmap.compress function. * * @param uri * @throws FileNotFoundException * @throws IOException */ private void writeUncompressedImage(Uri uri) throws FileNotFoundException, IOException { FileInputStream fis = new FileInputStream(FileUtils.stripFileProtocol(imageUri.toString())); OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri); byte[] buffer = new byte[4096]; int len; while ((len = fis.read(buffer)) != -1) { os.write(buffer, 0, len); } os.flush(); os.close(); fis.close(); } /** * Create entry in media store for image * * @return uri */ private Uri getUriFromMediaStore() { ContentValues values = new ContentValues(); values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); Uri uri; try { uri = this.cordova.getActivity().getContentResolver() .insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); } catch (UnsupportedOperationException e) { LOG.d(LOG_TAG, "Can't write to external media storage."); try { uri = this.cordova.getActivity().getContentResolver() .insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values); } catch (UnsupportedOperationException ex) { LOG.d(LOG_TAG, "Can't write to internal media storage."); return null; } } return uri; } /** * Return a scaled bitmap based on the target width and height * * @param imagePath * @return */ private Bitmap getScaledBitmap(String imagePath) { // If no new width or height were specified return the original bitmap if (this.targetWidth <= 0 && this.targetHeight <= 0) { return BitmapFactory.decodeFile(imagePath); } // figure out the original width and height of the image BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(imagePath, options); // determine the correct aspect ratio int[] widthHeight = calculateAspectRatio(options.outWidth, options.outHeight); // Load in the smallest bitmap possible that is closest to the size we // want options.inJustDecodeBounds = false; options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, this.targetWidth, this.targetHeight); Bitmap unscaledBitmap = BitmapFactory.decodeFile(imagePath, options); if (unscaledBitmap == null) { return null; } return Bitmap.createScaledBitmap(unscaledBitmap, widthHeight[0], widthHeight[1], true); } /** * Maintain the aspect ratio so the resulting image does not look smooshed * * @param origWidth * @param origHeight * @return */ public int[] calculateAspectRatio(int origWidth, int origHeight) { int newWidth = this.targetWidth; int newHeight = this.targetHeight; // If no new width or height were specified return the original bitmap if (newWidth <= 0 && newHeight <= 0) { newWidth = origWidth; newHeight = origHeight; } // Only the width was specified else if (newWidth > 0 && newHeight <= 0) { newHeight = (newWidth * origHeight) / origWidth; } // only the height was specified else if (newWidth <= 0 && newHeight > 0) { newWidth = (newHeight * origWidth) / origHeight; } // If the user specified both a positive width and height // (potentially different aspect ratio) then the width or height is // scaled so that the image fits while maintaining aspect ratio. // Alternatively, the specified width and height could have been // kept and Bitmap.SCALE_TO_FIT specified when scaling, but this // would result in whitespace in the new image. else { double newRatio = newWidth / (double) newHeight; double origRatio = origWidth / (double) origHeight; if (origRatio > newRatio) { newHeight = (newWidth * origHeight) / origWidth; } else if (origRatio < newRatio) { newWidth = (newHeight * origWidth) / origHeight; } } int[] retval = new int[2]; retval[0] = newWidth; retval[1] = newHeight; return retval; } /** * Figure out what ratio we can load our image into memory at while still * being bigger than our desired width and height * * @param srcWidth * @param srcHeight * @param dstWidth * @param dstHeight * @return */ public static int calculateSampleSize(int srcWidth, int srcHeight, int dstWidth, int dstHeight) { final float srcAspect = (float) srcWidth / (float) srcHeight; final float dstAspect = (float) dstWidth / (float) dstHeight; if (srcAspect > dstAspect) { return srcWidth / dstWidth; } else { return srcHeight / dstHeight; } } /** * Creates a cursor that can be used to determine how many images we have. * * @return a cursor */ private Cursor queryImgDB(Uri contentStore) { return this.cordova.getActivity().getContentResolver().query(contentStore, new String[] { MediaStore.Images.Media._ID }, null, null, null); } /** * Cleans up after picture taking. Checking for duplicates and that kind of * stuff. * * @param newImage */ private void cleanup(int imageType, Uri oldImage, Uri newImage, Bitmap bitmap) { if (bitmap != null) { bitmap.recycle(); } // Clean up initial camera-written image file. (new File(FileUtils.stripFileProtocol(oldImage.toString()))).delete(); checkForDuplicateImage(imageType); // Scan for the gallery to update pic refs in gallery if (this.saveToPhotoAlbum && newImage != null) { this.scanForGallery(newImage); } System.gc(); } /** * Used to find out if we are in a situation where the Camera Intent adds to * images to the content store. If we are using a FILE_URI and the number of * images in the DB increases by 2 we have a duplicate, when using a * DATA_URL the number is 1. * * @param type * FILE_URI or DATA_URL */ private void checkForDuplicateImage(int type) { int diff = 1; Uri contentStore = whichContentStore(); Cursor cursor = queryImgDB(contentStore); int currentNumOfImages = cursor.getCount(); if (type == FILE_URI && this.saveToPhotoAlbum) { diff = 2; } // delete the duplicate file if the difference is 2 for file URI or 1 // for Data URL if ((currentNumOfImages - numPics) == diff) { cursor.moveToLast(); int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID))); if (diff == 2) { id--; } Uri uri = Uri.parse(contentStore + "/" + id); this.cordova.getActivity().getContentResolver().delete(uri, null, null); } } /** * Determine if we are storing the images in internal or external storage * * @return Uri */ private Uri whichContentStore() { if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { return android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else { return android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI; } } /** * Compress bitmap using jpeg, convert to Base64 encoded string, and return * to JavaScript. * * @param bitmap */ public void processPicture(Bitmap bitmap) { ByteArrayOutputStream jpeg_data = new ByteArrayOutputStream(); try { if (bitmap.compress(CompressFormat.JPEG, mQuality, jpeg_data)) { byte[] code = jpeg_data.toByteArray(); byte[] output = Base64.encodeBase64(code); String js_out = new String(output); this.callbackContext.success(js_out); js_out = null; output = null; code = null; } } catch (Exception e) { this.failPicture("Error compressing image."); } jpeg_data = null; } /** * Send error message to JavaScript. * * @param err */ public void failPicture(String err) { this.callbackContext.error(err); } private void scanForGallery(Uri newImage) { this.scanMe = newImage; if (this.conn != null) { this.conn.disconnect(); } this.conn = new MediaScannerConnection(this.cordova.getActivity().getApplicationContext(), this); conn.connect(); } public void onMediaScannerConnected() { try { this.conn.scanFile(this.scanMe.toString(), "image/*"); } catch (java.lang.IllegalStateException e) { LOG.e(LOG_TAG, "Can't scan file in MediaScanner after taking picture"); } } public void onScanCompleted(String path, Uri uri) { this.conn.disconnect(); } }