Notification

Mac OSX specific forum
spacebuddy
Enthusiast
Enthusiast
Posts: 346
Joined: Thu Jul 02, 2009 5:42 am

Notification

Post by spacebuddy »

Is there anyway in PureBasic to send notifications in El Capitain?

Thanks :D
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Notification

Post 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 .
Windows (x64)
Raspberry Pi OS (Arm64)
spacebuddy
Enthusiast
Enthusiast
Posts: 346
Joined: Thu Jul 02, 2009 5:42 am

Re: Notification

Post by spacebuddy »

Okay thanks Wilbert, looks like this will be a hard one to figure out :shock:
User avatar
deseven
Enthusiast
Enthusiast
Posts: 362
Joined: Wed Jan 12, 2011 3:48 pm
Location: Serbia
Contact:

Re: Notification

Post 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?
User avatar
deseven
Enthusiast
Enthusiast
Posts: 362
Joined: Wed Jan 12, 2011 3:48 pm
Location: Serbia
Contact:

Re: Notification

Post 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.
User avatar
deseven
Enthusiast
Enthusiast
Posts: 362
Joined: Wed Jan 12, 2011 3:48 pm
Location: Serbia
Contact:

Re: Notification

Post 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?
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Notification

Post 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. :?
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
deseven
Enthusiast
Enthusiast
Posts: 362
Joined: Wed Jan 12, 2011 3:48 pm
Location: Serbia
Contact:

Re: Notification

Post 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?
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Notification

Post 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. :?
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
deseven
Enthusiast
Enthusiast
Posts: 362
Joined: Wed Jan 12, 2011 3:48 pm
Location: Serbia
Contact:

Re: Notification

Post 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?
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Notification

Post 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)
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
deseven
Enthusiast
Enthusiast
Posts: 362
Joined: Wed Jan 12, 2011 3:48 pm
Location: Serbia
Contact:

Re: Notification

Post 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.
User avatar
deseven
Enthusiast
Enthusiast
Posts: 362
Joined: Wed Jan 12, 2011 3:48 pm
Location: Serbia
Contact:

Re: Notification

Post 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
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Notification

Post 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")
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
deseven
Enthusiast
Enthusiast
Posts: 362
Joined: Wed Jan 12, 2011 3:48 pm
Location: Serbia
Contact:

Re: Notification

Post 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.
Post Reply