Java Question How to call a constructor in b4a

HotShoe

Well-Known Member
Licensed User
Longtime User
I have written a library that implements the Android FileObserver. The Class is named MLObserver and it extends FileObserver. The constructors name is also MLOBserver as it must be, and has 2 parameters.

How do I call this constructor from b4a? When the class is initialized, the constructor is called with no parameters, so an error is thrown during compile in b4a. There must be a way around this, but I don't know that trick.

Thanks,

--- Jem
 

warwound

Expert
Licensed User
Longtime User
You can wrap your MLObserver class in a B4A AbsObjectWrapper:

B4X:
import anywheresoftware.b4a.AbsObjectWrapper;
import anywheresoftware.b4a.BA.Author;
import anywheresoftware.b4a.BA.ShortName;
import anywheresoftware.b4a.BA.Version;

@Author("Your name")
@ShortName("MLObserver")
@Version(1.00f)

public class MLObserverWrapper extends AbsObjectWrapper<MLObserver> {
   /**
    * Initialize the MLObserver object
    */
   public void Initialize(String Path, int Mask){
      //   setObject() creates an instance of MLObserver and assigns it as the wrapped object
      setObject(new MLObserver(Path, Mask));
   }

   public void DoSomething(){
      //   to get the wrapped MLObserver object use getObject()
      getObject().doSomething();
   }
   
}

When an instance of MLObserverWrapper (which will be named "MLObserver" in the IDE) is Dimmed the wrapper instance is constructed.

Then when Initialize is called the actual instance of your MLObserver class is constructed.

B4X:
Dim MyObserver As MLObserver ' AbsObjectWrapper constructor is called

MyObserver.Initialize("a path", anInt) ' MLObserver constructor is called

Within the MLObserverWrapper class you use getObject() to get the wrapped instance of MLObserver.

Martin.
 
Last edited:

HotShoe

Well-Known Member
Licensed User
Longtime User
Okay, this sounds promising. I have done that, but eclipse doesn't know about setObject or getObject. Is there something I need to import? I have:

import android.os.FileObserver;
import anywheresoftware.b4a.AbsObjectWrapper;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.BA.Author;
import anywheresoftware.b4a.BA.Events;
import anywheresoftware.b4a.BA.Hide;
import anywheresoftware.b4a.BA.Permissions;
import anywheresoftware.b4a.BA.ShortName;
import anywheresoftware.b4a.BA.Version;

Thanks,

--- Jem
 

warwound

Expert
Licensed User
Longtime User
Within the MLObserverWrapper class Eclipse should recognise getObject() and setObject() as methods inherited from the AbsObjectWrapper class...

You are wrapping your MLObserver class and not trying to wrap the abstract FileObserver class are you?
I see no imports in your post for MLObserver but do see an import for the abstract FileObserver class.

Martin.
 

HotShoe

Well-Known Member
Licensed User
Longtime User
OK, I'm lost. It looks like a circular class to me. How does this wrapped class know about the FileObserver object and how to use it's methods? It's initializing an empty class isn't it?

--- Jem
 

warwound

Expert
Licensed User
Longtime User
I have written a library that implements the Android FileObserver. The Class is named MLObserver and it extends FileObserver.

You said you have implemented the abstract FileObserver class as MLObserver.
You can wrap your MLObserver class in a B4A AbsObjectWrapper.

You cannot wrap the abstract FileObserver class in aa AbsObjectWrapper, you must create your own implementation of the abstract FileObserver class and wrap that implementation.

Martin.
 

HotShoe

Well-Known Member
Licensed User
Longtime User
Can you guide me to an example of what you are saying? I am trying my best to get my head around java and interfacing with b4a, but I do not really understand what you are saying here.

I need 2 classes or 2 libraries? Can it all be done in 1 library file or do I need to implicitly import the wrapped library when it is completed? I still do not understand how the MLObserver class will know about the FileObserver without any mention of FileObserver in the MLObserver class.

Thanks,

--- Jem
 

warwound

Expert
Licensed User
Longtime User
Hi again.

I got a bit carried away writing some example code for you and have a working FileObserver library waiting for some tests before i upload it!!!

Here's what i did...

First implemented the native Android abstract FileObserver class as my own B4AFileObserver class:

B4X:
package uk.co.martinpearman.b4a.fileobserver;

import android.os.FileObserver;
import android.util.Log;
import anywheresoftware.b4a.BA;

public class B4AFileObserver extends FileObserver {
   //   keep a reference to the parent wrapper instance so it can be used in raiseEventFromDifferentThread
   //   the parent wrapper instance will be the B4A Sub's Sender object
   final private B4AFileObserverWrapper mB4AFileObserverWrapper;
   final private BA mBA;
   final private String mEventName;

   //   these 2 constructors allow the parent wrapper instance to pass a reference to itself and the BA object to this class
   public B4AFileObserver(BA pBA, B4AFileObserverWrapper pB4AFileObserverWrapper, String pEventName, String pPath) {
      super(pPath);
      mB4AFileObserverWrapper = pB4AFileObserverWrapper;
      mBA = pBA;
      mEventName = pEventName;
   }

   public B4AFileObserver(BA pBA, B4AFileObserverWrapper pB4AFileObserverWrapper, String pEventName, String pPath, int pMask) {
      super(pPath, pMask);
      mB4AFileObserverWrapper = pB4AFileObserverWrapper;
      mBA = pBA;
      mEventName = pEventName;
   }

   @Override
   public void onEvent(int pEventType, String pPath) {
      //   eventTypes are all the possible EventTypes
      int[] eventTypes = { FileObserver.ACCESS, FileObserver.ATTRIB, FileObserver.CLOSE_NOWRITE, FileObserver.CLOSE_WRITE, FileObserver.CREATE,
            FileObserver.DELETE, FileObserver.DELETE_SELF, FileObserver.MODIFY, FileObserver.MOVE_SELF, FileObserver.MOVED_FROM, FileObserver.MOVED_TO,
            FileObserver.OPEN };
      //   use -1 as a flag so an unrecognised pEventType can be detected
      int thisEventType = -1;
      int i = eventTypes.length;
      while (i-- > 0) {
         if ((eventTypes[i] & pEventType) != 0) {
            thisEventType = eventTypes[i];
            break;
         }
      }

      if (thisEventType != -1) {
         Log.d("B4A", "Raising event '"+mEventName+"', eventType="+thisEventType+", path="+pPath);
         final Object[] eventParameters = { thisEventType, pPath };
         mBA.raiseEventFromDifferentThread(mB4AFileObserverWrapper, null, 0, mEventName, false, eventParameters);

      } else {
         //   this condition should never be met but...!
         Log.i("B4A", "FileObserver EventType not recognised, EventType=" + pEventType+", Path="+pPath);
      }
   }
}

The B4AFileObserver class is more or less a native Android class but not useable in a B4A project so i wrapped it using the B4A AbsObjectWrapper class and created B4AFileObserverWrapper class:

B4X:
package uk.co.martinpearman.b4a.fileobserver;

import android.os.FileObserver;
import android.util.Log;
import anywheresoftware.b4a.AbsObjectWrapper;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.BA.Author;
import anywheresoftware.b4a.BA.Events;
import anywheresoftware.b4a.BA.ShortName;
import anywheresoftware.b4a.BA.Version;

@Author("Martin Pearman")
@Events(values = { "OnEvent (EventType As Int, Path As String)" })
@ShortName("FileObserver")
@Version(1.00f)
public class B4AFileObserverWrapper extends AbsObjectWrapper<B4AFileObserver> {
   /**
    * EventType: Data was read from a file .
    */
   public static final int ACCESS = FileObserver.ACCESS;
   /**
    * Event mask: All valid EventTypes, combined .
    */
   public static final int ALL_EVENTS = FileObserver.ALL_EVENTS;
   /**
    * EventType: Metadata (permissions, owner, timestamp) was changed explicitly .
    */
   public static final int ATTRIB = FileObserver.ATTRIB;
   /**
    * EventType: Someone had a file or directory open read-only, and closed it.
    */
   public static final int CLOSE_NOWRITE = FileObserver.CLOSE_NOWRITE;
   /**
    * EventType: Someone had a file or directory open for writing, and closed it .
    */
   public static final int CLOSE_WRITE = FileObserver.CLOSE_WRITE;
   /**
    * EventType: A new file or subdirectory was created under the monitored directory.
    */
   public static final int CREATE = FileObserver.CREATE;
   /**
    * EventType: A file was deleted from the monitored directory.
    */
   public static final int DELETE = FileObserver.DELETE;
   /**
    * EventType: The monitored file or directory was deleted; monitoring effectively stops.
    */
   public static final int DELETE_SELF = FileObserver.DELETE_SELF;
   /**
    * EventType: Data was written to a file .
    */
   public static final int MODIFY = FileObserver.MODIFY;
   /**
    * EventType: The monitored file or directory was moved; monitoring continues.
    */
   public static final int MOVE_SELF = FileObserver.MOVE_SELF;
   /**
    * EventType: A file or subdirectory was moved from the monitored directory.
    */
   public static final int MOVED_FROM = FileObserver.MOVED_FROM;
   /**
    * EventType: A file or subdirectory was moved to the monitored directory.
    */
   public static final int MOVED_TO = FileObserver.MOVED_TO;
   /**
    * EventType: A file or directory was opened.
    */
   public static final int OPEN = FileObserver.OPEN;

   /**
    * Returns the String identifier of an EventType.
    */
   public static final String EventTypeToString(int EventType) {
      switch (EventType) {
      case FileObserver.ACCESS:
         return "ACCESS";
      case FileObserver.MODIFY:
         return "MODIFY";
      case FileObserver.ATTRIB:
         return "ATTRIB";
      case FileObserver.CLOSE_WRITE:
         return "CLOSE_WRITE";
      case FileObserver.CLOSE_NOWRITE:
         return "CLOSE_NOWRITE";
      case FileObserver.OPEN:
         return "OPEN";
      case FileObserver.MOVED_FROM:
         return "MOVED_FROM";
      case FileObserver.MOVED_TO:
         return "MOVED_TO";
      case FileObserver.CREATE:
         return "CREATE";
      case FileObserver.DELETE:
         return "DELETE";
      case FileObserver.DELETE_SELF:
         return "DELETE_SELF";
      case FileObserver.MOVE_SELF:
         return "MOVE_SELF";
      default:
         return "UNKNOWN";
      }
   }

   /**
    * Initialize the FileObserver with an EventName and Path.
    *Equivalent to Initialize2(EventName, Path, FileObserver.ALL_EVENTS).
    */
   public void Initialize(BA pBA, String EventName, String Path) {
      if (pBA.subExists(EventName.toLowerCase() + "_onevent")) {
         setObject(new B4AFileObserver(pBA, this, EventName.toLowerCase() + "_onevent", Path));
      } else {
         Log.e("B4A", "FileObserver initialization error:");
         Log.e("B4A", "Sub " + EventName + "_OnEvent is undefined");
      }
   }

   /**
    * Initialize the FileObserver with an EventName, Path and EventMask.
    */
   public void Initialize2(BA pBA, String EventName, String Path, int EventMask) {
      if (pBA.subExists(EventName.toLowerCase() + "_onevent")) {
         setObject(new B4AFileObserver(pBA, this, EventName.toLowerCase() + "_onevent", Path, EventMask));
      } else {
         Log.e("B4A", "FileObserver initialization error:");
         Log.e("B4A", "Sub " + EventName + "_OnEvent is undefined");
      }
   }

   /**
    * Start watching for events.
    */
   public void StartWatching() {
      getObject().startWatching();
   }

   /**
    * Stop watching for events.
    */
   public void StopWatching() {
      getObject().stopWatching();
   }
}

You can see that the core of the functionality is in the B4AFileObserver class and that the B4AFileObserverWrapper class is just that - a wrapper that enables a native Android class to be used within B4A.

Testing the library using an Activity is tricky, i created this Activity:
(I needed to trigger an event without stopping the FileObserver).

B4X:
'   FileObserverDemo Activity module
Sub Process_Globals
End Sub

Sub Globals
   Dim FileObserver1 As FileObserver
End Sub

Sub Activity_Create(FirstTime As Boolean)

   FileObserver1.Initialize("FileObserver1", File.DirRootExternal&"/media")
   
   '   example of Initialize2 syntax
   '   FileObserver1.Initialize2("FileObserver1", File.DirRootExternal&"/media", FileObserver1.OPEN)
   
   FileObserver1.StartWatching
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
   '   FileObserver1.StopWatching
End Sub

Sub FileObserver1_OnEvent (EventType As Int, Path As String)

   Log("EventType="&FileObserver1.EventTypeToString(EventType)&", Path="&Path)
   
   Select EventType
      Case FileObserver1.ACCESS
         '   handle event
      Case FileObserver1.ATTRIB
         '   handle event
      Case FileObserver1.CLOSE_NOWRITE
         '   handle event
      Case FileObserver1.CLOSE_WRITE
         '   handle event
      Case FileObserver1.CREATE
         '   handle event
      Case FileObserver1.DELETE
         '   handle event
      Case FileObserver1.DELETE_SELF
         '   handle event
      Case FileObserver1.MODIFY
         '   handle event
      Case FileObserver1.MOVE_SELF
         '   handle event
      Case FileObserver1.MOVED_FROM
         '   handle event
      Case FileObserver1.MOVED_TO
         '   handle event
      Case FileObserver1.OPEN
         '   handle event
   End Select
End Sub

I can run that on my ZTE Blade, press the Home key and start up a file explorer program.
If i go to the media folder on my SD card and watch the log i see the library is working BUT the events are not generated until the B4A Activity above is resumed.
Returning to the B4A Activity the events are then passed to it and it's FileObserver1_OnEvent Sub is executed.

I shall update the wrapper with some more javadoc comments and upload the library to the libraries forum later.

The library jar and xml are attached for you to try now though, and if you'd like a copy of the eclipse project to play with then let me know.

Martin.

(I'll be removing the attachment from this thread once i have uploaded the library to the proper thread).

[edit]Attachment now removed[/edit]
 
Last edited:

HotShoe

Well-Known Member
Licensed User
Longtime User
This is outstanding. Now I get what you were talking about earlier. I was trying to do it in one file instead of one package with 2 .jar files. I'm glad we have a file observer now, so i will move on to something else.

Thanks again for helping to clear my mind. I'm used to writing C++ libs for linux and android. this is a different world.

--- Jem
 
Top