B4J Library jNotifications - Simple tray notifications (Class)

jmon

Well-Known Member
Licensed User
Hello,

This is a class I wrote that creates a simple pop-up notification at the bottom of the screen. It's animated and can be fully customized.

At the moment, only one position is supported, which is at the bottom right of the screen.

Features:
  • Set custom text and background colors
  • Set a custom title and message
  • animation, toast style (non linear animation, slowing down)
  • Set custom Icon
  • 2 events can be retrieved, 1) when the user clicks the [X] button, 2) when the user clicks the notification
  • Can set the offset from the border of the screen manually
Capture2.JPG
Capture.JPG


[Edit] Minimum Java 8u20
  • Notification will stay on top of other windows
  • Fixed a possible bug
  • Added the events so that they show in the event list when creating a new sub.
  • Event "_Closed" and "Action" are now called with a delay (callsubdelayed)
[Edit] Added new methods:
  • MessageColor
  • TitleColor
[Edit] Added library description to first post and some thumbnail images.

[Edit] Modified the library a bit, with script breaking changes:
  • Renamed the method: "Text" ( and setText) to "Message", so it is now clearer
 

Attachments

Last edited:

jmon

Well-Known Member
Licensed User
Very nice:
It is a bit jumpy when running in Debug mode but works smooth in Release (Debug mode will soon be much faster...).
Thanks. Yes, I noticed too that the debug mode is slower.

A question not directly related to this notification library, but do you know why the "setSelectedColor" function I wrote doesn't work?
[Edit] It actually works, but doesn't reflects the shown color in the Main GUI.
 
Last edited:

jmon

Well-Known Member
Licensed User
This is a reply to a post in another thread (jSystemTray - Add icons to the system tray (notifications)):
It would be possible that the notification appears above all other windows (topmost in desktop) ?
Yes, with jNotifications, you can have the notification appear above other windows if you comment the line SetOwner (but it won't stay on top always):
B4X:
frm.Initialize("frm", -1, -1)
frm.RootPane.LoadLayout("jNotifications")
'frm.SetOwner(Owner)
frm.Resizable = False
frm.SetFormStyle("UNDECORATED")
This way, everytime you call the Notif.Show method, the window will appear on top of others, because the window is closed everytime it disappears and recreated everytime.

You will be able to display notifications above other desktop windows even if your main window is in background. Now the issue, is that the jNotification cannot have the "Top most" attribute and will go back to background when another window is focused.

When you don't set the owner of a window, don't forget to "kill" the notification window in your main module when you close the application by doing so:
B4X:
Sub MainForm_CloseRequest (EventData As Event)
    Notif.Form.Close
End Sub
(Note that this is the reason why I allowed access to the Form method)

I am currently writing a chat program, and so I needed the notifications to be on top of other windows, even if the chat program was in background. To set the top most attribute, I have to call an external AutoIt script to set the attribute. Currently the jAutoItX doesn't work with all the winxxx functions, but WinSetOnTop(WindowName, WindowText) is what you need.
 

Gaver Powers

Member
Licensed User
JMon,

Thanks for the notification class...

I downloaded the zip this morning and tried to run on a WinXP system - got the following error in the log when run in debug mode.
B4X:
Program started.
An error occurred:
(Line: 192) Public Sub setImage(Img As Image)
java.lang.IndexOutOfBoundsException: index 6
Any suggestions on what I'm doing wrong, or how to fix?

Thanks again
G

UPDATE: It worked in Release mode.

Also.. was wondering if you could tell me how to set the message color without calling the getSelectedColor() function?

I tried:
B4X:
Notif.MessageColor = "oxff0000ff"
and I'm getting a types do not match error.
 
Last edited:

jmon

Well-Known Member
Licensed User
Also.. was wondering if you could tell me how to set the message color without calling the getSelectedColor() function?
Hi, thanks for your comments. Regarding the color problem, I dont have B4J with me at the moment. I will reply to this question tomorrow when I have B4J. In the meantime, could you try with either:
B4X:
Notif.MessageColor = fx.colors.rgb(0, 0, 255)
'or
Notif.MessageColor = "#0000FF"
(I forgot how I implemented it).

[EDIT]
Ok, now I see your error. Just remove the quotation marks:
B4X:
Notif.MessageColor = 0xFF0000FF
And I think that both example I wrote above work too.

[EDIT]
Note that it's not ox (OH-X), it's 0x (zero-X)
 
Last edited:

jmon

Well-Known Member
Licensed User
Ok, so I tried with B4J today, and this is the answer to set the color of the message or the title:
B4X:
Notif.MessageColor = fx.Colors.RGB(0, 0, 255)
 

jmon

Well-Known Member
Licensed User
It would be possible that the notification appears above all other windows (topmost in desktop) ?
For those having the same issue, here is the way I do it (Until the next update of javaFx which should add the possibility to put the window on top):
  • First, comment the line
    B4X:
    'frm.SetOwner(Owner)
  • Add a title to the Form in the jNotification:
    B4X:
    frm.Title = "My Notification."
  • I wrote an AutoIt Script that sets the window on top using the command line (Note that the next code is AutoIt and not B4J!) (I don't post the AutoIt file, you will have to do it yourself, because autoit files are often flagged as malware by antivirus programs, I don't want anyone blaming me for that :):
  • B4X:
    #NoTrayIcon
    If $CmdLine[0] < 2 Then Exit 0
    Switch $CmdLine[1]
        Case "setWindowOnTop"
            If $CmdLine[0] < 3 Then Exit 0
            If WinWait($CmdLine[2], "", 5000) = 0 Then Exit 0
            If WinSetOnTop($CmdLine[2], "", $CmdLine[3]) = 1 Then
                Exit 1
            Else
                Exit 0
            EndIf
    EndSwitch
    Exit 0
  • Compile the script, then name it setWindowOnTop.exe and put it in your ../Objects folder.
  • Then in the jNotification Class in B4J, paste this function (note that you will need to load the jShell library):
    B4X:
    Private Sub setWindowOnTopAsync(sTitle As String)
        Dim sArgs(3) As String
        sArgs(0) = "setWindowOnTop"
        sArgs(1) = sTitle
        sArgs(2) = "1"
        Dim jShell As Shell
        jShell.Initialize("jShell_setWinOnTop", File.Combine(File.DirApp, "setWindowOnTop.exe"), sArgs)
        jShell.Run(5000)
    End Sub
  • Note: If you want to remove the ontop attribute on the window, just change the args(2) to = "0"
  • Then add this code to the Sub Show of the jNotification library:
    B4X:
    CallSubDelayed2(Me, "setWindowOnTopAsync", frm.Title)
With this method, every time the jNotification is called, a new window is created and set on top each time. I know that this trick isn't full B4J or Java, but that the only way I found.
 

Pascual Pérez

Member
Licensed User
Thank you jmon, i'm using another similar alternative, the tool nircmd, calling nircmd.exe win settopmost "my notification".
I hope next JavaFX will bring this possibility, for portability sake.
 

jmon

Well-Known Member
Licensed User

JTmartins

Active Member
Licensed User
Hello,

How do we set the Notif.BackColor without using the getSelectedColor() ?

I've tryed the Notif.BackColor = fx.Colors.RGB(0, 0, 255), but it does not work (works ok for title & message).

I've noticed is a Paint object, but although I've tryed to play with it, no success was achieved.

Thanks

PS- Also an easy method to show notification always on top would be super.
 

JTmartins

Active Member
Licensed User
Notif.Back Color solved as

B4X:
Notif.BackColor=getSelectedColor (fx.Colors.RGB(61,121,204))
and changed Sub getSelectedColor as

B4X:
Public Sub getSelectedColor(color As JavaObject) As Paint
    Dim jo As JavaObject
    jo=color
    Return jo
End Sub
The AlwaysOnTop autoIT method seems to work Fine, but only in release mode. As during debug mode it does not come on top. I can live with that. :)
 
Last edited:

JTmartins

Active Member
Licensed User
Question

How do we make the notify window actually disapear ? Beacuse It pops up, stays there for the amount of time defined then goes down, but as I'm using aero in windows 7, I still see it under the taskbar transparency.

I can catch and close it by pressing the X with

B4X:
Sub Notif_Closed
    Notif.Form .Close
End Sub
but I would like to close it, after the animation has finished going down as there is no warranty the the user will press the X button.

Many thanks
 
Last edited:

jmon

Well-Known Member
Licensed User
Notif.Back Color solved as
B4X:
Notif.BackColor=getSelectedColor (fx.Colors.RGB(61,121,204))
Good

The AlwaysOnTop autoIT method seems to work Fine, but only in release mode. As during debug mode it does not come on top. I can live with that. :)
If you use Javafx 8u20 +, you can use "setWindowOnTop", here : http://www.basic4ppc.com/android/fo...indow-top-most-solved-jfx8.37319/#post-289599

How do we make the notify window actually disapear ? Beacuse It pops up, stays there for the amount of time defined then goes down, but as I'm using aero in windows 7, I still see it under the taskbar transparency.
You should modify the "Animation_Tick" sub:
B4X:
Private Sub Animation_Tick
    If ANIM_TYPE = ANIM_SHOWING AND Not(TOAST_AT_FINAL_POS) Then
        Dim Target As Float = MainScreen.MaxY - yOffset - frm.Height
        If frm.WindowTop > Target Then
            Dim Distance As Float = (frm.WindowTop - Target)
            frm.WindowTop = frm.WindowTop - ((Distance / 100) * ANIM_SPEED)
        Else
            TOAST_AT_FINAL_POS = True
            TOAST_IS_VISIBLE = True
            TimerAnim.Enabled = False
            Log("Toast is Visible")
        End If
    Else If ANIM_TYPE = ANIM_HIDING AND Not(TOAST_AT_FINAL_POS) Then
        Dim Target As Float = MainScreen.MaxY + 100
        If frm.WindowTop < Target Then
            Dim Distance As Float = (Target - frm.WindowTop)
            frm.WindowTop = frm.WindowTop + ((Distance / 100) * ANIM_SPEED * 2)
        Else
            TOAST_AT_FINAL_POS = True
            TOAST_IS_VISIBLE = False
            TimerAnim.Enabled = False
            TimerDuration.Enabled = False
            Log("Toast is hidden")
            If TOAST_HIDE_THEN_SHOW Then
                TOAST_HIDE_THEN_SHOW = False
                Show
            End If   
        End If       
    End If
End Sub
Actually, just modifying this line is enough:
B4X:
Dim Target As Float = MainScreen.MaxY + 100
It will move the window 100px lower. If it's not enough, increase the number.

I hope that helps.
Jmon.
 

jmon

Well-Known Member
Licensed User
This is an update of my autoit script that I posted above. That deals with the Window Aero problem mentioned above, where Windows adds window borders automatically:

Note that the following code is AUTOIT language, not B4J!
B4X:
#NoTrayIcon
#include <WinAPI.au3>

If $CmdLine[0] < 2 Then Exit 0

Switch $CmdLine[1]
    Case "setWindowOnTop"
        If $CmdLine[0] < 3 Then Exit 0
        If WinWait($CmdLine[2], "", 5000) = 0 Then Exit 0
        If setPopUpAttributes($CmdLine[2], "") = True Then
            Exit 1
        Else
            Exit 0
        EndIf
EndSwitch

Exit 0

Func setPopUpAttributes($Title, $Text)
    Local $sizeWindow, $sizeClient, $borderWidth, $titleHeight, $rgn
    ;Set on top:
    WinSetOnTop($Title, $Text, 1)
    ;Crop to client area:
    $sizeWindow = WinGetPos($Title, $Text)
    If @error Then Return False
    $sizeClient = WinGetClientSize($Title, $Text)
    If @error Then Return False
    $borderWidth = ($sizeWindow[2] - $sizeClient[0]) / 2
    $titleHeight = ($sizeWindow[3] - $sizeClient[1]) - $borderWidth
    $rgn = _WinAPI_CreateRectRgn($borderWidth, $titleHeight , $sizeClient[0] + $borderWidth, $sizeClient[1] + $titleHeight)
    If $rgn = 0 Then Return False
    _WinAPI_SetWindowRgn(WinGetHandle($Title), $rgn, True)
    Return True
EndFunc
This function will "Crop" the window to the client area, and cropping out the borders.

Now you won't need to call this anymore:
B4X:
frm.SetFormStyle("UNDECORATED")
 
Top