title: Customize Camera View for Android using zbar
body: <p>I am new in android development. My app should activate the camera to scan a QR code and decode it to a string. This works fine, but I have one more requirement: to present the camera view in a frame and with a logo in one of the corners. I did not find an appropriate API in zbar sdk to make the frame and attach the logo. This is my code:</p>
  
  <p>ZBarScannerActivity.java:</p>
  
  <pre><code>package com.myCompany.android;
  
  import android.app.Activity;
  import android.content.Intent;
  import android.content.pm.PackageManager;
  import android.hardware.Camera;
  import android.media.MediaPlayer;
  import android.media.MediaPlayer.OnCompletionListener;
  import android.os.Bundle;
  import android.os.Handler;
  import android.text.TextUtils;
  import android.util.Log;
  import android.view.Menu;
  import android.view.View;
  import android.view.Window;
  import android.view.WindowManager;
  import android.widget.Toast;
  import net.sourceforge.zbar.Config;
  import net.sourceforge.zbar.Image;
  import net.sourceforge.zbar.ImageScanner;
  import net.sourceforge.zbar.Symbol;
  import net.sourceforge.zbar.SymbolSet;
  
  public class ZBarScannerActivity extends Activity implements Camera.PreviewCallback, ZBarConstants {
  
      private static final String TAG = "ZBarScannerActivity";
      private CameraPreview mPreview;
      private Camera mCamera;
      private ImageScanner mScanner;
      private Handler mAutoFocusHandler;
      private boolean mPreviewing = true;
  
      static {
          System.loadLibrary("iconv");
      }
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
  
          // Hide the window title.
         requestWindowFeature(Window.FEATURE_NO_TITLE);
         getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
  
          mAutoFocusHandler = new Handler();
  
          // Create and configure the ImageScanner;
          setupScanner();
  
          // Create a RelativeLayout container that will hold a SurfaceView,
          // and set it as the content of our activity.
          mPreview = new CameraPreview(this, this, autoFocusCB);
  
          setContentView(mPreview);
      }
  
      public void setupScanner() {
          mScanner = new ImageScanner();
          mScanner.setConfig(0, Config.X_DENSITY, 3);
          mScanner.setConfig(0, Config.Y_DENSITY, 3);
  
          int[] symbols = getIntent().getIntArrayExtra(SCAN_MODES);
          if (symbols != null) {
              mScanner.setConfig(Symbol.NONE, Config.ENABLE, 0);
              for (int symbol : symbols) {
                  mScanner.setConfig(symbol, Config.ENABLE, 1);
              }
              //test customization
           }
      }
  
      @Override
      protected void onResume() {
          super.onResume();
  
          // Open the default i.e. the first rear facing camera.
          mCamera = Camera.open();
          mPreviewing = true;
          mPreview.setCamera(mCamera);
      }
  
      @Override
      protected void onPause() {
          super.onPause();
  
          // Because the Camera object is a shared resource, it's very
          // important to release it when the activity is paused.
          if (mCamera != null) {
              mPreview.setCamera(null);
              mCamera.cancelAutoFocus();
              mCamera.setPreviewCallback(null);
              mCamera.stopPreview();
              mCamera.release();
              mPreviewing = false;
              mCamera = null;
          }
      }
  
      public void onPreviewFrame(byte[] data, Camera camera) {
          Camera.Parameters parameters = camera.getParameters();
          Camera.Size size = parameters.getPreviewSize();
  
          Image barcode = new Image(size.width, size.height, "Y800");
          barcode.setData(data);
  
          int result = mScanner.scanImage(barcode);
  
          if (result != 0) {
              mCamera.cancelAutoFocus();
              mCamera.setPreviewCallback(null);
              mCamera.stopPreview();
              mPreviewing = false;
              SymbolSet syms = mScanner.getResults();
              for (Symbol sym : syms) {
                  String symData = sym.getData();
                  if (!TextUtils.isEmpty(symData)) {
                      Intent dataIntent = new Intent();
                      dataIntent.putExtra(SCAN_RESULT, symData);
                      dataIntent.putExtra(SCAN_RESULT_TYPE, sym.getType());
                      setResult(Activity.RESULT_OK, dataIntent);
                      finish();
                      break;
                  }
              }
          }
      }
      private Runnable doAutoFocus = new Runnable() {
          public void run() {
              if(mCamera != null &amp;&amp; mPreviewing) {
                  mCamera.autoFocus(autoFocusCB);
              }
          }
      };
  
      // Mimic continuous auto-focusing
      Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
          public void onAutoFocus(boolean success, Camera camera) {
              mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
          }
      };
  
      public void launchQRScanner(View v) {
          if (isCameraAvailable()) {
  
              //Toast.makeText(this, "Analyzing Image", Toast.LENGTH_LONG).show();
  
              Intent intent = new Intent(this, ZBarScannerActivity.class);
              intent.putExtra(ZBarConstants.SCAN_MODES, new int[]{Symbol.QRCODE});
              startActivityForResult(intent, ZBAR_QR_SCANNER_REQUEST);
          } else {
              Toast.makeText(this, "Camera Unavailable", Toast.LENGTH_SHORT).show();
          }
      }
  
      public boolean isCameraAvailable() {
          PackageManager pm = getPackageManager();
          return pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);
      }
  
      @Override
      public boolean onCreateOptionsMenu(Menu menu) {
          // Inflate the menu; this adds items to the action bar if it is present.
          //getMenuInflater().inflate(R.menu.activity_qrimport_token, menu);
          return true;
      }
  
     /* @Override
      protected void onActivityResult(int requestCode, int resultCode, Intent data)
      {
          switch (requestCode) {
              case ZBAR_SCANNER_REQUEST:
                  Toast.makeText(this, data.getStringExtra(ZBarConstants.SCAN_RESULT), Toast.LENGTH_LONG).show();
                  break;
              case ZBAR_QR_SCANNER_REQUEST:
  
                  playSound();
  
                  if (resultCode == RESULT_OK) {
                      Toast.makeText(this, data.getStringExtra(ZBarConstants.SCAN_RESULT), Toast.LENGTH_LONG).show();
                      Toast.makeText(this, "Scan Result = " + data.getStringExtra(ZBarConstants.SCAN_RESULT), Toast.LENGTH_SHORT).show();
  
                      // put whatever you want to do with the code here
                      //TextView tv = new TextView(this);
                      //tv.setText(data.getStringExtra(ZBarConstants.SCAN_RESULT));
                      Log.d("Data",""+data.getStringExtra(ZBarConstants.SCAN_RESULT));
                      //setContentView(tv);
                  }
                  break;
          }
      }*/
  
      /*private void playSound() 
      {
          MediaPlayer mp = MediaPlayer.create(getBaseContext(), R.raw.camera_shutter);  
          mp.start();             
  
          mp.setOnCompletionListener(new OnCompletionListener()
          {                    
              @Override                 
              public void onCompletion(MediaPlayer mp) 
              {                       
                  mp.release();       
              }              
          }); 
  
      }*/
  }
  </code></pre>
  
  <p>CameraPreview.java:</p>
  
  <pre><code>package com.myCompany.android;
  
  import android.content.Context;
  import android.hardware.Camera;
  import android.hardware.Camera.AutoFocusCallback;
  import android.hardware.Camera.PreviewCallback;
  import android.hardware.Camera.Size;
  import android.util.Log;
  import android.view.SurfaceHolder;
  import android.view.SurfaceView;
  import android.view.View;
  import android.view.ViewGroup;
  import java.io.IOException;
  import java.util.List;
  
  class CameraPreview extends ViewGroup implements SurfaceHolder.Callback {
      private final String TAG = "CameraPreview";
  
      SurfaceView mSurfaceView;
      SurfaceHolder mHolder;
      Size mPreviewSize;
      List&lt;Size&gt; mSupportedPreviewSizes;
      Camera mCamera;
      PreviewCallback mPreviewCallback;
      AutoFocusCallback mAutoFocusCallback;
  
      CameraPreview(Context context, PreviewCallback previewCallback, AutoFocusCallback autoFocusCb) {
          super(context);
  
          mPreviewCallback = previewCallback;
          mAutoFocusCallback = autoFocusCb;
          mSurfaceView = new SurfaceView(context);
          addView(mSurfaceView);
  
          // Install a SurfaceHolder.Callback so we get notified when the
          // underlying surface is created and destroyed.
          mHolder = mSurfaceView.getHolder();
          mHolder.addCallback(this);
          mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
      }
  
      public void setCamera(Camera camera) {
          mCamera = camera;
          if (mCamera != null) {
              mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
              requestLayout();
          }
      }
  
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          // We purposely disregard child measurements because act as a
          // wrapper to a SurfaceView that centers the camera preview instead
          // of stretching it.
          final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
          final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
          setMeasuredDimension(width, height);
  
          if (mSupportedPreviewSizes != null) {
              mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
          }
      }
  
      @Override
      protected void onLayout(boolean changed, int l, int t, int r, int b) {
          if (changed &amp;&amp; getChildCount() &gt; 0) {
              final View child = getChildAt(0);
  
              final int width = r - l;
              final int height = b - t;
  
              int previewWidth = width;
              int previewHeight = height;
              if (mPreviewSize != null) {
                  previewWidth = mPreviewSize.width;
                  previewHeight = mPreviewSize.height;
              }
  
              // Center the child SurfaceView within the parent.
              if (width * previewHeight &gt; height * previewWidth) {
                  final int scaledChildWidth = previewWidth * height / previewHeight;
                  child.layout((width - scaledChildWidth) / 2, 0,
                          (width + scaledChildWidth) / 2, height);
              } else {
                  final int scaledChildHeight = previewHeight * width / previewWidth;
                  child.layout(0, (height - scaledChildHeight) / 2,
                          width, (height + scaledChildHeight) / 2);
              }
          }
      }
  
      public void surfaceCreated(SurfaceHolder holder) {
          // The Surface has been created, acquire the camera and tell it where
          // to draw.
          try {
              if (mCamera != null) {
                  mCamera.setPreviewDisplay(holder);
              }
          } catch (IOException exception) {
              Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
          }
      }
  
      public void surfaceDestroyed(SurfaceHolder holder) {
          // Surface will be destroyed when we return, so stop the preview.
          if (mCamera != null) {
              mCamera.cancelAutoFocus();
              mCamera.stopPreview();
          }
      }
  
  
      private Size getOptimalPreviewSize(List&lt;Size&gt; sizes, int w, int h) {
          final double ASPECT_TOLERANCE = 0.1;
          double targetRatio = (double) w / h;
          if (sizes == null) return null;
  
          Size optimalSize = null;
          double minDiff = Double.MAX_VALUE;
  
          int targetHeight = h;
  
          // Try to find an size match aspect ratio and size
          for (Size size : sizes) {
              double ratio = (double) size.width / size.height;
              if (Math.abs(ratio - targetRatio) &gt; ASPECT_TOLERANCE) continue;
              if (Math.abs(size.height - targetHeight) &lt; minDiff) {
                  optimalSize = size;
                  minDiff = Math.abs(size.height - targetHeight);
              }
          }
  
          // Cannot find the one match the aspect ratio, ignore the requirement
          if (optimalSize == null) {
              minDiff = Double.MAX_VALUE;
              for (Size size : sizes) {
                  if (Math.abs(size.height - targetHeight) &lt; minDiff) {
                      optimalSize = size;
                      minDiff = Math.abs(size.height - targetHeight);
                  }
              }
          }
          return optimalSize;
      }
  
      public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
          if (holder.getSurface() == null){
            // preview surface does not exist
            return;
          }
  
          // Now that the size is known, set up the camera parameters and begin
          // the preview.
          Camera.Parameters parameters = mCamera.getParameters();
          parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
          requestLayout();
  
          mCamera.setParameters(parameters);
          mCamera.setPreviewCallback(mPreviewCallback);
          mCamera.startPreview();
          mCamera.autoFocus(mAutoFocusCallback);
      }
  
  }
  </code></pre>
  
  <p>
  How to customize the camera view for android? I saw some suggested solutions for iPhone, but zbar API for Android is totally different and much smaller. I would be appreciated to get any idea/hint...</p>