Page 1 of 2

Notification

Posted: Thu Feb 18, 2016 9:44 pm
by spacebuddy
Is there anyway in PureBasic to send notifications in El Capitain?

Thanks :D

Re: Notification

Posted: Fri Feb 19, 2016 10:33 am
by wilbert
It should be possible but I'm having trouble to get it working :?
You need the NSUserNotificationCenter and NSUserNotification classes and the plist must contain a CFBundleIdentifier .

Re: Notification

Posted: Sat Feb 20, 2016 7:22 pm
by spacebuddy
Okay thanks Wilbert, looks like this will be a hard one to figure out :shock:

Re: Notification

Posted: Wed Sep 07, 2016 11:47 pm
by deseven
You can add a notification like that:

Code: Select all

notification = CocoaMessage(0,CocoaMessage(0,0,"NSUserNotification alloc"),"init")
CocoaMessage(0,notification,"setTitle:$",@"test")
CocoaMessage(0,notification,"setSubtitle:$",@"test2")
CocoaMessage(0,notification,"setInformativeText:$",@"test3")

notificationCenter = CocoaMessage(0,0,"NSUserNotificationCenter defaultUserNotificationCenter")
CocoaMessage(0,notificationCenter,"deliverNotification:",notification)
I tested it and it works fine on OS X 10.11.
However i'm not sure if you need to sign your app. It didn't work for the first time, then i signed an app with the same bundle id and it started to work everywhere.
UPD: yes, you need to sign your app first or use a bundle id of any other already signed app (it seems that there are no checks for valid id)

Image

Still, to display a popup you need to use delegate.

Code: Select all

- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center
     shouldPresentNotification:(NSUserNotification *)notification
{
    return YES;
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
 
    NSUserNotificationCenter *userNotificationCenter = [NSUserNotificationCenter defaultUserNotificationCenter];
    userNotificationCenter.delegate = self;
}
I have no idea how to port it. Wilbert, can you please help?

Re: Notification

Posted: Thu Sep 08, 2016 12:22 am
by deseven
Another thing i found is that you don't need to do anything else if your app is hidden or at least not frontmost.

This code adds a notification and displays a popup:

Code: Select all

app = CocoaMessage(0,0,"NSApplication sharedApplication")

OpenWindow(0,0,0,0,0,"")
CocoaMessage(0,app,"hide:")

notificationCenter = CocoaMessage(0,0,"NSUserNotificationCenter defaultUserNotificationCenter")
notification = CocoaMessage(0,CocoaMessage(0,0,"NSUserNotification alloc"),"init")
CocoaMessage(0,notification,"setTitle:$",@"test")
CocoaMessage(0,notification,"setSubtitle:$",@"test2")
CocoaMessage(0,notification,"setInformativeText:$",@"test3")

CocoaMessage(0,notificationCenter,"deliverNotification:",notification)

Repeat
  ev = WaitWindowEvent()
Until ev = #PB_Event_CloseWindow
But it would be nice to know how to do it in all cases.

Re: Notification

Posted: Sat Sep 10, 2016 6:20 pm
by deseven
What else i'm trying to achieve is to control a behaviour when user clicks on notification. By default it just activates your application, which can be not exactly what you need. As far as i know, there is no way to define desired action.

I looked into sources of TerminalNotifier (very nice app by the way) and it seems that there is a callback called userActivatedNotification which is being called by NotificationCenter itself (?).

How to implement it in PB?

Re: Notification

Posted: Sat Sep 10, 2016 7:27 pm
by wilbert
From what I understand you need to implement applicationDidFinishLaunching: .
When the application is launched, it sends a NSNotification object to this method from which you need the userInfo to get the NSUserNotification object.
NSUserNotification * userNotification = [[aNotification userInfo] objectForKey:NSApplicationLaunchUserNotificationKey];

I think PB also uses applicationDidFinishLaunching so you might need to hook into this and still call the implementation PureBasic provides to prevent problems. :?

Re: Notification

Posted: Sat Sep 10, 2016 7:37 pm
by deseven
Thanks wilbert.
wilbert wrote:I think PB also uses applicationDidFinishLaunching so you might need to hook into this and still call the implementation PureBasic provides to prevent problems. :?
How can i do that? Where to start?

Re: Notification

Posted: Sat Sep 10, 2016 7:53 pm
by wilbert
deseven wrote:How can i do that? Where to start?
You need method swizzling but I'm no expert on that :(
What also might work is add an observer for NSApplicationDidFinishLaunchingNotification but I'm not sure if that sends the right object. :?

Re: Notification

Posted: Sat Sep 10, 2016 10:32 pm
by deseven
For now i don't need to know what notification it is, it will be enough to know that user clicked on any notification. So probably i don't need an object with notification.
How to add an observer for NSApplicationDidFinishLaunchingNotification then?

Re: Notification

Posted: Sun Sep 11, 2016 1:13 pm
by wilbert
deseven wrote:How to add an observer for NSApplicationDidFinishLaunchingNotification then?
Can you try if this does anything at all ?

Code: Select all

Global notificationCenter = CocoaMessage(0, 0, "NSNotificationCenter defaultCenter")
Global appDelegate = CocoaMessage(0, CocoaMessage(0, 0, "NSApplication sharedApplication"), "delegate")
Global delegateClass = object_getClass_(appDelegate)

ProcedureC DidFinishLaunching(obj, sel, notification)
  Protected userInfo.i = CocoaMessage(0, notification, "userInfo")
  Protected userNotification.i = CocoaMessage(0, userInfo, "objectForKey:$", @"NSApplicationLaunchUserNotificationKey")
  If userNotification
    MessageRequester("UserNotification", "UserNotification was clicked")
  EndIf
EndProcedure

selector = sel_registerName_("MyAppDidFinishLaunching:")
class_addMethod_(delegateClass, selector, @DidFinishLaunching(), "v@:@")
CocoaMessage(0, notificationCenter, "addObserver:", appDelegate, "selector:", selector, "name:$", @"NSApplicationDidFinishLaunchingNotification", "object:", #nil)
  
 
If OpenWindow(0, 0, 0, 320, 170, "Test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget)
  
  EditorGadget(0, 10, 10, 300, 150)
  
  
  Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
  
EndIf

CocoaMessage(0, notificationCenter, "removeObserver:", appDelegate, "name:$", @"NSApplicationDidFinishLaunchingNotification", "object:", #nil)

Re: Notification

Posted: Sun Sep 11, 2016 3:43 pm
by deseven
Nope.
DidFinishLaunching() is called only once when application starts (which is actually strange), click on a notification doesn't do anything.
The only difference with your code is that if your app is closed completely then click on a notification opens an empty window without title. Don't know how that happens.

But thanks for showing me how to add observers, i'll try to figure something out.

Re: Notification

Posted: Sun Sep 11, 2016 3:48 pm
by deseven
It seems that NSApplicationDidFinishLaunchingNotification doesn't have anything to do with notifications from NotificationCenter at all.
UPD: here is what i need to catch:

Code: Select all

- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification

Re: Notification

Posted: Sun Sep 11, 2016 4:59 pm
by wilbert
According to the NSUserNotificationCenterDelegate Protocol Reference ...
NSUserNotificationCenterDelegate Protocol Reference wrote:To take an action when your application is launched as a result of a user clicking on a notification, be sure to implement the applicationDidFinishLaunching: method in the application class that implements the NSApplicationDelegate Protocol protocol. The notification parameter to that method has a userInfo dictionary, and if that dictionary has the NSApplicationLaunchUserNotificationKey key. The value of that key is the NSUserNotification object that caused the application to launch. The NSUserNotification object is delivered to the NSApplication delegate because that message will be sent before your application has a chance to set a delegate for the NSUserNotificationCenter.
I didn't read the last line carefully.
It might be impossible to catch this because of the message already been delivered before the PB code can do something about it.

But it seems this behavior is not what you are looking for.
Try this

Code: Select all

ProcedureC DidActivateNotification(obj, sel, center, notification)
  MessageRequester("","didActivateNotification")
EndProcedure

ProcedureC ShouldPresentNotification(obj, sel, center, notification)
  ProcedureReturn #YES
EndProcedure


; >>> Create delegate class <<<
delegateClass = objc_allocateClassPair_(objc_getClass_("NSObject"), "myDelegateClass", 0)
; >>> Add delegate methods <<<
class_addMethod_(delegateClass, 
                 sel_registerName_("userNotificationCenter:didActivateNotification:"),
                 @DidActivateNotification(), "v@:@@")
class_addMethod_(delegateClass, 
                 sel_registerName_("userNotificationCenter:shouldPresentNotification:"),
                 @ShouldPresentNotification(), "c@:@@")
; >>> Register class <<< 
objc_registerClassPair_(delegateClass)
; >>> Create delegate instance <<<
delegate = class_createInstance_(delegateClass, 0)


Global userNotificationCenter = CocoaMessage(0, 0, "NSUserNotificationCenter defaultUserNotificationCenter")
CocoaMessage(0, userNotificationCenter, "setDelegate:", delegate)
By the way, new is a combination of alloc and init so you can also create your notification
notification = CocoaMessage(0,0,"NSUserNotification new")

Re: Notification

Posted: Sun Sep 11, 2016 6:15 pm
by deseven
wilbert wrote:Try this
Works like a charm! Thank you very much!
wilbert wrote:By the way, new is a combination of alloc and init so you can also create your notification
notification = CocoaMessage(0,0,"NSUserNotification new")
Didn't know, thanks again.

Next thing is to populate and parse userInfo dictionary to be able to define specific actions, but that's easy enough, i can do it.
I think i'm going to write a module so everyone can use native OS X notifications easily.