B4A Library ChatHead wrapper + GIF overlay

(2017/07/01)

Based on this wrapper, I also built some strange things on it, with different purposes. I am not working on most of them anymore, so I decided to share.

This one is funny. Similar to the video but with added elements
It combines several overlay elements and brings them into life simulating GIF animation (based on some previously converted GIF files to PNG) and sequencing them with a timer. In this version there are some xmas stars, a Santa and a spider skull

I publish it here since the thread already exists and is related to it... As always, perhaps the code style is not the best in the world to learn from, but I have tried to comment a bit and delete unneeded things so that it will be more easy to follow.

Important: in most recent android versions, you must grant directly to the app the permission to show elements on top of others (it is not enough with the declared manifest permissions). So, if nothing appears on your screen you should check it.

Hope you enjoy it :)
-----------------------------------

I'm happy to share this chatHead wrapper made with B4A and Inline Java. :) The original project is taken from here . I wrapped and modified it "a bit" for my purposes.



These views are not "normal" views in the sense that they don't need to be in an activity, but are added diectly to the WindowManager.
This allows for special uses, such as pop-up notifications which the user can interact with or simply discard. But also special care must be taken.

The attached zip is itself a small demo. All the "important" things are in the inline Java part in "oService", and how are they created and destroyed. The rest is just for the demo itself

Possibly many things can be improved, such as adding animations (I think in this case they are a bit tricky and still struggling with them)

Important: These views are not automatically destroyed when you destroy your app. Please pay attention at Service_Destroy

Also: the sound example in Files folder is from http://www.freesfx.co.uk/. It is free, but if you use it, they need to be mentioned



Enjoy!

(I am preparing the drop option as in facebook messenger, but will take a while ;))
 

Attachments

  • chatHeadDemo.zip
    122.2 KB · Views: 618
  • GIF_overlay_shared.zip
    160.6 KB · Views: 439
Last edited:

Star-Dust

Expert
Licensed User
I'm unlucky and I can not see my code now. As soon as I get back I see how I did it.
 

Smee

Well-Known Member
Licensed User
Longtime User
I'm unlucky and I can not see my code now. As soon as I get back I see how I did it.
No problem, i fixed it. BTW i had a look at your library, well done, looks really good
 

Star-Dust

Expert
Licensed User
.. the code is as follows
B4X:
        public void onLongPress(MotionEvent e) {    
            processBA.raiseEvent(null, "onLong_Press", index);
        }
I use this:
B4X:
@Override
        public boolean onDoubleTap(MotionEvent e) {
            processBA.raiseEvent(null, "ondouble_tap", index);
            return true;
        }
        public boolean onSingleTapConfirmed(MotionEvent e) {
            processBA.raiseEvent(null, "onsingle_tap", index);
            return true;
        }
        public void onLongPress(MotionEvent e) {
            processBA.raiseEvent(null, "onlong_tap", index);
        }
 

Smee

Well-Known Member
Licensed User
Longtime User
UPDATE:
Is it not always the way. I have struggled with this for ages and as soon as I ask for help the solution comes to me. Well a partial solution anyway. By destroying any previous head in Init_elements first it works. Every other place has generated an error. This at least allows me to create the latest head but I do not have the history of the previous ones on the screen. If you could help with that it would be fantastic. Thanks

Hi
I know that you are not actively involved with this anymore but I am trying to understand how the system destroys the standout created. I have generated a few heads (for want of a better word) and then try to destroy them. No matter what I do Only one is ever destroyed. The others stay in view untill the program ceases to run. I have moved the code from Service_destroy to a sub routine and I have tried to call it from various places. The result is always the same. Only the last one generated is destroyed. I have stepped through the code that removes the heads and it only ever goes through the loop once. Can you offer any advice as to why it does not work or how I could remove them
Many thanks for any replies

EDIT
I think I have realised the problem, Your code creates as many heads as is required immediately, What I have tried to do is make a call to the code and create a head when required and then when one is removed they are all supposed to be removed. However they are created in the map one at a time so there will only ever be one active at any time. I have then tried to destroy the one that is active before creating a new one but that does not work either. Any suggestions?
 
Last edited:

JordiCP

Well-Known Member
Licensed User
Longtime User
I know that you are not actively involved with this anymore
Yes and not ;). I also had some funny "variations" based on the original code and will now release one of them in the first post (I was just finishing to clean it), since it is good for me if people can play with it and make new things

About how to destroy them, you should keep a reference to each created object (in a map, list or array, depending on how your code works) and then call the destructor for each one of them (and remove from the list/map if you don't do it when you finish the service)

-EDIT- Just released it -
 
Last edited:

Smee

Well-Known Member
Licensed User
Longtime User
Thanks for the reply, I will check out your new release.
 

Star-Dust

Expert
Licensed User
(2017/07/01)

Based on this wrapper, I also built some strange things on it, with different purposes. I am not working on most of them anymore, so I decided to share.

This one is funny. Similar to the video but with added elements
It combines several overlay elements and brings them into life simulating GIF animation (based on some previously converted GIF files to PNG) and sequencing them with a timer. In this version there are some xmas stars, a Santa and a spider skull

I publish it here since the thread already exists and is related to it... As always, perhaps the code style is not the best in the world to learn from, but I have tried to comment a bit and delete unneeded things so that it will be more easy to follow.

Important: in most recent android versions, you must grant directly to the app the permission to show elements on top of others (it is not enough with the declared manifest permissions). So, if nothing appears on your screen you should check it.

Hope you enjoy it :)
-----------------------------------

I'm happy to share this chatHead wrapper made with B4A and Inline Java. :) The original project is taken from here . I wrapped and modified it "a bit" for my purposes.



These views are not "normal" views in the sense that they don't need to be in an activity, but are added diectly to the WindowManager.
This allows for special uses, such as pop-up notifications which the user can interact with or simply discard. But also special care must be taken.

The attached zip is itself a small demo. All the "important" things are in the inline Java part in "oService", and how are they created and destroyed. The rest is just for the demo itself

Possibly many things can be improved, such as adding animations (I think in this case they are a bit tricky and still struggling with them)

Important: These views are not automatically destroyed when you destroy your app. Please pay attention at Service_Destroy

Also: the sound example in Files folder is from http://www.freesfx.co.uk/. It is free, but if you use it, they need to be mentioned



Enjoy!

(I am preparing the drop option as in facebook messenger, but will take a while ;))

But if I extend the GroupView class instead of ImageView, would I be able to make a set of Views floating? How a floating panel? What do you think about it

B4X:
public class CustomImageView extends GroupView {
     }
 

JordiCP

Well-Known Member
Licensed User
Longtime User
Yes, you can place a GroupView or derived class and place elements inside it.
Now I don't remember exactly, but something similar to what was done here ;)
 

Star-Dust

Expert
Licensed User
Thank's. But I think I arrived late, someone thought it long before me. :p
 

DaniDPX

Member
Licensed User
Hi @JordiCP, It is really helpful example. I have one question to ask you on how I use Panel view instead of Bitmaps to be displayed exactly same like GiF overlay?

Regards
Daniel G.
 

Star-Dust

Expert
Licensed User
Why it does not work by setting in the manifetst sdk_target = 26?

This message appears
PSX_20180126_201857.jpg

But there is not this option on the device
 
Last edited:

Kevin

Well-Known Member
Licensed User
Longtime User
Any updates on thoughts on how to allow dropping, which I assume means flinging it to the bottom to close the app?

Pretty cool, either way.
 

JordiCP

Well-Known Member
Licensed User
Longtime User
Sorry, now I see some pending questions which I didn't reply when they were made.:oops:

I haven't been into this project for months, and won't take it again in some time (that's why I released the source code) so my memory may fail me, but will try to answer what I remember.

Hi @JordiCP, It is really helpful example. I have one question to ask you on how I use Panel view instead of Bitmaps to be displayed exactly same like GiF overlay?
B4A Panel Views are activity objects and this chathead is meant to work from a service so they will not work directly. Not sure since I still must test it, but perhaps you can take a look to OverlayWindow from Informatix's Pro Bundle.

This message appears
The only problem that I am aware of, when using alert windows, is that in the later android versions they are seen as dangerous and need special permissions which don't work as the normal ones: you must go to a special settings page to manually allow it. I made THIS (also based on overlays) some time ago that handles the access to that settings page (look at the inline java part of the activity) ;)

Any updates on thoughts on how to allow dropping, which I assume means flinging it to the bottom to close the app?
This would mean adding a second 'circle' that would move a bit (if we think of facebook messenger chathead) following the movement of the main chathead. It can be done in the gesturelistener of the inline java part, but requires time and someone to code it ;)
 

Star-Dust

Expert
Licensed User
The only problem that I am aware of, when using alert windows, is that in the later android versions they are seen as dangerous and need special permissions which don't work as the normal ones: you must go to a special settings page to manually allow it. I made THIS (also based on overlays) some time ago that handles the access to that settings page (look at the inline java part of the activity) ;)

Dear @JordiCP, permission request worked perfectly from SDK 23 up to 25
Unfortunately, by setting SDK_Taget = 26 a new problem arises.

standout_init_elements (B4A line: 107)
myChatHead.instance=J.RunMethod("newInstance2",
java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4j.object.JavaObject.RunMethod(JavaObject.java:131)
at it.tecnomedia.floatingtimer.standout._init_elements(standout.java:466)
at it.tecnomedia.floatingtimer.standout._service_start(standout.java:994)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:186)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:166)
at it.tecnomedia.floatingtimer.standout.handleStart(standout.java:116)
at it.tecnomedia.floatingtimer.standout.access$000(standout.java:19)
at it.tecnomedia.floatingtimer.standout$1.run(standout.java:81)
at anywheresoftware.b4a.objects.ServiceHelper$StarterHelper.onStartCommand(ServiceHelper.java:105)
at it.tecnomedia.floatingtimer.standout.onStartCommand(standout.java:79)
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:4032)
at android.app.ActivityThread.-wrap21(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2006)
at android.os.Handler.dispatchMessage(Handler.java:108)
at android.os.Looper.loop(Looper.java:166)
at android.app.ActivityThread.main(ActivityThread.java:7415)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
Caused by: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@fd3d5d -- permission denied for window type 2002
at android.view.ViewRootImpl.setView(ViewRootImpl.java:905)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:369)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:128)
at it.tecnomedia.floatingtimer.standout$CustomImageView.sharedConstructing(standout.java:1207)
at it.tecnomedia.floatingtimer.standout$CustomImageView.<init>(standout.java:1178)
at it.tecnomedia.floatingtimer.standout.newInstance2(standout.java:1345)
... 21 more

Everything happens to this line of your code:

B4X:
For Each descriptor As String In elements.Keys
        Dim num_instances As Int = elements.Get(descriptor)
        For k=0 To num_instances-1
            Dim myChatHead As chElement
            myChatHead.Initialize
            If descriptor="pic" Then myChatHead.ttype = PICTYPE
            If descriptor="drop" Then myChatHead.ttype = DROPTYPE

            myChatHead.active=True
            myChatHead.id=id
            ' -------------------   ERROR ON THIS ROW  -----------------------
            myChatHead.instance=J.RunMethod("newInstance2",Array(J,id))

            Dim Rect1 As Rect
            Rect1.Initialize(0,0,myChatHead.ttype.wwe,myChatHead.ttype.hhe)
            Dim bbb As Bitmap
            bbb.InitializeMutable(myChatHead.ttype.wwe,myChatHead.ttype.hhe)
            Dim cv As Canvas
            cv.Initialize2(bbb)
            cv.DrawBitmap(myChatHead.ttype.bitmap,Null,Rect1)
            If FloatingStandOut.WhiteCircle Then cv.DrawCircle(myChatHead.ttype.wwe/2,myChatHead.ttype.hhe/2,myChatHead.ttype.wwe/2-1dip,Colors.ARGB(200,255,255,255),False,1dip)
            myChatHead.instance.RunMethod("setImageBitmap",    Array(bbb))

            Dim xxe As Int = FloatingStandOut.X 'Rnd(0,a.Width-myChatHead.ttype.wwe)
            Dim yye As Int = FloatingStandOut.Y 'Rnd(0,a.Height-myChatHead.ttype.hhe)
  
            myChatHead.instance.RunMethod("mSetPos",Array(xxe,yye))
            'Add to the map
            myChatHeadsMap.Put(id,myChatHead)
            id=id+1
        Next
    Next
 

JordiCP

Well-Known Member
Licensed User
Longtime User
Just a guess based on some answers I've seen over there. Not tested but may be the cause

Look for the TYPE_PHONE or SYSTEM_ALERT string in the code where params is initted, and replace it by LAYOUT_FLAG, which should be defined as follows

B4X:
// add these lines before the camParams initialisation
int LAYOUT_FLAG;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
   LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
   LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE;
}

params = new WindowManager.LayoutParams(
   <whatever there was here>,
   <whatever there was here>,
   LAYOUT_FLAG,
   <....>
   <....> );

Pls report results ;)
 
Last edited:

Star-Dust

Expert
Licensed User
Now solved.
Of course it is getting complicated .... From sdk 23+ you have to request a special permit, while from 26+ you have to change the flag

Add this
B4X:
import android.os.Build;
Modified the code like this
B4X:
private void sharedConstructing(Context context) {
        super.setClickable(true);

        this.context = context;
        windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

        if (Build.VERSION.SDK_INT >= 26) {
           params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,//TYPE_SYSTEM_ALERT,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
                PixelFormat.TRANSLUCENT);
        } else {
           params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_PHONE,//TYPE_SYSTEM_ALERT,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
                PixelFormat.TRANSLUCENT);
        }

        params.gravity = Gravity.TOP | Gravity.LEFT;
        params.x = 0;
        params.y = 100;

        windowManager.addView(this, params);
 
Top