B4A Library Camera library v2.20

Erel

Administrator
Staff member
Licensed User
The camera library was updated.
It now also supports the front camera.

There were several internal improvements required for the CameraEx class.

Installation instructions:
- Unzip the attached file and copy both files to the internal libraries folder.

Edit:
V2.20 is attached. Adds Camera.AutoFocus and Camera.CancelAutoFocus methods. CameraEx class was also updated and it includes a new FocusAndTakePicture method.
V2.10 is attached. The camera management code was rewritten to fix stability issues.
 

Attachments

Roger Garstang

Well-Known Member
Licensed User
The camera library supports Android 1.6 and above. Many of the methods in this class (CameraEX) depend on Android 2.3. This is why they cannot be merged into the Camera library. It is true that I can create a new Camera2 library however there are methods that are only available in Android 4 and above. So quickly we will need Camera3 library.
I see now. Lately with all the issues with older versions then Honeycomb variations problems I restrict most of my apps to a minimum 2.3.3 (API 10), and tend to ignore Homeycomb and jump right to 4+ so a minimum 2.3 wouldn't be an issue with a Class for the 4+ stuff. Will the source code for this be released like it was for Camera1?
 

Erel

Administrator
Staff member
Licensed User
Here is the code:
B4X:
@ActivityObject
@Permissions(values={"android.permission.CAMERA"})
@ShortName("Camera")
@Version(2.01f)
@Events(values={"Ready (Success As Boolean)", "PictureTaken (Data() As Byte)",
      "Preview (Data() As Byte)"})
public class CameraW {
   @Hide
   public Camera camera;
   private static ConcurrentHashMap<Integer, Camera> closingCameras = new ConcurrentHashMap<Integer, Camera>();
   private SurfaceView sv;
   private String eventName;
   private BA ba;
   private int currentId;
   private AtomicInteger readyCount = new AtomicInteger();
   /**
    * Initializes the back camera.
    *Panel - The preview images will be displayed on the panel.
    *EventName - Events subs prefix.
    *The Ready event will be raised when the camera has finished opening.
    */
   public void Initialize(final BA ba, ViewGroup Panel, String EventName) {
      shared(ba, Panel, EventName, -1);
   }
   /**
    * Same as Initialize. CameraId is the id of the hardware camera.
    *<b>This method is only available from Android 2.3+.</b>
    */
   public void Initialize2(final BA ba, ViewGroup Panel, String EventName, int CameraId) {
      shared(ba, Panel, EventName, CameraId);
   }
   private void shared(final BA ba, ViewGroup Panel, String EventName, final int CameraId) {
      this.ba = ba;
      currentId = CameraId;
      readyCount.set(0);
      this.eventName = EventName.toLowerCase(BA.cul);
      sv = new SurfaceView(ba.context);
      anywheresoftware.b4a.BALayout.LayoutParams lp = new anywheresoftware.b4a.BALayout.LayoutParams(0, 0,
            Panel.getLayoutParams().width, Panel.getLayoutParams().height);
      Panel.addView(sv, lp);
      camera = closingCameras.get(CameraId);
      closingCameras.remove(CameraId);
      if (camera == null) {
         //release other cached cameras (relevant when switching cameras)
         synchronized (closingCameras) { //make sure that release is only called once.
            for (Camera c : closingCameras.values()) {
               c.release();
            }
            closingCameras.clear();
         }
      }
      
      sv.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
      sv.getHolder().setFixedSize(Panel.getLayoutParams().width, Panel.getLayoutParams().height);
      sv.getHolder().addCallback(new SurfaceHolder.Callback() {

         @Override
         public void surfaceChanged(SurfaceHolder holder, int format,
               int width, int height) {
         }

         @Override
         public void surfaceCreated(SurfaceHolder holder) {
            if (readyCount.addAndGet(1) == 2) {
               ba.raiseEvent(null, eventName + "_ready", true);
            }
            
         }

         @Override
         public void surfaceDestroyed(SurfaceHolder holder) {

         }

      });
      ba.submitRunnable(new Runnable() {

         @Override
         public void run() {
            try {
               if (camera == null) {
                  if (CameraId == -1)
                     camera = Camera.open();
                  else {
                     Method m = Camera.class.getMethod("open", int.class);
                     camera = (Camera)m.invoke(null, CameraId);
                  }
               }
               if (readyCount.addAndGet(1) == 2) {
                  ba.raiseEventFromDifferentThread(null, CameraW.this, -1,eventName +  "_ready", false, new Object[] {true});
               }
               if (ba.subExists(eventName + "_preview")) {
                  camera.setPreviewCallback(new Camera.PreviewCallback() {

                     @Override
                     public void onPreviewFrame(byte[] data,
                           Camera camera) {
                        ba.raiseEvent(null, eventName + "_preview", data);
                     }
                     
                  });
               }
            }
            catch (Exception e) {
               e.printStackTrace();
               Release();
               ba.raiseEventFromDifferentThread(null, CameraW.this, -1,eventName +  "_ready", false, new Object[] {false});
            }
         }

      }, this, -1);
   }
   /**
    * Starts displaying the preview images.
    */
   public void StartPreview() throws IOException {
      camera.setPreviewDisplay(sv.getHolder());
      camera.startPreview();
   }
   /**
    * Stops displaying the preview images.
    */
   public void StopPreview() {
      if (camera != null)
         camera.stopPreview();
   }
   /**
    * Releases the camera object and allows other processes to access the camera.
    */
   public void Release() {
if (camera != null) {
         camera.setPreviewCallback(null);
         camera.stopPreview();
         closingCameras.put(currentId, camera);
         final Camera c = camera;
         camera = null;
         Thread t = new Thread(new Runnable() {

            @Override
            public void run() {
               try {
                  Thread.sleep(5000);
                  synchronized (closingCameras) {
                     if (closingCameras.remove(currentId) != null) {
                        c.release();
                     }
                  }
               } catch (Exception e) {
                  e.printStackTrace();
               }
            }
            
         });
         t.start();
         
      }
      if (sv != null) {
         ViewGroup vg = (ViewGroup) sv.getParent();
if (vg != null)
         vg.removeView(sv);
         sv = null;
      }
      
   }
   /**
    * Takes a picture. When the picture is ready, the PictureTaken event will be raised.
    *You should not call TakePicture while another picture is currently taken.
    *The preview images are stopped after calling this method. You can call StartPreview to restart the preview images.
    */
   public void TakePicture() {
      camera.takePicture(null , null, new Camera.PictureCallback() {

         @Override
         public void onPictureTaken(byte[] data, Camera camera) {
            ba.raiseEvent(null, eventName + "_picturetaken", data);
         }
         
      });
   }

}
As I previously wrote I think that it will be simpler to extend or modify CameraEx class.
 

Roger Garstang

Well-Known Member
Licensed User
Cool, thanks for the code too. Always good to visualize what is being done and the more advanced folks can add the class stuff in a library too.

I guess I wasn't paying attention yesterday and now see that the Initialize2 function states it requires 2.3+, and you had said earlier it is 1.6+. Both Initializes call the same function, so is Camera.class.getMethod call in the Runnable the only place requiring 2.3? Might be good just to do a android.os.Build.VERSION.SDK_INT check (Also available since 1.6) there and if not available do camera = Camera.open();. Should eliminate any errors thrown and the same could be done for any other 2.3+ stuff.

If you still don't plan on integrating the class stuff I'll probably do something after I get my Relative Layout Library done. Main things for me was image orientation matching and capturing GPS both in code with existing GPS library and in Camera EXIF for my Evidence Collection app. Anything else I'll probably leave to the class.

Also, what changed in 2.01?
 
Last edited:

bluedude

Well-Known Member
Licensed User
Roger,

API 10 seems to be a save option as a minimum. Checked some app. stats and biggest chunk of OS is Android 2.3.3 - 2.3.7

Anyone more stats? Would like to know if 2.3 still exists :)
 

dunski

Member
Licensed User
Cannot see initialize2

Hi this updated library looks great the only thing is I cant see initialize2.

I removed the old library and added the new one but It doesn't seem to have initialize2

Please help.
 

ziomorgan

Member
Licensed User
Hi,
I have too the same problem even after copying the library in the internal library folder (Unknown member: initialize2). The IDE show always the same library version 1.10.
Thanks
 

dunski

Member
Licensed User
Same here

Same here

The old version is by default in the libraries directory which is not in configure paths c:/program files/basic4android/libraries

It seems to come installed there with B4A installation.

I was adding the new library to my addLibraries folder which is the one in my configured paths for additional libraries which is what I normally do without a problem.

So I deleted the one in the default library like Ziomorgan

A well its all sorted now anyway.

Thanks for the help Erel and Ziomorgan
 

bluedude

Well-Known Member
Licensed User
Zoom in preview

Erel,

My eyes are getting bad and I have an idea to use my phone when I forget my glasses. Can I use the zoom in preview mode?

I want to create a small app. that increases the size of the stuff that I need to read, my phone would be perfect for that.

Cheers,
 
Top