com.almalence.util.Util.java Source code

Java tutorial

Introduction

Here is the source code for com.almalence.util.Util.java

Source

/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * 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 com.almalence.util;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.location.Location;
import android.net.Uri;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.StatFs;
import android.provider.DocumentsContract;
import android.provider.Settings;
import android.support.v4.provider.DocumentFile;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.OrientationEventListener;
import android.view.Surface;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;

/* <!-- +++
 import com.almalence.opencam_plus.ApplicationScreen;
 import com.almalence.opencam_plus.cameracontroller.CameraController;
 +++ --> */
// <!-- -+-
import com.almalence.opencam.ApplicationScreen;
import com.almalence.opencam.cameracontroller.CameraController;
//-+- -->

/**
 * Collection of utility functions used in this package.
 */
public final class Util {

    private static final String TAG = "Util";

    // The brightness setting used when it is set to automatic in the system.
    // The reason why it is set to 0.7 is just because 1.0 is too bright.
    // Use the same setting among the Camera, VideoCamera and Panorama modes.
    private static final float DEFAULT_CAMERA_BRIGHTNESS = 0.7f;

    // Orientation hysteresis amount used in rounding, in degrees
    private static final int ORIENTATION_HYSTERESIS = 5;

    private static final String REVIEW_ACTION = "com.android.camera.action.REVIEW";

    private static boolean sIsTabletUI;
    private static float sPixelDensity = 1;

    // Workaround for QC cameras with broken face detection on front camera
    private static boolean sNoFaceDetectOnFrontCamera;
    private static boolean sNoFaceDetectOnRearCamera;

    private static Matrix mMeteringMatrix = new Matrix();

    private Util() {
    }

    public static void initialize(Context context) {
        sIsTabletUI = false;

        DisplayMetrics metrics = new DisplayMetrics();
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        wm.getDefaultDisplay().getMetrics(metrics);
        sPixelDensity = metrics.density;
    }

    public static boolean isTabletUI() {
        return sIsTabletUI;
    }

    public static int dpToPixel(int dp) {
        return Math.round(sPixelDensity * dp);
    }

    public static boolean noFaceDetectOnFrontCamera() {
        return sNoFaceDetectOnFrontCamera;
    }

    public static boolean noFaceDetectOnRearCamera() {
        return sNoFaceDetectOnRearCamera;
    }

    // Rotates the bitmap by the specified degree.
    // If a new bitmap is created, the original bitmap is recycled.
    public static Bitmap rotate(Bitmap b, int degrees) {
        return rotateAndMirror(b, degrees, false);
    }

    // Rotates and/or mirrors the bitmap. If a new bitmap is created, the
    // original bitmap is recycled.
    public static Bitmap rotateAndMirror(Bitmap b, int degrees, boolean mirror) {
        if ((degrees != 0 || mirror) && b != null) {
            Matrix m = new Matrix();
            // Mirror first.
            // horizontal flip + rotation = -rotation + horizontal flip
            if (mirror) {
                m.postScale(-1, 1);
                degrees = (degrees + 360) % 360;
                if (degrees == 0 || degrees == 180) {
                    m.postTranslate((float) b.getWidth(), 0);
                } else if (degrees == 90 || degrees == 270) {
                    m.postTranslate((float) b.getHeight(), 0);
                } else {
                    throw new IllegalArgumentException("Invalid degrees=" + degrees);
                }
            }
            if (degrees != 0) {
                // clockwise
                m.postRotate(degrees, (float) b.getWidth() / 2, (float) b.getHeight() / 2);
            }

            try {
                Bitmap b2 = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, true);
                if (b != b2) {
                    b.recycle();
                    b = b2;
                }
            } catch (OutOfMemoryError ex) {
                // We have no memory to rotate. Return the original bitmap.
            }
        }
        return b;
    }

    /*
     * Compute the sample size as a function of minSideLength and
     * maxNumOfPixels. minSideLength is used to specify that minimal width or
     * height of a bitmap. maxNumOfPixels is used to specify the maximal size in
     * pixels that is tolerable in terms of memory usage.
     * 
     * The function returns a sample size based on the constraints. Both size
     * and minSideLength can be passed in as -1 which indicates no care of the
     * corresponding constraint. The functions prefers returning a sample size
     * that generates a smaller bitmap, unless minSideLength = -1.
     * 
     * Also, the function rounds up the sample size to a power of 2 or multiple
     * of 8 because BitmapFactory only honors sample size this way. For example,
     * BitmapFactory downsamples an image by 2 even though the request is 3. So
     * we round up the sample size to avoid OOM.
     */
    public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
        int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels);

        int roundedSize;
        if (initialSize <= 8) {
            roundedSize = 1;
            while (roundedSize < initialSize) {
                roundedSize <<= 1;
            }
        } else {
            roundedSize = (initialSize + 7) / 8 * 8;
        }

        return roundedSize;
    }

    private static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength,
            int maxNumOfPixels) {
        double w = options.outWidth;
        double h = options.outHeight;

        int lowerBound = (maxNumOfPixels < 0) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
        int upperBound = (minSideLength < 0) ? 128
                : (int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength));

        if (upperBound < lowerBound) {
            // return the larger one when there is no overlapping zone.
            return lowerBound;
        }

        if (maxNumOfPixels < 0 && minSideLength < 0) {
            return 1;
        } else if (minSideLength < 0) {
            return lowerBound;
        } else {
            return upperBound;
        }
    }

    public static Bitmap makeBitmap(byte[] jpegData, int maxNumOfPixels) {
        try {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, options);
            if (options.mCancel || options.outWidth == -1 || options.outHeight == -1) {
                return null;
            }
            options.inSampleSize = computeSampleSize(options, -1, maxNumOfPixels);
            options.inJustDecodeBounds = false;

            options.inDither = false;
            options.inPreferredConfig = Bitmap.Config.ARGB_8888;
            return BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, options);
        } catch (OutOfMemoryError ex) {
            Log.e(TAG, "Got oom exception ", ex);
            return null;
        }
    }

    public static void closeSilently(Closeable c) {
        if (c == null)
            return;
        try {
            c.close();
        } catch (Exception t) {
        }
    }

    public static void Assert(boolean cond) {
        if (!cond) {
            throw new AssertionError();
        }
    }

    public static <T> T checkNotNull(T object) {
        if (object == null)
            throw new NullPointerException();
        return object;
    }

    public static int nextPowerOf2(int n) {
        n -= 1;
        n |= n >>> 16;
        n |= n >>> 8;
        n |= n >>> 4;
        n |= n >>> 2;
        n |= n >>> 1;
        return n + 1;
    }

    public static float distance(float x, float y, float sx, float sy) {
        float dx = x - sx;
        float dy = y - sy;
        return (float) Math.sqrt(dx * dx + dy * dy);
    }

    public static int clamp(int x, int min, int max) {
        if (x > max)
            return max;
        if (x < min)
            return min;
        return x;
    }

    public static float clamp(float x, float min, float max) {
        if (x > max)
            return max;
        if (x < min)
            return min;
        return x;
    }

    public static int getDisplayRotation(Activity activity) {
        int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
        switch (rotation) {
        case Surface.ROTATION_0:
            return 0;
        case Surface.ROTATION_90:
            return 90;
        case Surface.ROTATION_180:
            return 180;
        case Surface.ROTATION_270:
            return 270;
        default:
            break;
        }
        return 0;
    }

    @TargetApi(9)
    public static int getDisplayOrientation(int degrees, int cameraId) {
        // See android.hardware.Camera.setDisplayOrientation for
        // documentation.
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, info);
        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360; // compensate the mirror
        } else { // back-facing
            result = (info.orientation - degrees + 360) % 360;
        }
        return result;
    }

    public static int roundOrientation(int orientation, int orientationHistory) {
        boolean changeOrientation = false;
        if (orientationHistory == OrientationEventListener.ORIENTATION_UNKNOWN) {
            changeOrientation = true;
        } else {
            int dist = Math.abs(orientation - orientationHistory);
            dist = Math.min(dist, 360 - dist);
            changeOrientation = (dist >= 45 + ORIENTATION_HYSTERESIS);
        }
        if (changeOrientation) {
            return ((orientation + 45) / 90 * 90) % 360;
        }
        return orientationHistory;
    }

    //value to know current interval for orientation. Not using system functions consuming more resources
    //0 - unknown (need initial calculations), 1- [0 +-40], 2 [270 +-40], 3 - [180 +-40], 4 - [90 +-40]
    private static int orientationDisplayInterval = 0;

    public static void setOrientationIntervalInitial() {
        orientationDisplayInterval = 0;
    }

    private static void setOrientationInterval(int orientation) {
        if (orientation >= 320 || orientation < 40)
            orientationDisplayInterval = 1;
        else if (orientation >= 230 && orientation < 310)
            orientationDisplayInterval = 2;
        else if (orientation >= 140 && orientation < 220)
            orientationDisplayInterval = 3;
        else if (orientation >= 50 && orientation < 130)
            orientationDisplayInterval = 4;
    }

    public static boolean checkOrientationInterval(int orientation) {
        //if 0 - set initial interval, if not - check if new value is in the same interval
        switch (orientationDisplayInterval) {
        case 1:
            if (orientation >= 320 || orientation < 40)
                return true;
            break;
        case 2:
            if (orientation >= 230 && orientation < 310)
                return true;
            break;
        case 3:
            if (orientation >= 140 && orientation < 220)
                return true;
            break;
        case 4:
            if (orientation >= 50 && orientation < 130)
                return true;
            break;
        default:
            break;
        }
        setOrientationInterval(orientation);
        return false;
    }

    // Returns the largest picture size which matches the given aspect ratio.
    public static Size getOptimalVideoSnapshotPictureSize(List<Size> sizes, double targetRatio) {
        // Use a very small tolerance because we want an exact match.
        final double ASPECT_TOLERANCE = 0.001;
        if (sizes == null)
            return null;

        Size optimalSize = null;

        // Try to find a size matches aspect ratio and has the largest width
        for (Size size : sizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
                continue;
            if (optimalSize == null || size.width > optimalSize.width) {
                optimalSize = size;
            }
        }

        // Cannot find one that matches the aspect ratio. This should not
        // happen.
        // Ignore the requirement.
        if (optimalSize == null) {
            Log.w(TAG, "No picture size match the aspect ratio");
            for (Size size : sizes) {
                if (optimalSize == null || size.width > optimalSize.width) {
                    optimalSize = size;
                }
            }
        }
        return optimalSize;
    }

    public static void dumpParameters(Parameters parameters) {
        String flattened = parameters.flatten();
        StringTokenizer tokenizer = new StringTokenizer(flattened, ";");
        Log.d(TAG, "Dump all camera parameters:");
        while (tokenizer.hasMoreElements()) {
            Log.d(TAG, tokenizer.nextToken());
        }
    }

    private static int[] mLocation = new int[2];

    // This method is not thread-safe.
    public static boolean pointInView(float x, float y, View v) {
        v.getLocationInWindow(mLocation);
        return x >= mLocation[0] && x < (mLocation[0] + v.getWidth()) && y >= mLocation[1]
                && y < (mLocation[1] + v.getHeight());
    }

    public static void dumpRect(RectF rect, String msg) {
        Log.v(TAG, msg + "=(" + rect.left + "," + rect.top + "," + rect.right + "," + rect.bottom + ")");
    }

    public static void rectFToRect(RectF rectF, Rect rect) {
        rect.left = Math.round(rectF.left);
        rect.top = Math.round(rectF.top);
        rect.right = Math.round(rectF.right);
        rect.bottom = Math.round(rectF.bottom);
    }

    public static void prepareMatrix(Matrix matrix, boolean mirror, int displayOrientation, int viewWidth,
            int viewHeight) {
        // Need mirror for front camera.
        matrix.setScale(mirror ? -1 : 1, 1);
        // This is the value for android.hardware.Camera.setDisplayOrientation.
        matrix.postRotate(displayOrientation);
        // Camera driver coordinates range from (-1000, -1000) to (1000, 1000).
        // UI coordinates range from (0, 0) to (width, height).
        matrix.postScale(viewWidth / 2000f, viewHeight / 2000f);
        matrix.postTranslate(viewWidth / 2f, viewHeight / 2f);
    }

    public static void fadeIn(View view) {
        if (view.getVisibility() == View.VISIBLE)
            return;

        view.setVisibility(View.VISIBLE);
        Animation animation = new AlphaAnimation(0F, 1F);
        animation.setDuration(400);
        view.startAnimation(animation);
    }

    public static void fadeOut(View view) {
        if (view.getVisibility() != View.VISIBLE)
            return;

        Animation animation = new AlphaAnimation(1F, 0F);
        animation.setDuration(400);
        view.startAnimation(animation);
        view.setVisibility(View.GONE);
    }

    public static void setGpsParameters(Parameters parameters, Location loc) {
        // Clear previous GPS location from the parameters.
        parameters.removeGpsData();

        // We always encode GpsTimeStamp
        parameters.setGpsTimestamp(System.currentTimeMillis() / 1000);

        // Set GPS location.
        if (loc != null) {
            double lat = loc.getLatitude();
            double lon = loc.getLongitude();
            boolean hasLatLon = (lat != 0.0d) || (lon != 0.0d);

            if (hasLatLon) {
                // Log.d(TAG, "Set gps location");
                parameters.setGpsLatitude(lat);
                parameters.setGpsLongitude(lon);
                parameters.setGpsProcessingMethod(loc.getProvider().toUpperCase());
                if (loc.hasAltitude()) {
                    parameters.setGpsAltitude(loc.getAltitude());
                } else {
                    // for NETWORK_PROVIDER location provider, we may have
                    // no altitude information, but the driver needs it, so
                    // we fake one.
                    parameters.setGpsAltitude(0);
                }
                if (loc.getTime() != 0) {
                    // Location.getTime() is UTC in milliseconds.
                    // gps-timestamp is UTC in seconds.
                    long utcTimeSeconds = loc.getTime() / 1000;
                    parameters.setGpsTimestamp(utcTimeSeconds);
                }
            } else {
                loc = null;
            }
        }
    }

    public static boolean isNumeric(String str) {
        NumberFormat formatter = NumberFormat.getInstance();
        ParsePosition pos = new ParsePosition(0);
        formatter.parse(str, pos);
        return str.length() == pos.getIndex();
    }

    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            // Calculate ratios of height and width to requested height and
            // width
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);

            // Choose the smallest ratio as inSampleSize value, this will
            // guarantee
            // a final image with both dimensions larger than or equal to the
            // requested height and width.
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }

        return inSampleSize;
    }

    public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {

        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);
    }

    public static String createName(String format, long dateTaken) {
        Date date = new Date(dateTaken);
        SimpleDateFormat dateFormat = new SimpleDateFormat(format);
        return dateFormat.format(date);
    }

    public static String createNameForOriginalFrames(String format, long dateTaken, int frameIndex) {
        Date date = new Date(dateTaken);
        SimpleDateFormat dateFormat = new SimpleDateFormat(format);
        return dateFormat.format(date);
    }

    public static boolean isUriValid(Uri uri, ContentResolver resolver) {
        if (uri == null)
            return false;

        try {
            ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r");
            if (pfd == null) {
                Log.e(TAG, "Fail to open URI. URI=" + uri);
                return false;
            }
            pfd.close();
        } catch (IOException ex) {
            return false;
        }
        return true;
    }

    public static void viewUri(Uri uri, Context context) {
        if (!isUriValid(uri, context.getContentResolver())) {
            Log.e(TAG, "Uri invalid. uri=" + uri);
            return;
        }

        try {
            context.startActivity(new Intent(Util.REVIEW_ACTION, uri));
        } catch (ActivityNotFoundException ex) {
            try {
                context.startActivity(new Intent(Intent.ACTION_VIEW, uri));
            } catch (ActivityNotFoundException e) {
                Log.e(TAG, "review image fail. uri=" + uri, e);
            }
        }
    }

    public static void enterLightsOutMode(Window window) {
        WindowManager.LayoutParams params = window.getAttributes();
        window.setAttributes(params);
    }

    public static void initializeScreenBrightness(Window win, ContentResolver resolver) {
        // Overright the brightness settings if it is automatic
        int mode = Settings.System.getInt(resolver, Settings.System.SCREEN_BRIGHTNESS_MODE,
                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
        if (mode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) {
            WindowManager.LayoutParams winParams = win.getAttributes();
            winParams.screenBrightness = DEFAULT_CAMERA_BRIGHTNESS;
            win.setAttributes(winParams);
        }
    }

    /**
     * SHAME@JAVA
     */
    public static float mathSquare(double value) {
        return (float) (value * value);
    }

    /*************************************************************************************************
     * Returns size in MegaBytes.
     * 
     * If you need calculate external memory, change this: StatFs statFs = new
     * StatFs(Environment.getRootDirectory().getAbsolutePath()); to this: StatFs
     * statFs = new
     * StatFs(Environment.getExternalStorageDirectory().getAbsolutePath());
     **************************************************************************************************/
    public static long TotalDeviceMemory() {
        StatFs statFs = new StatFs(Environment.getDataDirectory().getPath());
        long blockSize = statFs.getBlockSize();
        long blockCount = statFs.getBlockCount();
        return (blockCount * blockSize) / 1048576;
    }

    public static long TotalExternalMemory() {
        StatFs statFs = new StatFs(Environment.getExternalStorageDirectory().getPath());
        long blockSize = statFs.getBlockSize();
        long blockCount = statFs.getBlockCount();
        return (blockCount * blockSize) / 1048576;
    }

    public static long FreeDeviceMemory() {
        StatFs statFs = new StatFs(Environment.getDataDirectory().getPath());
        long blockSize = statFs.getBlockSize();
        long availableBloks = statFs.getAvailableBlocks();
        return (availableBloks * blockSize) / 1048576;
    }

    public static long FreeExternalMemory() {
        StatFs statFs = new StatFs(Environment.getExternalStorageDirectory().getPath());
        long blockSize = statFs.getBlockSize();
        long availableBloks = statFs.getAvailableBlocks();
        return (availableBloks * blockSize) / 1048576;
    }

    public static long BusyDeviceMemory() {
        StatFs statFs = new StatFs(Environment.getRootDirectory().getAbsolutePath());
        long Total = (statFs.getBlockCount() * statFs.getBlockSize()) / 1048576;
        long Free = (statFs.getAvailableBlocks() * statFs.getBlockSize()) / 1048576;
        return (Total - Free);
    }

    public static long BusyExternalMemory() {
        StatFs statFs = new StatFs(Environment.getExternalStorageDirectory().getAbsolutePath());
        long Total = (statFs.getBlockCount() * statFs.getBlockSize()) / 1048576;
        long Free = (statFs.getAvailableBlocks() * statFs.getBlockSize()) / 1048576;
        return (Total - Free);
    }

    public static long AvailablePictureCount() {
        long freeMemory = Util.FreeDeviceMemory() - 5;
        if (freeMemory < 5)
            return 0;

        // RAW size of picture is width*height*3 (rgb). JPEG compress picture on
        // average 86% (compress quality 95)
        CameraController.Size saveImageSize = CameraController.getCameraImageSize();
        double imageSize = ((double) (saveImageSize.getWidth() * saveImageSize.getHeight() * 3 * 0.14) / 1048576d);
        if (imageSize == 0)
            return 0;

        return Math.round(freeMemory / imageSize);
    }

    public static <T extends Comparable<? super T>> List<T> asSortedList(Collection<T> c) {
        List<T> list = new ArrayList<T>(c);
        java.util.Collections.sort(list);
        return list;
    }

    public static void initializeMeteringMatrix() {
        Matrix matrix = new Matrix();
        Util.prepareMatrix(matrix, CameraController.isFrontCamera(), 0, ApplicationScreen.getPreviewWidth(),
                ApplicationScreen.getPreviewHeight());
        matrix.invert(mMeteringMatrix);
    }

    public static Matrix getMeteringMatrix() {
        return mMeteringMatrix;
    }

    public static Rect convertToDriverCoordinates(Rect rect) {
        RectF rectF = new RectF(rect.left, rect.top, rect.right, rect.bottom);
        mMeteringMatrix.mapRect(rectF);
        Util.rectFToRect(rectF, rect);

        if (rect.left < -1000)
            rect.left = -1000;
        if (rect.left > 1000)
            rect.left = 1000;

        if (rect.right < -1000)
            rect.right = -1000;
        if (rect.right > 1000)
            rect.right = 1000;

        if (rect.top < -1000)
            rect.top = -1000;
        if (rect.top > 1000)
            rect.top = 1000;

        if (rect.bottom < -1000)
            rect.bottom = -1000;
        if (rect.bottom > 1000)
            rect.bottom = 1000;

        return rect;
    }

    public static String toString(final Object[] objects, final char separator) {
        final StringBuilder stringBuilder = new StringBuilder();

        for (final Object object : objects) {
            stringBuilder.append(object.toString());
            stringBuilder.append(separator);
        }

        return stringBuilder.toString();
    }

    public static String logMatrix(final float[] transform, final int width, final int height) {
        if (width * height < transform.length) {
            throw new ArrayIndexOutOfBoundsException(
                    String.format("width(%d) * height(%d) > transform(%d)", width, height, transform));
        }

        String format = "";
        final Object[] args = new Object[width * height];
        int cursor = 0;

        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                format += "% #6.2f  ";
                args[cursor] = transform[cursor];
                cursor++;
            }

            format += "\n";
        }

        return String.format(format, args);
    }

    public static boolean shouldRemapOrientation(final int orientationProc, final int rotation) {
        return (orientationProc == Configuration.ORIENTATION_LANDSCAPE && rotation == Surface.ROTATION_0)
                || (orientationProc == Configuration.ORIENTATION_LANDSCAPE && rotation == Surface.ROTATION_180)
                || (orientationProc == Configuration.ORIENTATION_PORTRAIT && rotation == Surface.ROTATION_90)
                || (orientationProc == Configuration.ORIENTATION_PORTRAIT && rotation == Surface.ROTATION_270);
    }

    // Get File object from DocumentFile object.
    // It's possible only if documentFile stored in phone memory, not SD-card.
    public static File getFileFromDocumentFile(DocumentFile documentFile) {
        try {
            File file = new File(URI.create(documentFile.getUri().toString()));
            return file;
        } catch (Exception e) {
            return null;
        }
    }

    // Get File absolute path from DocumentFile object.
    // This method should be used only for files saved to SD-card.
    public static String getAbsolutePathFromDocumentFile(DocumentFile documentFile) {
        // We can't get absolute path from DocumentFile or Uri.
        // It is a hack to build absolute path by DocumentFile.
        // May not work on some devices.
        Uri uri = documentFile.getUri();
        final String docId = DocumentsContract.getDocumentId(uri);
        final String[] split = docId.split(":");
        final String id = split[1];

        String sd = null;
        sd = System.getenv("SECONDARY_STORAGE");
        if (sd == null) {
            sd = System.getenv("EXTERNAL_STORAGE");
        }

        if (sd != null) {
            // On some devices SECONDARY_STORAGE has several paths
            // separated with a colon (":"). This is why we split
            // the String.
            String[] paths = sd.split(":");
            for (String p : paths) {
                File fileSD = new File(p);
                if (fileSD.isDirectory()) {
                    sd = fileSD.getAbsolutePath();
                }
            }

            String documentPath = sd + "/" + id;
            return documentPath;
        }
        return null;
    }

    public static int getMaxImageSizeIndex(android.util.Size[] ImageSizes) {
        int maxSizeIndex = 0;
        long maxSize = ImageSizes[0].getWidth() * ImageSizes[0].getHeight();
        for (int i = 1; i < ImageSizes.length; i++) {
            long currentSize = ImageSizes[i].getWidth() * ImageSizes[i].getHeight();
            if (currentSize > maxSize) {
                maxSizeIndex = i;
                maxSize = currentSize;
            }
        }

        return maxSizeIndex;
    }

    public static boolean listContainsSize(List<CameraController.Size> list, CameraController.Size size) {
        boolean res = false;

        for (CameraController.Size s : list) {
            if (s.getWidth() == size.getWidth() && s.getHeight() == size.getHeight()) {
                res = true;
                break;
            }
        }

        return res;
    }
}