content: package com.almalence.plugins.vf.barcodescanner;
  
  import java.io.ByteArrayOutputStream;
  import java.io.File;
  import java.io.FileNotFoundException;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.util.Calendar;
  
  import android.content.Context;
  import android.content.DialogInterface;
  import android.content.DialogInterface.OnDismissListener;
  import android.content.SharedPreferences;
  import android.content.SharedPreferences.Editor;
  import android.graphics.Bitmap;
  import android.graphics.Bitmap.CompressFormat;
  import android.graphics.BitmapFactory;
  import android.graphics.Canvas;
  import android.graphics.ImageFormat;
  import android.graphics.Matrix;
  import android.graphics.Paint;
  import android.graphics.Rect;
  import android.graphics.YuvImage;
  import android.os.AsyncTask;
  import android.preference.PreferenceManager;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.view.View.OnClickListener;
  import android.view.ViewGroup.LayoutParams;
  import android.widget.AdapterView;
  import android.widget.AdapterView.OnItemClickListener;
  import android.widget.RelativeLayout;
  
  import com.almalence.ui.RotateImageView;
  import com.almalence.util.ImageConversion;
  import com.google.zxing.BinaryBitmap;
  import com.google.zxing.MultiFormatReader;
  import com.google.zxing.PlanarYUVLuminanceSource;
  import com.google.zxing.ReaderException;
  import com.google.zxing.Result;
  import com.google.zxing.common.HybridBinarizer;
  /* <!-- +++
   import com.almalence.opencam_plus.ApplicationScreen;
   import com.almalence.opencam_plus.MainScreen;
   import com.almalence.opencam_plus.PluginManager;
   import com.almalence.opencam_plus.PluginViewfinder;
   import com.almalence.opencam_plus.R;
   import com.almalence.opencam_plus.SoundPlayer;
   import com.almalence.opencam_plus.cameracontroller.CameraController;
  
   +++ --> */
  //<!-- -+-
  import com.almalence.opencam.ApplicationScreen;
  import com.almalence.opencam.MainScreen;
  import com.almalence.opencam.PluginManager;
  import com.almalence.opencam.PluginViewfinder;
  import com.almalence.opencam.R;
  import com.almalence.opencam.SoundPlayer;
  import com.almalence.opencam.cameracontroller.CameraController;
  //-+- -->
  
  public class BarcodeScannerVFPlugin extends PluginViewfinder
  {
  
  	private static final double			BOUNDS_FRACTION			= 0.6;
  	private static final Boolean		ON						= true;
  	private static final Boolean		OFF						= false;
  
  	private final MultiFormatReader		mMultiFormatReader		= new MultiFormatReader();
  	private SoundPlayer					mSoundPlayer			= null;
  	private static Boolean				mBarcodeScannerState	= OFF;
  	private static Boolean				decodedProcessing		= false;
  	private int							mFrameCounter			= 0;
  	private int							mOrientation			= 0;
  	private BoundingView				mBound					= null;
  	private RotateImageView				mBarcodesListButton;
  	private View						mButtonsLayout;
  	private BarcodeHistoryListDialog	barcodeHistoryDialog;
  	private BarcodeViewDialog			barcodeViewDialog;
  
  	//takes each X image, skipping other
  	private int							skipImgNum				= 10;
  		
  	public BarcodeScannerVFPlugin()
  	{
  		super("com.almalence.plugins.barcodescannervf", R.xml.preferences_vf_barcodescanner, 0,
  				R.drawable.gui_almalence_settings_scene_barcode_on, "Barcode scanner");
  	}
  
  	@Override
  	public void onResume()
  	{
  		updatePreferences();
  	}
  
  	@Override
  	public boolean needPreviewFrame()
  	{
  		if (mBarcodeScannerState == ON)
  			return true;
  		else
  			return false;
  	}
  	
  	void updatePreferences()
  	{
  		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ApplicationScreen.getMainContext());
  		mBarcodeScannerState = prefs.getBoolean("PrefBarcodescannerVF", false);
  
  		if (mBarcodeScannerState == ON)
  		{
  			quickControlIconID = R.drawable.gui_almalence_settings_scene_barcode_on;
  		} else
  		{
  			quickControlIconID = R.drawable.gui_almalence_settings_off_barcode_scanner;
  		}
  
  		showGUI();
  	}
  
  	@Override
  	public void onOrientationChanged(int orientation)
  	{
  		mOrientation = orientation;
  		if (mBarcodesListButton != null)
  		{
  			mBarcodesListButton.setOrientation(ApplicationScreen.getGUIManager().getLayoutOrientation());
  			mBarcodesListButton.invalidate();
  			mBarcodesListButton.requestLayout();
  		}
  		if (barcodeHistoryDialog != null)
  		{
  			barcodeHistoryDialog.setRotate(ApplicationScreen.getGUIManager().getLayoutOrientation());
  		}
  		if (barcodeViewDialog != null)
  		{
  			barcodeViewDialog.setRotate(ApplicationScreen.getGUIManager().getLayoutOrientation());
  		}
  	}
  
  	@Override
  	public void onQuickControlClick()
  	{
  		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ApplicationScreen.getMainContext());
  		Editor editor = prefs.edit();
  
  		if (mBarcodeScannerState == ON)
  		{
  			quickControlIconID = R.drawable.gui_almalence_settings_off_barcode_scanner;
  			editor.putBoolean("PrefBarcodescannerVF", false);
  		} else
  		{
  			quickControlIconID = R.drawable.gui_almalence_settings_scene_barcode_on;
  			editor.putBoolean("PrefBarcodescannerVF", true);
  		}
  		editor.commit();
  		
  		updatePreferences();
  
  		CameraController.checkNeedPreviewFrame();
  	}
  
  	/**
  	 * Show or hide GUI elements of plugin. Depends on plugin state and history.
  	 */
  	public void showGUI()
  	{
  		if (mBarcodeScannerState == ON)
  		{
  			if (mBound == null)
  			{
  				createBoundView();
  			}
  			if (mBarcodesListButton == null)
  			{
  				createScreenButton();
  			}
  
  			if (mBound != null)
  			{
  				mBound.setVisibility(View.VISIBLE);
  			}
  			if (mBarcodesListButton != null)
  			{
  				if (BarcodeStorageHelper.getBarcodesList() != null && BarcodeStorageHelper.getBarcodesList().size() > 0)
  				{
  					mBarcodesListButton.setVisibility(View.VISIBLE);
  				} else
  				{
  					mBarcodesListButton.setVisibility(View.GONE);
  				}
  			}
  		} else
  		{
  			if (mBound != null)
  			{
  				mBound.setVisibility(View.GONE);
  			}
  			if (mBarcodesListButton != null)
  			{
  				mBarcodesListButton.setVisibility(View.GONE);
  			}
  		}
  	}
  
  	public void initializeSoundPlayer()
  	{
  		mSoundPlayer = new SoundPlayer(ApplicationScreen.getMainContext(), ApplicationScreen.getMainContext().getResources()
  				.openRawResourceFd(R.raw.plugin_vf_focus_ok));
  	}
  
  	public void releaseSoundPlayer()
  	{
  		if (mSoundPlayer != null)
  		{
  			mSoundPlayer.release();
  			mSoundPlayer = null;
  		}
  	}
  
  	@Override
  	public void onCameraParametersSetup()
  	{
  		initializeSoundPlayer();
  	}
  
  	@Override
  	public void onPause()
  	{
  		releaseSoundPlayer();
  		if (mBound != null)
  			mBound.setVisibility(View.GONE);
  		if (mBarcodesListButton != null)
  			mBarcodesListButton.setVisibility(View.GONE);
  		clearViews();
  		mBound = null;
  		mBarcodesListButton = null;
  		
  		if (mBarcodeScannerState == ON)
  		{
  			onQuickControlClick();
  		}
  	}
  
  	@Override
  	public void onGUICreate()
  	{
  		showGUI();
  	}
  
  	/**
  	 * Create bound view.
  	 */
  	public void createBoundView()
  	{
  		if (mBound != null)
  		{
  			return;
  		}
  
  		mBound = new BoundingView(ApplicationScreen.getMainContext());
  		mBound.setVisibility(View.VISIBLE);
  
  		RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT,
  				LayoutParams.MATCH_PARENT);
  		((RelativeLayout) ApplicationScreen.instance.findViewById(R.id.specialPluginsLayout)).addView(mBound, params);
  
  		mBound.setLayoutParams(params);
  
  		((RelativeLayout) ApplicationScreen.instance.findViewById(R.id.specialPluginsLayout)).requestLayout();
  	}
  
  	/**
  	 * Create history button.
  	 */
  	public void createScreenButton()
  	{
  		LayoutInflater inflator = ApplicationScreen.instance.getLayoutInflater();
  		mButtonsLayout = inflator.inflate(R.layout.plugin_vf_barcodescanner_layout, null, false);
  		mButtonsLayout.setVisibility(View.VISIBLE);
  
  		mBarcodesListButton = (RotateImageView) mButtonsLayout.findViewById(R.id.buttonBarcodesList);
  
  		ApplicationScreen.getGUIManager().removeViews(mButtonsLayout, R.id.specialPluginsLayout3);
  
  		mBarcodesListButton.setOnClickListener(new OnClickListener()
  		{
  			@Override
  			public void onClick(View v)
  			{
  				showBarcodesHistoryDialog();
  			}
  
  		});
  
  		RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
  				LayoutParams.WRAP_CONTENT);
  		params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
  		params.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
  
  		((RelativeLayout) ApplicationScreen.instance.findViewById(R.id.specialPluginsLayout3)).addView(mButtonsLayout,
  				params);
  
  		mButtonsLayout.setLayoutParams(params);
  		mButtonsLayout.requestLayout();
  
  		((RelativeLayout) ApplicationScreen.instance.findViewById(R.id.specialPluginsLayout3)).requestLayout();
  
  		mBarcodesListButton.setOrientation(ApplicationScreen.getGUIManager().getLayoutOrientation());
  		mBarcodesListButton.invalidate();
  		mBarcodesListButton.requestLayout();
  	}
  
  	protected void showBarcodesHistoryDialog()
  	{
  		barcodeHistoryDialog = new BarcodeHistoryListDialog(ApplicationScreen.instance);
  		barcodeHistoryDialog.setRotate(ApplicationScreen.getGUIManager().getLayoutOrientation());
  
  		barcodeHistoryDialog.list.setOnItemClickListener(new OnItemClickListener()
  		{
  			@Override
  			public void onItemClick(AdapterView<?> parent, View view, int position, long id)
  			{
  				Barcode barcode = (Barcode) barcodeHistoryDialog.list.getAdapter().getItem(position);
  				showBarcodeViewDialog(barcode);
  			}
  		});
  
  		barcodeHistoryDialog.setOnDismissListener(new OnDismissListener()
  		{
  			public void onDismiss(DialogInterface dialog)
  			{
  				mBarcodeScannerState = ON;
  				showGUI();
  			}
  		});
  		mBarcodeScannerState = OFF;
  
  		barcodeHistoryDialog.show();
  	}
  
  	@Override
  	public void onPreviewFrame(byte[] data)
  	{
  		if (mBarcodeScannerState == OFF)
  			return;
  		mFrameCounter++;
  		if (mFrameCounter != skipImgNum)
  		{
  			return;
  		}
  
  		new DecodeAsyncTask(ApplicationScreen.getPreviewWidth(), ApplicationScreen.getPreviewHeight()).execute(data);
  
  		mFrameCounter = 0;
  	}
  
  	public synchronized PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height,
  			Rect boundingRect)
  	{
  		return new PlanarYUVLuminanceSource(data, width, height, boundingRect.left, boundingRect.top,
  				boundingRect.width(), boundingRect.height(), false);
  	}
  
  	/**
  	 * Handle success decoded barcode.
  	 * 
  	 * @param barcode
  	 */
  	public void onDecoded(Barcode barcode)
  	{
  		if (mBarcodeScannerState == OFF)
  		{
  			return;
  		}
  
  		// <!-- -+-
  		// sale hook
  		if (barcode.getData().equals("abc.almalence.com/qrpromo") && !MainScreen.getInstance().isUnlockedAll())
  		{
  			MainScreen.getInstance().activateCouponSale();
  			ApplicationScreen.getGUIManager().showStore();
  			return;
  		}
  		// -+- -->
  
  		BarcodeStorageHelper.addBarcode(barcode);
  
  		showBarcodeViewDialog(barcode);
  
  		if (mSoundPlayer != null)
  			if (!ApplicationScreen.instance.isShutterSoundEnabled())
  				mSoundPlayer.play();
  		
  		decodedProcessing = false;
  	}
  
  	protected void showBarcodeViewDialog(Barcode barcode)
  	{
  		try
  		{
  			barcodeViewDialog = new BarcodeViewDialog(ApplicationScreen.instance, barcode);
  			barcodeViewDialog.setRotate(ApplicationScreen.getGUIManager().getLayoutOrientation());
  			showGUI();
  
  			barcodeViewDialog.setOnDismissListener(new OnDismissListener()
  			{
  				public void onDismiss(DialogInterface dialog)
  				{
  					mBarcodeScannerState = ON;
  				}
  			});
  
  			mBarcodeScannerState = OFF;
  			barcodeViewDialog.show();
  		} catch (Exception e)
  		{
  		}
  	}
  
  	/**
  	 * @return bounding rect for camera
  	 */
  	public final synchronized Rect getBoundingRect()
  	{
  
  		double heightFraction = BOUNDS_FRACTION;
  		double widthFraction = BOUNDS_FRACTION;
  
  		int height = (int) (ApplicationScreen.getPreviewHeight() * heightFraction);
  		int width = (int) (ApplicationScreen.getPreviewWidth() * widthFraction);
  		int left = (int) (ApplicationScreen.getPreviewWidth() * ((1 - widthFraction) / 2));
  		int top = (int) (ApplicationScreen.getPreviewHeight() * ((1 - heightFraction) / 2));
  		int right = left + width;
  		int bottom = top + height;
  
  		return new Rect(left, top, right, bottom);
  	}
  
  	/**
  	 * @return bounding rect for ui
  	 */
  	public final synchronized Rect getBoundingRectUi(int uiWidth, int uiHeight)
  	{
  		double heightFraction = BOUNDS_FRACTION;
  		double widthFraction = BOUNDS_FRACTION;
  
  		int height = (int) (uiHeight * heightFraction);
  		int width = (int) (uiWidth * widthFraction);
  		int left = (int) (uiWidth * ((1 - widthFraction) / 2));
  		int top = (int) (uiHeight * ((1 - heightFraction) / 2));
  		int right = left + width;
  		int bottom = top + height;
  
  		return new Rect(left, top, right, bottom);
  	}
  
  	/**
  	 * Asynchronous task for decoding and finding barcode
  	 */
  	private class DecodeAsyncTask extends AsyncTask<byte[], Void, Barcode>
  	{
  		private int	width;
  		private int	height;
  
  		private DecodeAsyncTask(int width, int height)
  		{
  			this.width = width;
  			this.height = height;
  		}
  
  		@Override
  		protected Barcode doInBackground(byte[]... datas)
  		{
  			Result rawResult = null;
  			File file = null;
  			final PlanarYUVLuminanceSource source = buildLuminanceSource(datas[0], width, height, getBoundingRect());
  			if (source != null)
  			{
  				BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
  				try
  				{
  					rawResult = mMultiFormatReader.decodeWithState(bitmap);
  				} catch (ReaderException re)
  				{
  					// nothing to do here
  				} catch (Exception e)
  				{
  					e.printStackTrace();
  				} finally
  				{
  					mMultiFormatReader.reset();
  				}
  			}
  
  			if (rawResult == null)
  			{
  				return null;
  			}
  
  			synchronized (datas)
  			{
  				if (rawResult != null && !decodedProcessing)
  				{
  					decodedProcessing = true;
  					file = saveDecodedImageToFile(datas);
  				} else {
  					return null;
  				}
  			}
  
  			Barcode barcode = null;
  			if (file != null)
  			{
  				barcode = new Barcode(rawResult, file.getAbsolutePath());
  			} else
  			{
  				barcode = new Barcode(rawResult);
  			}
  
  			return barcode;
  		}
  
  		@Override
  		protected void onPostExecute(Barcode barcode)
  		{
  			if (barcode != null)
  			{
  				onDecoded(barcode);
  			}
  		}
  	}
  
  	private synchronized File saveDecodedImageToFile(byte[]... datas)
  	{
  		File file = null;
  		byte[] dataRotated = new byte[datas[0].length];
  		ImageConversion.TransformNV21(datas[0], dataRotated, ApplicationScreen.getPreviewWidth(),
  				ApplicationScreen.getPreviewHeight(), 0, 0, 1);
  		datas[0] = dataRotated;
  
  		Rect rect = new Rect(0, 0, ApplicationScreen.getPreviewHeight(), ApplicationScreen.getPreviewWidth());
  		YuvImage img = new YuvImage(datas[0], ImageFormat.NV21, ApplicationScreen.getPreviewHeight(),
  				ApplicationScreen.getPreviewWidth(), null);
  
  		Calendar d = Calendar.getInstance();
  		String fileFormat = String.format("%04d%02d%02d_%02d%02d%02d", d.get(Calendar.YEAR), d.get(Calendar.MONTH) + 1,
  				d.get(Calendar.DAY_OF_MONTH), d.get(Calendar.HOUR_OF_DAY), d.get(Calendar.MINUTE),
  				d.get(Calendar.SECOND));
  
  		File saveDir = PluginManager.getInstance().getSaveDir(false);
  		file = new File(saveDir, fileFormat + ".jpg");
  		FileOutputStream os = null;
  		try
  		{
  			os = new FileOutputStream(file);
  		} catch (Exception e)
  		{
  			// save always if not working saving to sdcard
  			e.printStackTrace();
  			saveDir = PluginManager.getInstance().getSaveDir(true);
  			file = new File(saveDir, fileFormat + ".jpg");
  			try
  			{
  				os = new FileOutputStream(file);
  			} catch (FileNotFoundException e1)
  			{
  				e1.printStackTrace();
  			}
  		}
  
  		if (os != null)
  		{
  			try
  			{
  				ByteArrayOutputStream baos = new ByteArrayOutputStream();
  				byte[] rawImage = null;
  				img.compressToJpeg(rect, 100, baos);
  				rawImage = baos.toByteArray();
  
  				// This is the same image as the preview but in JPEG and not
  				// rotated
  				Bitmap bitmap = BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
  
  				// Rotate the Bitmap
  				Matrix matrix = new Matrix();
  				matrix.postRotate(mOrientation - 90);
  
  				// We rotate the same Bitmap
  				bitmap = Bitmap.createBitmap(bitmap, 0, 0, ApplicationScreen.getPreviewHeight(), ApplicationScreen.getPreviewWidth(),
  						matrix, false);
  
  				// We dump the rotated Bitmap to the stream
  				bitmap.compress(CompressFormat.JPEG, 100, os);
  
  				os.flush();
  				os.close();
  			} catch (FileNotFoundException e)
  			{
  				e.printStackTrace();
  			} catch (IOException e)
  			{
  				e.printStackTrace();
  			}
  		}
  
  		return file;
  	}
  
  	/**
  	 * View for displaying bounds for active camera region
  	 */
  	class BoundingView extends View
  	{
  		private Paint	paint;
  
  		public BoundingView(Context context)
  		{
  			super(context);
  			paint = new Paint(Paint.ANTI_ALIAS_FLAG);
  			paint.setARGB(110, 128, 128, 128);
  		}
  
  		@Override
  		protected void onDraw(Canvas canvas)
  		{
  			int width = canvas.getWidth();
  			int height = canvas.getHeight();
  			Rect boundingRect = getBoundingRectUi(canvas.getWidth(), canvas.getHeight());
  
  			canvas.drawRect(0, 0, width, boundingRect.top, paint);
  			canvas.drawRect(0, boundingRect.top, boundingRect.left, boundingRect.bottom + 1, paint);
  			canvas.drawRect(boundingRect.right + 1, boundingRect.top, width, boundingRect.bottom + 1, paint);
  			canvas.drawRect(0, boundingRect.bottom + 1, width, height, paint);
  			super.onDraw(canvas);
  		}
  	}
  
  }
  