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);
}
}
}