mirror of
https://github.com/videolan/vlc.git
synced 2024-12-05 07:46:32 +08:00
osx_notifications: remove Growl support, refactor
As per the discussion on the ML, Growl support is removed for VLC 4.0. With this refactoring the module is ARC compatible too, simplifying memory management. https://mailman.videolan.org/pipermail/vlc-devel/2018-February/117924.html
This commit is contained in:
parent
086a2838eb
commit
66cdaaaf1f
3
NEWS
3
NEWS
@ -23,6 +23,9 @@ Video output:
|
||||
* Remove evas plugin
|
||||
* Remove omxil_vout plugin
|
||||
|
||||
macOS:
|
||||
* Remove Growl notification support
|
||||
|
||||
|
||||
Changes between 2.2.8 and 3.0.0:
|
||||
--------------------------------
|
||||
|
10
configure.ac
10
configure.ac
@ -4103,15 +4103,11 @@ dnl
|
||||
dnl OS X notification plugin
|
||||
dnl
|
||||
AC_ARG_ENABLE(osx_notifications,
|
||||
[ --enable-osx-notifications osx notification plugin (default disabled)],,
|
||||
[AS_HELP_STRING([--enable-osx-notifications],
|
||||
[macOS notification plugin (default disabled)])],,
|
||||
[enable_osx_notifications=no])
|
||||
AS_IF([test "${enable_osx_notifications}" != "no"], [
|
||||
if test -d ${CONTRIB_DIR}/Growl.framework -o -d ${CONTRIB_DIR}/Frameworks/Growl.framework
|
||||
then
|
||||
VLC_ADD_PLUGIN([osx_notifications])
|
||||
VLC_ADD_LIBS([osx_notifications], [-Wl,-framework,Growl,-framework,Foundation])
|
||||
VLC_ADD_OBJCFLAGS([osx_notifications], [-fobjc-exceptions] )
|
||||
fi
|
||||
VLC_ADD_PLUGIN([osx_notifications])
|
||||
])
|
||||
|
||||
dnl
|
||||
|
@ -281,7 +281,7 @@ $Id$
|
||||
* opus: a opus audio decoder/packetizer/encoder using the libopus library
|
||||
* os2drive: service discovery for OS/2 drives
|
||||
* oss: audio output module using the OSS /dev/dsp interface
|
||||
* osx_notifications: announce currently playing stream to OS X/Growl
|
||||
* osx_notifications: announce currently playing stream to OS X
|
||||
* packetizer_a52: A/52 basic parser/packetizer
|
||||
* packetizer_avparser: libavcodec packetizer
|
||||
* packetizer_copy: Simple copy packetizer
|
||||
|
@ -1,9 +1,8 @@
|
||||
notifydir = $(pluginsdir)/notify
|
||||
|
||||
libosx_notifications_plugin_la_SOURCES = notify/osx_notifications.m
|
||||
libosx_notifications_plugin_la_OBJCFLAGS = $(AM_OBJCFLAGS) $(OBJCFLAGS_osx_notifications)
|
||||
libosx_notifications_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(notifydir)' -Wl,-framework,AppKit
|
||||
libosx_notifications_plugin_la_LIBADD = $(LIBS_osx_notifications)
|
||||
libosx_notifications_plugin_la_OBJCFLAGS = $(AM_OBJCFLAGS) -fobjc-arc -fobjc-exceptions
|
||||
libosx_notifications_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(notifydir)' -Wl,-framework,AppKit,-framework,Foundation
|
||||
|
||||
libnotify_plugin_la_SOURCES = notify/notify.c
|
||||
libnotify_plugin_la_CFLAGS = $(AM_CFLAGS) $(NOTIFY_CFLAGS)
|
||||
|
@ -1,9 +1,10 @@
|
||||
/*****************************************************************************
|
||||
* osx_notifications.m : OS X notification plugin
|
||||
*****************************************************************************
|
||||
* VLC specific code:
|
||||
* osx_notifications.m : macOS notification plugin
|
||||
*
|
||||
* Copyright © 2008,2011,2012,2015 the VideoLAN team
|
||||
* This plugin provides support for macOS notifications on current playlist
|
||||
* item changes.
|
||||
*****************************************************************************
|
||||
* Copyright © 2008, 2011, 2012, 2015, 2018 the VideoLAN team
|
||||
* $Id$
|
||||
*
|
||||
* Authors: Rafaël Carré <funman@videolanorg>
|
||||
@ -23,42 +24,9 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* Growl specific code, ripped from growlnotify:
|
||||
*
|
||||
* Copyright (c) The Growl Project, 2004-2005
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of Growl nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* Preamble
|
||||
*****************************************************************************/
|
||||
#define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
@ -66,9 +34,7 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <Growl/Growl.h>
|
||||
|
||||
#define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
|
||||
#include <vlc_common.h>
|
||||
#include <vlc_plugin.h>
|
||||
#include <vlc_playlist.h>
|
||||
@ -77,170 +43,158 @@
|
||||
#include <vlc_interface.h>
|
||||
#include <vlc_url.h>
|
||||
|
||||
/*****************************************************************************
|
||||
* intf_sys_t, VLCGrowlDelegate
|
||||
*****************************************************************************/
|
||||
@interface VLCGrowlDelegate : NSObject <GrowlApplicationBridgeDelegate>
|
||||
#pragma mark -
|
||||
#pragma mark Class interfaces
|
||||
@interface VLCNotificationDelegate : NSObject <NSUserNotificationCenterDelegate>
|
||||
{
|
||||
NSString *applicationName;
|
||||
NSString *notificationType;
|
||||
NSMutableDictionary *registrationDictionary;
|
||||
id lastNotification;
|
||||
bool isInForeground;
|
||||
intf_thread_t *interfaceThread;
|
||||
/** Interface thread, required for skipping to the next item */
|
||||
intf_thread_t * _Nonnull interfaceThread;
|
||||
|
||||
/** Holds the last notification so it can be cleared when the next one is delivered */
|
||||
NSUserNotification * _Nullable lastNotification;
|
||||
|
||||
/** Indicates if VLC is in foreground */
|
||||
BOOL isInForeground;
|
||||
}
|
||||
|
||||
- (id)initWithInterfaceThread:(intf_thread_t *)thread;
|
||||
- (void)registerToGrowl;
|
||||
- (void)notifyWithTitle:(const char *)title
|
||||
artist:(const char *)artist
|
||||
album:(const char *)album
|
||||
andArtUrl:(const char *)url;
|
||||
/**
|
||||
* Initializes a new VLCNotification Delegate with a given intf_thread_t
|
||||
*/
|
||||
- (instancetype)initWithInterfaceThread:(intf_thread_t * _Nonnull)intf_thread;
|
||||
|
||||
/**
|
||||
* Delegate method called when the current input changed
|
||||
*/
|
||||
- (void)currentInputDidChanged:(input_thread_t * _Nonnull)input;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Local prototypes
|
||||
struct intf_sys_t
|
||||
{
|
||||
VLCGrowlDelegate *o_growl_delegate;
|
||||
void *vlcNotificationDelegate;
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
* Local prototypes
|
||||
*****************************************************************************/
|
||||
static int Open ( vlc_object_t * );
|
||||
static void Close ( vlc_object_t * );
|
||||
static int InputCurrent(vlc_object_t *, const char *,
|
||||
vlc_value_t, vlc_value_t, void *);
|
||||
|
||||
static int InputCurrent( vlc_object_t *, const char *,
|
||||
vlc_value_t, vlc_value_t, void * );
|
||||
|
||||
/*****************************************************************************
|
||||
* Module descriptor
|
||||
****************************************************************************/
|
||||
vlc_module_begin ()
|
||||
set_category( CAT_INTERFACE )
|
||||
set_subcategory( SUBCAT_INTERFACE_CONTROL )
|
||||
set_shortname( "OSX-Notifications" )
|
||||
add_shortcut( "growl" )
|
||||
set_description( N_("OS X Notification Plugin") )
|
||||
set_capability( "interface", 0 )
|
||||
set_callbacks( Open, Close )
|
||||
vlc_module_end ()
|
||||
|
||||
/*****************************************************************************
|
||||
* Open: initialize and create stuff
|
||||
*****************************************************************************/
|
||||
static int Open( vlc_object_t *p_this )
|
||||
#pragma mark -
|
||||
#pragma mark C module functions
|
||||
/*
|
||||
* Open: Initialization of the module
|
||||
*/
|
||||
static int Open(vlc_object_t *p_this)
|
||||
{
|
||||
intf_thread_t *p_intf = (intf_thread_t *)p_this;
|
||||
playlist_t *p_playlist = pl_Get( p_intf );
|
||||
intf_sys_t *p_sys = p_intf->p_sys = calloc( 1, sizeof(intf_sys_t) );
|
||||
playlist_t *p_playlist = pl_Get(p_intf);
|
||||
intf_sys_t *p_sys = p_intf->p_sys = calloc(1, sizeof(intf_sys_t));
|
||||
|
||||
if( !p_sys )
|
||||
if (!p_sys)
|
||||
return VLC_ENOMEM;
|
||||
|
||||
p_sys->o_growl_delegate = [[VLCGrowlDelegate alloc] initWithInterfaceThread:p_intf];
|
||||
if( !p_sys->o_growl_delegate )
|
||||
{
|
||||
free( p_sys );
|
||||
return VLC_ENOMEM;
|
||||
@autoreleasepool {
|
||||
VLCNotificationDelegate *notificationDelegate =
|
||||
[[VLCNotificationDelegate alloc] initWithInterfaceThread:p_intf];
|
||||
|
||||
if (notificationDelegate == nil) {
|
||||
free(p_sys);
|
||||
return VLC_ENOMEM;
|
||||
}
|
||||
|
||||
p_sys->vlcNotificationDelegate = (__bridge_retained void*)notificationDelegate;
|
||||
}
|
||||
|
||||
var_AddCallback( p_playlist, "input-current", InputCurrent, p_intf );
|
||||
var_AddCallback(p_playlist, "input-current", InputCurrent, p_intf);
|
||||
|
||||
[p_sys->o_growl_delegate registerToGrowl];
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Close: destroy interface stuff
|
||||
*****************************************************************************/
|
||||
static void Close( vlc_object_t *p_this )
|
||||
/*
|
||||
* Close: Destruction of the module
|
||||
*/
|
||||
static void Close(vlc_object_t *p_this)
|
||||
{
|
||||
intf_thread_t *p_intf = (intf_thread_t *)p_this;
|
||||
playlist_t *p_playlist = pl_Get( p_intf );
|
||||
playlist_t *p_playlist = pl_Get(p_intf);
|
||||
intf_sys_t *p_sys = p_intf->p_sys;
|
||||
|
||||
// Remove the callback, this must be done here, before deallocating the
|
||||
// notification delegate object
|
||||
var_DelCallback(p_playlist, "input-current", InputCurrent, p_intf);
|
||||
|
||||
var_DelCallback( p_playlist, "input-current", InputCurrent, p_intf );
|
||||
@autoreleasepool {
|
||||
// Transfer ownership of notification delegate object back to ARC
|
||||
VLCNotificationDelegate *notificationDelegate =
|
||||
(__bridge_transfer VLCNotificationDelegate*)p_sys->vlcNotificationDelegate;
|
||||
|
||||
[GrowlApplicationBridge setGrowlDelegate:nil];
|
||||
[p_sys->o_growl_delegate release];
|
||||
free( p_sys );
|
||||
// Ensure the object is deallocated
|
||||
notificationDelegate = nil;
|
||||
}
|
||||
|
||||
free(p_sys);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* InputCurrent: Current playlist item changed callback
|
||||
*****************************************************************************/
|
||||
static int InputCurrent( vlc_object_t *p_this, const char *psz_var,
|
||||
vlc_value_t oldval, vlc_value_t newval, void *param )
|
||||
/*
|
||||
* Callback invoked on playlist item change
|
||||
*/
|
||||
static int InputCurrent(vlc_object_t *p_this, const char *psz_var,
|
||||
vlc_value_t oldval, vlc_value_t newval, void *param)
|
||||
{
|
||||
VLC_UNUSED(oldval);
|
||||
|
||||
intf_thread_t *p_intf = (intf_thread_t *)param;
|
||||
intf_sys_t *p_sys = p_intf->p_sys;
|
||||
input_thread_t *p_input = newval.p_address;
|
||||
char *psz_title = NULL;
|
||||
char *psz_artist = NULL;
|
||||
char *psz_album = NULL;
|
||||
char *psz_arturl = NULL;
|
||||
VLC_UNUSED(oldval);
|
||||
|
||||
if( !p_input )
|
||||
return VLC_SUCCESS;
|
||||
|
||||
input_item_t *p_item = input_GetItem( p_input );
|
||||
if( !p_item )
|
||||
return VLC_SUCCESS;
|
||||
|
||||
/* Get title */
|
||||
psz_title = input_item_GetNowPlayingFb( p_item );
|
||||
if( !psz_title )
|
||||
psz_title = input_item_GetTitleFbName( p_item );
|
||||
|
||||
if( EMPTY_STR( psz_title ) )
|
||||
{
|
||||
free( psz_title );
|
||||
return VLC_SUCCESS;
|
||||
@autoreleasepool {
|
||||
VLCNotificationDelegate *notificationDelegate =
|
||||
(__bridge VLCNotificationDelegate*)p_sys->vlcNotificationDelegate;
|
||||
|
||||
[notificationDelegate currentInputDidChanged:(input_thread_t *)p_input];
|
||||
}
|
||||
|
||||
/* Get Artist name */
|
||||
psz_artist = input_item_GetArtist( p_item );
|
||||
if( EMPTY_STR( psz_artist ) )
|
||||
FREENULL( psz_artist );
|
||||
|
||||
/* Get Album name */
|
||||
psz_album = input_item_GetAlbum( p_item ) ;
|
||||
if( EMPTY_STR( psz_album ) )
|
||||
FREENULL( psz_album );
|
||||
|
||||
/* Get Art path */
|
||||
psz_arturl = input_item_GetArtURL( p_item );
|
||||
if( psz_arturl )
|
||||
{
|
||||
char *psz = vlc_uri2path( psz_arturl );
|
||||
free( psz_arturl );
|
||||
psz_arturl = psz;
|
||||
}
|
||||
|
||||
[p_sys->o_growl_delegate notifyWithTitle:psz_title
|
||||
artist:psz_artist
|
||||
album:psz_album
|
||||
andArtUrl:psz_arturl];
|
||||
|
||||
free( psz_title );
|
||||
free( psz_artist );
|
||||
free( psz_album );
|
||||
free( psz_arturl );
|
||||
return VLC_SUCCESS;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* VLCGrowlDelegate
|
||||
*****************************************************************************/
|
||||
@implementation VLCGrowlDelegate
|
||||
|
||||
- (id)initWithInterfaceThread:(intf_thread_t *)thread {
|
||||
if( !( self = [super init] ) )
|
||||
/**
|
||||
* Transfers a null-terminated UTF-8 C "string" to a NSString
|
||||
* in a way that the NSString takes ownership of it.
|
||||
*
|
||||
* \warning After calling this function, passed cStr must not be used anymore!
|
||||
*
|
||||
* \param cStr Pointer to a zero-terminated UTF-8 encoded char array
|
||||
*
|
||||
* \return An NSString instance that uses cStr as internal data storage and
|
||||
* frees it when done. On error, nil is returned and cStr is freed.
|
||||
*/
|
||||
static inline NSString* CharsToNSString(char * _Nullable cStr)
|
||||
{
|
||||
if (!cStr)
|
||||
return nil;
|
||||
|
||||
@autoreleasepool {
|
||||
NSString *resString = [[NSString alloc] initWithBytesNoCopy:cStr
|
||||
length:strlen(cStr)
|
||||
encoding:NSUTF8StringEncoding
|
||||
freeWhenDone:YES];
|
||||
if (unlikely(resString == nil))
|
||||
free(cStr);
|
||||
|
||||
return resString;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Class implementation
|
||||
@implementation VLCNotificationDelegate
|
||||
|
||||
- (id)initWithInterfaceThread:(intf_thread_t *)intf_thread
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
interfaceThread = intf_thread;
|
||||
|
||||
// Subscribe to notifications to determine if VLC is in foreground or not
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(applicationActiveChange:)
|
||||
@ -251,159 +205,64 @@ static int InputCurrent( vlc_object_t *p_this, const char *psz_var,
|
||||
selector:@selector(applicationActiveChange:)
|
||||
name:NSApplicationDidResignActiveNotification
|
||||
object:nil];
|
||||
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
|
||||
}
|
||||
// Start in background
|
||||
isInForeground = NO;
|
||||
|
||||
lastNotification = nil;
|
||||
applicationName = nil;
|
||||
notificationType = nil;
|
||||
registrationDictionary = nil;
|
||||
interfaceThread = thread;
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
- (void)currentInputDidChanged:(input_thread_t *)input
|
||||
{
|
||||
// Clear the remaining lastNotification in Notification Center, if any
|
||||
@autoreleasepool {
|
||||
if (lastNotification) {
|
||||
[NSUserNotificationCenter.defaultUserNotificationCenter
|
||||
removeDeliveredNotification:(NSUserNotification *)lastNotification];
|
||||
[lastNotification release];
|
||||
}
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
if (!input)
|
||||
return;
|
||||
|
||||
input_item_t *item = input_GetItem(input);
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
// Get title, first try now playing
|
||||
NSString *title = CharsToNSString(input_item_GetNowPlayingFb(item));
|
||||
|
||||
// Fallback to item title or name
|
||||
if ([title length] == 0)
|
||||
title = CharsToNSString(input_item_GetTitleFbName(item));
|
||||
|
||||
// If there is still not title, do not notify
|
||||
if (unlikely([title length] == 0))
|
||||
return;
|
||||
|
||||
// Get artist name
|
||||
NSString *artist = CharsToNSString(input_item_GetArtist(item));
|
||||
|
||||
// Get album name
|
||||
NSString *album = CharsToNSString(input_item_GetAlbum(item));
|
||||
|
||||
// Get coverart path
|
||||
NSString *artPath = nil;
|
||||
|
||||
char *psz_arturl = input_item_GetArtURL(item);
|
||||
if (psz_arturl) {
|
||||
artPath = CharsToNSString(vlc_uri2path(psz_arturl));
|
||||
free(psz_arturl);
|
||||
}
|
||||
|
||||
// Release everything
|
||||
[applicationName release];
|
||||
[notificationType release];
|
||||
[registrationDictionary release];
|
||||
[super dealloc];
|
||||
}
|
||||
// Construct final description string
|
||||
NSString *desc = nil;
|
||||
|
||||
- (void)registerToGrowl
|
||||
{
|
||||
@autoreleasepool {
|
||||
applicationName = [[NSString alloc] initWithUTF8String:_( "VLC media player" )];
|
||||
notificationType = [[NSString alloc] initWithUTF8String:_( "New input playing" )];
|
||||
|
||||
NSArray *defaultAndAllNotifications = [NSArray arrayWithObject: notificationType];
|
||||
registrationDictionary = [[NSMutableDictionary alloc] init];
|
||||
[registrationDictionary setObject:defaultAndAllNotifications
|
||||
forKey:GROWL_NOTIFICATIONS_ALL];
|
||||
[registrationDictionary setObject:defaultAndAllNotifications
|
||||
forKey: GROWL_NOTIFICATIONS_DEFAULT];
|
||||
|
||||
[GrowlApplicationBridge setGrowlDelegate:self];
|
||||
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter]
|
||||
setDelegate:(id<NSUserNotificationCenterDelegate>)self];
|
||||
if (artist && album) {
|
||||
desc = [NSString stringWithFormat:@"%@ – %@", artist, album];
|
||||
} else if (artist) {
|
||||
desc = artist;
|
||||
}
|
||||
|
||||
// Notify!
|
||||
[self notifyWithTitle:title description:desc imagePath:artPath];
|
||||
}
|
||||
|
||||
- (void)notifyWithTitle:(const char *)title
|
||||
artist:(const char *)artist
|
||||
album:(const char *)album
|
||||
andArtUrl:(const char *)url
|
||||
{
|
||||
@autoreleasepool {
|
||||
// Do not notify if in foreground
|
||||
if (isInForeground)
|
||||
return;
|
||||
|
||||
// Init Cover
|
||||
NSData *coverImageData = nil;
|
||||
NSImage *coverImage = nil;
|
||||
|
||||
if (url) {
|
||||
coverImageData = [NSData dataWithContentsOfFile:[NSString stringWithUTF8String:url]];
|
||||
coverImage = [[NSImage alloc] initWithData:coverImageData];
|
||||
}
|
||||
|
||||
// Init Track info
|
||||
NSString *titleStr = nil;
|
||||
NSString *artistStr = nil;
|
||||
NSString *albumStr = nil;
|
||||
|
||||
if (title) {
|
||||
titleStr = [NSString stringWithUTF8String:title];
|
||||
} else {
|
||||
// Without title, notification makes no sense, so return here
|
||||
// title should never be empty, but better check than crash.
|
||||
[coverImage release];
|
||||
return;
|
||||
}
|
||||
if (artist)
|
||||
artistStr = [NSString stringWithUTF8String:artist];
|
||||
if (album)
|
||||
albumStr = [NSString stringWithUTF8String:album];
|
||||
|
||||
// Notification stuff
|
||||
if ([GrowlApplicationBridge isGrowlRunning]) {
|
||||
// Make the Growl notification string
|
||||
NSString *desc = nil;
|
||||
|
||||
if (artistStr && albumStr) {
|
||||
desc = [NSString stringWithFormat:@"%@\n%@ [%@]", titleStr, artistStr, albumStr];
|
||||
} else if (artistStr) {
|
||||
desc = [NSString stringWithFormat:@"%@\n%@", titleStr, artistStr];
|
||||
} else {
|
||||
desc = titleStr;
|
||||
}
|
||||
|
||||
// Send notification
|
||||
[GrowlApplicationBridge notifyWithTitle:[NSString stringWithUTF8String:_("Now playing")]
|
||||
description:desc
|
||||
notificationName:notificationType
|
||||
iconData:coverImageData
|
||||
priority:0
|
||||
isSticky:NO
|
||||
clickContext:nil
|
||||
identifier:@"VLCNowPlayingNotification"];
|
||||
} else {
|
||||
// Make the OS X notification and string
|
||||
NSUserNotification *notification = [NSUserNotification new];
|
||||
NSString *desc = nil;
|
||||
|
||||
if (artistStr && albumStr) {
|
||||
desc = [NSString stringWithFormat:@"%@ – %@", artistStr, albumStr];
|
||||
} else if (artistStr) {
|
||||
desc = artistStr;
|
||||
}
|
||||
|
||||
notification.title = titleStr;
|
||||
notification.subtitle = desc;
|
||||
notification.hasActionButton = YES;
|
||||
notification.actionButtonTitle = [NSString stringWithUTF8String:_("Skip")];
|
||||
|
||||
// Private APIs to set cover image, see rdar://23148801
|
||||
// and show action button, see rdar://23148733
|
||||
[notification setValue:coverImage forKey:@"_identityImage"];
|
||||
[notification setValue:@(YES) forKey:@"_showsButtons"];
|
||||
[NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:notification];
|
||||
[notification release];
|
||||
}
|
||||
|
||||
// Release stuff
|
||||
[coverImage release];
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Delegate methods
|
||||
*****************************************************************************/
|
||||
- (NSDictionary *)registrationDictionaryForGrowl
|
||||
{
|
||||
return registrationDictionary;
|
||||
}
|
||||
|
||||
- (NSString *)applicationNameForGrowl
|
||||
{
|
||||
return applicationName;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the applications activity status changes
|
||||
*/
|
||||
- (void)applicationActiveChange:(NSNotification *)n {
|
||||
if (n.name == NSApplicationDidBecomeActiveNotification)
|
||||
isInForeground = YES;
|
||||
@ -411,24 +270,99 @@ static int InputCurrent( vlc_object_t *p_this, const char *psz_var,
|
||||
isInForeground = NO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the user interacts with a notification
|
||||
*/
|
||||
- (void)userNotificationCenter:(NSUserNotificationCenter *)center
|
||||
didActivateNotification:(NSUserNotification *)notification
|
||||
{
|
||||
// Skip to next song
|
||||
// Check if notification button ("Skip") was clicked
|
||||
if (notification.activationType == NSUserNotificationActivationTypeActionButtonClicked) {
|
||||
// Skip to next song
|
||||
playlist_Next(pl_Get(interfaceThread));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when a new notification was delivered
|
||||
*/
|
||||
- (void)userNotificationCenter:(NSUserNotificationCenter *)center
|
||||
didDeliverNotification:(NSUserNotification *)notification
|
||||
{
|
||||
// Only keep the most recent notification in the Notification Center
|
||||
if (lastNotification) {
|
||||
[center removeDeliveredNotification: (NSUserNotification *)lastNotification];
|
||||
[lastNotification release];
|
||||
}
|
||||
[notification retain];
|
||||
if (lastNotification)
|
||||
[center removeDeliveredNotification:lastNotification];
|
||||
|
||||
lastNotification = notification;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a notification to the default user notification center
|
||||
*/
|
||||
- (void)notifyWithTitle:(NSString * _Nonnull)titleText
|
||||
description:(NSString * _Nullable)descriptionText
|
||||
imagePath:(NSString * _Nullable)imagePath
|
||||
{
|
||||
NSImage *image = nil;
|
||||
|
||||
// Load image if any
|
||||
if (imagePath) {
|
||||
image = [[NSImage alloc] initWithContentsOfFile:imagePath];
|
||||
}
|
||||
|
||||
// Create notification
|
||||
NSUserNotification *notification = [NSUserNotification new];
|
||||
|
||||
notification.title = titleText;
|
||||
notification.subtitle = descriptionText;
|
||||
notification.hasActionButton = YES;
|
||||
notification.actionButtonTitle = [NSString stringWithUTF8String:_("Skip")];
|
||||
|
||||
// Try to set private properties
|
||||
@try {
|
||||
// Private API to set cover image, see rdar://23148801
|
||||
[notification setValue:image forKey:@"_identityImage"];
|
||||
// Private API to show action button, see rdar://23148733
|
||||
[notification setValue:@(YES) forKey:@"_showsButtons"];
|
||||
} @catch (NSException *exception) {
|
||||
if (exception.name == NSUndefinedKeyException)
|
||||
NSLog(@"VLC macOS notifcations plugin failed to set private notification values.");
|
||||
else
|
||||
@throw exception;
|
||||
}
|
||||
|
||||
// Send notification
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter]
|
||||
deliverNotification:notification];
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleanup
|
||||
*/
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
// Clear a remaining lastNotification in Notification Center, if any
|
||||
if (lastNotification) {
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter]
|
||||
removeDeliveredNotification:lastNotification];
|
||||
lastNotification = nil;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark VLC Module descriptor
|
||||
|
||||
vlc_module_begin()
|
||||
set_shortname("OSX-Notifications")
|
||||
set_description(N_("macOS notifications plugin"))
|
||||
add_shortcut("growl") // Kept for backwards compatibility
|
||||
set_category(CAT_INTERFACE)
|
||||
set_subcategory(SUBCAT_INTERFACE_CONTROL)
|
||||
set_capability("interface", 0)
|
||||
set_callbacks(Open, Close)
|
||||
vlc_module_end()
|
||||
|
Loading…
Reference in New Issue
Block a user