com.polyvi.xface.extension.camera.XCameraExt.java Source code

Java tutorial

Introduction

Here is the source code for com.polyvi.xface.extension.camera.XCameraExt.java

Source

/*
 This file was modified from or inspired by Apache Cordova.
    
 Licensed to the Apache Software Foundation (ASF) under one
 or more contributor license agreements. See the NOTICE file
 distributed with this work for additional information
 regarding copyright ownership. The ASF licenses this file
 to you 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 com.polyvi.xface.extension.camera;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Calendar;

import org.json.JSONArray;
import org.json.JSONException;

import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;

import com.polyvi.xface.core.XConfiguration;
import com.polyvi.xface.extension.XActivityResultListener;
import com.polyvi.xface.extension.XCallbackContext;
import com.polyvi.xface.extension.XExtension;
import com.polyvi.xface.extension.XExtensionResult;
import com.polyvi.xface.util.XBase64;
import com.polyvi.xface.util.XConstant;
import com.polyvi.xface.util.XLog;
import com.polyvi.xface.util.XNotification;
import com.polyvi.xface.util.XPathResolver;
import com.polyvi.xface.util.XStringUtils;
import com.polyvi.xface.util.XUtils;

public class XCameraExt extends XExtension implements XActivityResultListener {
    private static final String COMMAND_TAKE_PICTURE = "takePicture";

    private static final long DURATION = 4000;//alert??

    private static final int DATA_URL = 0; // ?
    private static final int FILE_URI = 1; // 
    private static final int NATIVE_URI = 2; // androidFILE_URI

    private static final int PHOTOLIBRARY = 0; //  picture library
                                               // (android?SAVEDPHOTOALBUM)
    private static final int CAMERA = 1; // camera
    private static final int SAVEDPHOTOALBUM = 2; //  picture library
                                                  // (android?PHOTOLIBRARY)

    private static final int PICTURE = 0;
    private static final int VIDEO = 1;
    private static final int ALLMEDIA = 2;

    private static final int JPEG = 0; // JPEG
    private static final int PNG = 1; // 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 CLASS_NAME = "Camera";

    private static final String RESIZED_PIC_NAME = "Resize.jpg";
    private static final String MEDIA_MIME_TYPE = "image/jpeg";

    private static final int CAMERA_REQUEST_CODE = XUtils.genActivityRequestCode();
    private static final int PHOTO_REQUEST_CODE = XUtils.genActivityRequestCode();
    private static final int PICTURE_CUT_REQUEST_CODE = XUtils.genActivityRequestCode();

    private int mQuality; // ?? (0-100)
    private int mTargetWidth; // ?
    private int mTargetHeight; // ?
    private Uri mImageUri; // ?
    private int mEncodingType; // ?JPEG, PNG)
    private int mMediaType; // (PICTURE, VIDEO, ALLMEDIA)
    private int mSrcType; // ??(PHOTOLIBRARY, CAMERA, SAVEDPHOTOALBUM)
    private int mDestType; // ??(DATA_URL, FILE_URINATIVE_URI)
    private boolean mSaveToPhotoAlbum; // ???
    private boolean mAllowEdit;//????

    private XCallbackContext mCallbackCtx;
    private int mNumPics;

    //????Intent?
    private final static String CROP_IMAGE_ACTION = "android.intent.action.XFACE_IMAGE_CROP";

    private Uri mCroppedImageUri; // ??Uri????

    @Override
    public void sendAsyncResult(String result) {

    }

    @Override
    public boolean isAsync(String action) {
        return false;
    }

    @Override
    public XExtensionResult exec(String action, JSONArray args, XCallbackContext callbackCtx) throws JSONException {

        XExtensionResult.Status status = XExtensionResult.Status.OK;
        String result = "";
        mCallbackCtx = callbackCtx;

        try {
            if (COMMAND_TAKE_PICTURE.equals(action)) {
                mSrcType = CAMERA;
                mDestType = FILE_URI;
                mTargetHeight = 0;
                mTargetWidth = 0;
                mEncodingType = JPEG;
                mMediaType = PICTURE;
                mQuality = 80;
                mSaveToPhotoAlbum = false;
                mAllowEdit = false;

                mQuality = args.getInt(0);
                mDestType = args.getInt(1);
                mSrcType = args.getInt(2);
                mTargetWidth = args.getInt(3);
                mTargetHeight = args.getInt(4);
                mEncodingType = args.getInt(5);
                mMediaType = args.getInt(6);
                mAllowEdit = args.getBoolean(7);
                mSaveToPhotoAlbum = args.getBoolean(9);

                if (mSrcType == CAMERA) { // ?
                    takePicture(mEncodingType); // ??
                } else if ((mSrcType == PHOTOLIBRARY) || (mSrcType == SAVEDPHOTOALBUM)) {//?
                    getImage();
                }
                XExtensionResult r = new XExtensionResult(XExtensionResult.Status.NO_RESULT);
                r.setKeepCallback(true);
                return r;
            }
            return new XExtensionResult(status, result);
        } catch (IllegalArgumentException e) {
            XLog.e(CLASS_NAME, "Take picture arguments error!");
            return new XExtensionResult(XExtensionResult.Status.ERROR);
        }
    }

    public void takePicture(int mEncodingType) {
        Cursor cursor = queryImgDB();
        if (null != cursor) {
            mNumPics = cursor.getCount();// ??
        } else {
            mNumPics = 0;//Cursor
        }

        // ?camera?
        Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
        File photo = createCaptureFile(mEncodingType); // 

        // ?URI???
        intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
        mImageUri = Uri.fromFile(photo);

        // ?Camera??onActivityResult?
        mExtensionContext.getSystemContext().startActivityForResult(this, intent, CAMERA_REQUEST_CODE);
    }

    public void getImage() {
        Intent intent = new Intent();
        String title = GET_PICTURE;
        if (mMediaType == PICTURE) {
            intent.setType("image/*");
        } else if (mMediaType == VIDEO) {
            intent.setType("video/*");
            title = GET_VIDEO;
        } else if (mMediaType == ALLMEDIA) {
            intent.setType("*/*");
            title = GET_All;
        }

        intent.setAction(Intent.ACTION_GET_CONTENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);

        // ??
        mExtensionContext.getSystemContext().startActivityForResult(this,
                Intent.createChooser(intent, new String(title)), PHOTO_REQUEST_CODE);
    }

    private Cursor queryImgDB() {
        Uri contentUri = null;
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            contentUri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        } else {
            contentUri = android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI;
        }

        return mExtensionContext.getSystemContext().getContext().getContentResolver().query(contentUri,
                new String[] { MediaStore.Images.Media._ID }, null, null, null);
    }

    private File createCaptureFile(int encodingType) {
        Context context = getContext();
        String picName = "";
        File photo = null;
        long time = Calendar.getInstance().getTimeInMillis();
        if (mEncodingType == JPEG) {
            picName = String.valueOf(time) + ".jpg";
        } else if (mEncodingType == PNG) {
            picName = String.valueOf(time) + ".png";
        } else {
            throw new IllegalArgumentException("Invalid Encoding Type: " + mEncodingType);
        }

        photo = context.getFileStreamPath(picName);
        if (!photo.exists()) {
            try {
                FileOutputStream outStream = context.openFileOutput(picName,
                        Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
                outStream.close();
                photo = context.getFileStreamPath(picName);
            } catch (FileNotFoundException e) {
                XLog.e(CLASS_NAME, "Create picture failed!");
            } catch (IOException e) {
                XLog.e(CLASS_NAME, "Close picture fileStream failed!");
            }
        }
        return photo;
    }

    /**
     * ?
     *
     * @return uri
     */
    private Uri getUriFromMediaStore() {
        Uri uri = null;
        ContentValues values = new ContentValues();
        values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, MEDIA_MIME_TYPE);
        try {
            uri = getContext().getContentResolver()
                    .insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        } catch (UnsupportedOperationException e) {
            XLog.d(CLASS_NAME, "Can't write to external media storage.");
            try {
                uri = getContext().getContentResolver()
                        .insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
            } catch (UnsupportedOperationException ex) {
                XLog.d(CLASS_NAME, "Can't write to internal media storage.");
                mCallbackCtx.error("Error capturing image - no media storage found.");
                return null;
            }
        }
        return uri;
    }

    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        if (CAMERA_REQUEST_CODE == requestCode) {
            if (resultCode != Activity.RESULT_OK) {
                XLog.i(CLASS_NAME, "Camera cancelled.");
                mCallbackCtx.error("Camera cancelled.");
                return;
            }
            if (!mAllowEdit) {
                cameraSucess(intent);
            } else {
                startCutPhotoAfterCamera(intent);
            }
        } else if (PHOTO_REQUEST_CODE == requestCode) {
            if (resultCode != Activity.RESULT_OK || null == intent) {
                XLog.e(CLASS_NAME, "Selection cancelled.");
                mCallbackCtx.error("Selection cancelled.");
                return;
            }
            if (!mAllowEdit) {
                photoSucess(intent);
            } else {
                mImageUri = intent.getData();
                startCutPhoto(mImageUri);
            }
        } else {
            if (resultCode != Activity.RESULT_OK || null == intent) {
                XLog.e(CLASS_NAME, "cut picture cancelled.");
                mCallbackCtx.error("cut picture cancelled.");
                return;
            }
            if (mSrcType == CAMERA) {
                cameraSucess(intent);
            } else if ((mSrcType == PHOTOLIBRARY) || (mSrcType == SAVEDPHOTOALBUM)) {
                photoSucess(intent);
            }
        }
    }

    /**
     * ???? ???????
     *
     * @param intent
     */
    private void startCutPhotoAfterCamera(Intent intent) {
        // mDstTypeFILE_URIintentnull?mImageUri
        if (null != intent && null != intent.getData()) {
            startCutPhoto(intent.getData());
        } else {
            startCutPhoto(mImageUri);
        }
    }

    /**
     * ?
     *
     * ???????
     *
     * @param uri
     */
    private boolean startCutPhoto(Uri uri) {
        if (!(mTargetHeight > 0 && mTargetWidth > 0)) {
            XLog.e(CLASS_NAME, "Width and height must be larger than 1 when you want to crop image.");
            mCallbackCtx.error("you must give target width and height for crop photo.");
            return false;
        }

        // ????????
        Uri cropped_image_uri = createEmptyFileForCroppedImageBeforeCrop();
        Intent intent = new Intent();
        intent.putExtra(ImageCroppingActivity.SOURCE_IMAGE_URI, uri);
        intent.putExtra(ImageCroppingActivity.CROPPED_IMAGE_URI, cropped_image_uri);
        intent.setClass(getContext(), ImageCroppingActivity.class);
        mExtensionContext.getSystemContext().startActivityForResult(this, intent, PICTURE_CUT_REQUEST_CODE);
        return true;

    }

    /**
     * ????????
     * ???URI???URI??
     *
     * @return ??URI
     */
    private Uri createEmptyFileForCroppedImageBeforeCrop() {
        String cropped_image_name = null;
        if (mEncodingType == JPEG) {
            String tempStr = System.currentTimeMillis() + ".jpg";
            cropped_image_name = tempStr + "_cropped.jpg";
        } else if (mEncodingType == PNG) {
            String tempStr = System.currentTimeMillis() + ".png";
            cropped_image_name = tempStr + "_cropped.png";
        } else {
            throw new IllegalArgumentException("Invalid Encoding Type: " + mEncodingType);
        }

        File cropped_image = new File(mWebContext.getWorkSpace(), cropped_image_name);
        mCroppedImageUri = null;
        if (cropped_image.exists()) {
            cropped_image.delete();
        }
        try {
            cropped_image.createNewFile();
            mCroppedImageUri = Uri.fromFile(cropped_image);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return mCroppedImageUri;
    }

    /**
     * ?
     *
     * @param bitmap   ??.
     * @return Bitmap  ?.
     */
    public Bitmap scaleBitmap(Bitmap bitmap) {
        int newWidth = mTargetWidth;
        int newHeight = mTargetHeight;
        int origWidth = bitmap.getWidth();
        int origHeight = bitmap.getHeight();

        // ?
        if (newWidth <= 0 && newHeight <= 0) {
            return bitmap;
        } else if (newWidth > 0 && newHeight <= 0) {
            newHeight = (newWidth * origHeight) / origWidth;
        } else if (newWidth <= 0 && newHeight > 0) {
            newWidth = (newHeight * origWidth) / origHeight;
        } 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;
            }
        }
        return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
    }

    /**
     * ???
     *
     * @param type   FILE_URINATIVE_URI DATA_URL
     */
    private void checkForDuplicateImage(int type) {
        int diff = 1;
        Cursor cursor = queryImgDB();
        int currentNumOfImages = 0;
        if (null != cursor) {
            currentNumOfImages = cursor.getCount();
        }

        if (type == FILE_URI || type == NATIVE_URI) {
            diff = 2;
        }

        // ??
        if ((currentNumOfImages - mNumPics) == diff) {
            cursor.moveToLast();
            int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID))) - 1;
            Uri uri = Uri.parse(MediaStore.Images.Media.EXTERNAL_CONTENT_URI + "/" + id);
            getContext().getContentResolver().delete(uri, null, null);
        }
    }

    private void cameraSucess(Intent intent) {
        try {
            Bitmap bitmap = null;
            try {
                //???imagebitmap
                if (mAllowEdit) {
                    //??????Android???
                    //???URI
                    Bundle extras = intent.getExtras();
                    if (extras != null) {
                        bitmap = extras.getParcelable("data");
                    }

                    //?????URI
                    if ((bitmap == null) || (extras == null)) {
                        bitmap = getCroppedBitmap(intent);
                    }
                } else { // ????
                    bitmap = android.provider.MediaStore.Images.Media.getBitmap(getContext().getContentResolver(),
                            mImageUri);
                }
            } catch (FileNotFoundException e) {
                Uri uri = intent.getData();
                android.content.ContentResolver resolver = getContext().getContentResolver();
                bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri));
            } catch (IOException e) {
                mCallbackCtx.error("Can't open image");
            } catch (OutOfMemoryError e) {
                XNotification notification = new XNotification(mExtensionContext.getSystemContext());
                notification.alert("Size of Image is too large!", "Save Image Error", "OK", null, DURATION);
                mCallbackCtx.error("Size of image is too large");
                return;
            }

            // ???
            Bitmap scaleBitmap = scaleBitmap(bitmap);
            Uri uri = null;
            if (mDestType == DATA_URL) {
                processPicture(scaleBitmap);
                checkForDuplicateImage(DATA_URL);
            } else if (mDestType == FILE_URI || mDestType == NATIVE_URI) {
                if (!this.mSaveToPhotoAlbum) {
                    String suffixName = null;
                    if (mEncodingType == JPEG) {
                        suffixName = ".jpg";
                    } else if (mEncodingType == PNG) {
                        suffixName = ".png";
                    } else {
                        throw new IllegalArgumentException("Invalid Encoding Type: " + mEncodingType);
                    }
                    String photoName = System.currentTimeMillis() + suffixName;
                    uri = Uri.fromFile(new File(mWebContext.getWorkSpace(), photoName));
                } else {
                    uri = getUriFromMediaStore();
                }
                if (uri == null) {
                    mCallbackCtx.error("Error capturing image - no media storage found.");
                }

                // ?
                OutputStream os = getContext().getContentResolver().openOutputStream(uri);
                scaleBitmap.compress(Bitmap.CompressFormat.JPEG, mQuality, os);
                os.close();

                // ??success callback
                XPathResolver pathResolver = new XPathResolver(uri.toString(), "", getContext());
                mCallbackCtx.success(XConstant.FILE_SCHEME + pathResolver.resolve());
            }
            scaleBitmap.recycle();
            scaleBitmap = null;
            cleanup(FILE_URI, mImageUri, uri, bitmap);
        } catch (IOException e) {
            mCallbackCtx.error("Error capturing image.");
        }
    }

    /**
     * ?URI?Bitmap
     *
     * @param intent  ???URIintent
     * @return Bitmap ?intentURI?
     */
    private Bitmap getCroppedBitmap(Intent intent) {
        Bitmap bitmap = null;
        InputStream is = null;
        Uri uri = null; //??URI?

        if (intent == null) {
            uri = mCroppedImageUri;
        } else {
            uri = intent.getParcelableExtra(ImageCroppingActivity.CROPPED_IMAGE_URI);
        }

        try {
            is = getInputStream(uri);
            bitmap = BitmapFactory.decodeStream(is);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException ignored) {
                }
            }
        }
        return bitmap;
    }

    private InputStream getInputStream(Uri mUri) throws IOException {
        try {
            if (mUri.getScheme().equals("file")) {
                return new java.io.FileInputStream(mUri.getPath());
            } else {
                return getContext().getContentResolver().openInputStream(mUri);
            }
        } catch (FileNotFoundException ex) {
            return null;
        }
    }

    /**
     * ??.
     */
    private void cleanup(int imageType, Uri oldImage, Uri newImage, Bitmap bitmap) {
        if (bitmap != null) {
            bitmap.recycle();
            bitmap = null;
        }

        // Clean up initial camera-written image file.
        String filePath = oldImage.toString();
        if (filePath.startsWith("file://")) {
            filePath = filePath.substring(7);
        }
        (new File(filePath)).delete();

        checkForDuplicateImage(imageType);
        System.gc();
    }

    /**
     * jpeg????base64?
     *
     * @param bitmap   ?
     *
     **/
    private 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 = XBase64.encode(code, XBase64.DEFAULT);
                String js_out = new String(output);
                mCallbackCtx.success(js_out);
                js_out = null;
                output = null;
                code = null;
            }
        } catch (Exception e) {
            mCallbackCtx.error("Error compressing image.");
        }
        jpeg_data = null;
    }

    private void photoSucess(Intent intent) {
        //URI????URI?URI??
        //???try-catch???
        Uri uri = intent.getData();
        if (null == uri) {
            uri = mImageUri;
        }
        ContentResolver resolver = getContext().getContentResolver();
        XPathResolver pathResolver = new XPathResolver(null == uri ? null : uri.toString(), "", getContext());
        Bitmap bitmap = null;
        try {
            if (!mAllowEdit) {
                String path = pathResolver.resolve();
                if (!XStringUtils.isEmptyString(path)) {
                    bitmap = XUtils.decodeBitmap(path);
                }
            } else {
                //??????Android???
                bitmap = intent.getExtras().getParcelable("data");

                //?????URI
                if (bitmap == null) {
                    bitmap = getCroppedBitmap(intent);
                }
            }
        } catch (OutOfMemoryError e) {
            mCallbackCtx.error("OutOfMemoryError when decode image.");
            return;
        }
        if (mDestType == DATA_URL) {
            int rotate = 0;
            String[] cols = { MediaStore.Images.Media.ORIENTATION };
            Cursor cursor = resolver.query(uri, cols, null, null, null);
            if (null != cursor) {
                cursor.moveToPosition(0);
                rotate = cursor.getInt(0);
                cursor.close();
            }
            if (0 != rotate) {
                Matrix matrix = new Matrix();
                matrix.setRotate(rotate);
                bitmap = bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
            }
            bitmap = scaleBitmap(bitmap);
            processPicture(bitmap);
            bitmap.recycle();
            bitmap = null;
            System.gc();
        } else if (mTargetHeight > 0 && mTargetWidth > 0) {
            try {
                Bitmap scaleBitmap = scaleBitmap(bitmap);

                String fileName = XConfiguration.getInstance().getWorkDirectory() + RESIZED_PIC_NAME;
                OutputStream os = new FileOutputStream(fileName);
                scaleBitmap.compress(Bitmap.CompressFormat.JPEG, mQuality, os);
                os.close();

                bitmap.recycle();
                bitmap = null;
                scaleBitmap.recycle();
                scaleBitmap = null;

                mCallbackCtx.success("file://" + fileName + "?" + System.currentTimeMillis());
                System.gc();
            } catch (Exception e) {
                mCallbackCtx.error("Error retrieving image.");
                return;
            }
        } else {
            mCallbackCtx.success(XConstant.FILE_SCHEME + pathResolver.resolve());
        }
    }
}