content: /*
   * Copyright (C) 2009 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
   * 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 &quot;AS IS&quot; 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;
  		}
  	}
  
  	
  //	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;
  //	}
  	
  	
  	// 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)
  	{
  		try{
  		// 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");
  			
  			String documentPath = "/storage" + "/" + docId.replace(":", "/");
  			return documentPath;
  		}
  		// 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;
  		}
  		catch(Exception e)
  		{
  			Log.e(TAG, "Got getAbsolutePathFromDocumentFile exception ", e);	
  		}
  		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;
  	}
  }
  