Java Question Interact with views from service

XverhelstX

Well-Known Member
Licensed User
Longtime User
Hello,

After a long time, I managed to port the StandOut library to Basic4Android.
StandOut is a library which you can use to make floating like apps.

StandOut - Create Floating Android Apps - YouTube

When calling a StandOut window, it creates a service which shows the window.

Now the problem is how I can interact with the views from Basic4Android.

In Basic4Android, I start a WindowService which takes care of the StandOut library. (Because it will be useless if you use StandOut with an Activity on it.)

1. I need to find a way on how to interact with all events from the views, for example button clicks, etc from a B4A service.
So some kind of eventlistener that listens on the service when a button is pressed.

2. Also, i found a way on how to add views using CallSubDelayed, but i don't know if this is the correct way.
Isn't there an easier way i can add views to the window from a service? (because most of them result in a nullpointer. Probably because of the context?)

3. Is it possible to create a view programmatically in a class, pass it to the service and the service adds the view to the StandOut Window? So the class has it's own activity context, so actually the events of the window will be called to the class module?

Any possible ideas to help me are welcome

Kind regards,
Tomas
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
As a general rule services should not interact directly with views. It is also problematic to create views from a service as the service doesn't have a reference to an activity context.
Views hold a reference to the activity context and this may lead to a memory leak when Android tries to destroy the activity.

It is technically possible to create a set of views and delegate the events to a non-activity object that the service holds.
 

XverhelstX

Well-Known Member
Licensed User
Longtime User
Erel, I would really appreciate if you could help me out on this one.
These last months I have been trying to port new libraries (AndEngine, Samsung S Pen SDK, StandOut, OpenCV) to Basic4Android, but due the lack of knowledge and resources, I haven't been able to port a single one. Now I have finally managed to port StandOut, and the only thing that is avoiding me is finding a good way to interact with views.

So I really wish you could help me solve this problem. Even if you write a code example, it might help future library developers who are facing the same issue like i do.

Tomas
 

warwound

Expert
Licensed User
Longtime User
Heh Tomas.

I mentioned to you last week that the way that B4A interacts with widgets might point you in the right direction.
I still think that could be true.

The concept is similar:
  • You want B4A to create Views that are not part of a B4A Activity.
  • You want to interact with those Views.

http://www.b4x.com/forum/basic4andr...roid-home-screen-widgets-tutorial-part-i.html

It is important to understand that the widgets are created and managed in another process, different than the process that your application is running in. The home screen application is hosting your widgets.
This means that it is not possible to directly access the widgets views. Instead we are using a special object named RemoteViews which gives us indirect access to the widget views.

That sounds very much like the problem that you are searching for a solution to.

Maybe you should look at the Android RemoteView class?
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
You can do something like:

1. The user will pass a panel with the views to your library.
2. You will add a transparent panel over the views.
3. In your code you will handle the panel touch events and delegate them to an object with a static reference to BA.
4. The user will add the object to a service and then will be able to handle its events.

This is more or less how LiveWallpaper works (LiveWallpaper is actually more complicated).
 

XverhelstX

Well-Known Member
Licensed User
Longtime User
Thanks a lot for this information. I still have a couple of questions:

2. You will add a transparent panel over the views.

I'll have to make a panel for each view, correct? So not one big panel over all the views?
So i'd need a ba instance if i am going to make a panel
B4X:
PanelWrapper pnl = new PanelWrapper();
pnl.Initialize(ba, "eventname")

So ba would be an activity context again, which will be destroyed again?,

Will i also be able to handle other events like Webview_pagefinished, etc (other than click/touch events)?

Could you also elaborate 3 and 4 a little more?
How will the user be able to handle the events from an object?
 
Last edited:

XverhelstX

Well-Known Member
Licensed User
Longtime User
Actually i managed to find a way to do it! I don't know if it's the best way, but it seems to work. (without needing an activity in the background!)

Let me elaborate:

Whenever i add a panel to the window, it will grab all it's views of it and create a ViewEventListener for the view. The ViewEventListener will handle the events.

B4X:
/**
    * Adds a view to the window.
    */
   public void AddPanel(PanelWrapper child, int Left, int Top, int Width, int Height) {
      
      //Gets the eventname of the view.
      String s = (String) child.getTag();
      String[] fields = s.split(";");
   
      anywheresoftware.b4a.BALayout.LayoutParams lp = new anywheresoftware.b4a.BALayout.LayoutParams(Left, Top, Width, Height);
      this.window.addView(child.getObject(), lp);

      for (int i=0;i<child.getNumberOfViews();i++) {
         //Create a new view based on the child.
         s = (String) child.GetView(i).getTag();
         fields = s.split(";");
      
         ViewEventListener vel = new ViewEventListener();
         vel.Initialize(this.ba, fields[2], child.GetView(i).getObject());
      }
      
      s = (String) child.getTag();
      fields = s.split(";");
      
      ViewEventListener vel = new ViewEventListener();
      vel.Initialize(this.ba, fields[2], child.getObject());
   }

B4X:
package com.rootsoft.standout;

import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.View.OnTouchListener;
import anywheresoftware.b4a.BA;


public class ViewEventListener{
   
   private BA ba; 
   private String eventName;
   private View view;
   
   /**
    * Initializes listener for the view.
    * EventName - Events subs prefix. 
    */
   public void Initialize(final BA ba, String EventName, View v) {
      this.ba = ba;
      this.eventName = EventName.toLowerCase(BA.cul);
      
      //Create a new view based on the child.
      String s = (String) v.getTag();
      String[] fields = s.split(";");
      
      view = v;
      view.setOnClickListener(onClickListener);
      view.setOnLongClickListener(onLongClickListener);
      view.setOnTouchListener(onTouchListener);
   }
   
   OnClickListener onClickListener = new OnClickListener() {

      @Override
      public void onClick(View arg0) {
         // TODO Auto-generated method stub
         Log.i("B4A", "CLICKCKCK: " + eventName);
         if (ba.subExists(eventName + "_click") == true) {
            ba.raiseEvent(this, eventName + "_click", new Object[] { arg0 } );
         }
      }
      
   };
   
   OnLongClickListener onLongClickListener = new OnLongClickListener() {

      @Override
      public boolean onLongClick(View arg0) {
         Log.i("B4A", "Long CLICKCKCK");
         // TODO Auto-generated method stub
         if (ba.subExists(eventName + "_longclick") == true) {
            ba.raiseEvent(this, eventName + "_longclick", new Object[] { arg0 } );
         }
         return false;
      }   
   };
   
   OnTouchListener onTouchListener = new OnTouchListener() {

      @Override
      public boolean onTouch(View arg0, MotionEvent arg1) {
         // TODO Auto-generated method stub
         if (ba.subExists(eventName + "_touch") == true) {
            ba.raiseEvent(this, eventName + "_touch", new Object[] { arg0, arg1.getAction(), arg1.getX(), arg1.getY() } );
         }
         return false;
      }

   
   };
   

}

And it works! :D
The only thing left is positioning on the framelayout which i cannot seem to find because it keeps staying on the topleft corner.

http://www.b4x.com/forum/libraries-developers-questions/20802-position-view-framelayout.html

Tomas
 
Top