Libactivator: Difference between revisions

From iPhone Development Wiki
m (Formatting)
 
(23 intermediate revisions by 8 users not shown)
Line 1: Line 1:
{{DISPLAYTITLE:libactivator}}
{{Infobox Package
{{Infobox Package
|developer=[[User:Rpetrich|Ryan Petrich]]
|developer=[[User:Rpetrich|Ryan Petrich]]
|version=1.8.3
|version=1.9.0
|package=libactivator
|package=libactivator
}}
}}
Line 10: Line 11:


== How to use this library ==
== How to use this library ==
Headers are available from [https://github.com/rpetrich/libactivator Activator's GitHub project], and the library can be found at <code>/usr/lib/libactivator.dylib</code> on a device where Activator is installed. If using Theos, place the headers in <code>$THEOS/include/libactivator</code>, the library in <code>$THEOS/lib/</code> and add <code>activator</code> to the <code>XXX_LIBRARIES</code> Makefile variable. See [[#Packaging]] for more usage advice.


= Observing Events (via LAListener) =
Headers are available from [https://github.com/rpetrich/libactivator Activator's GitHub project], and the library can be found at <code>/usr/lib/libactivator.dylib</code> on a device where Activator is installed. If using Theos, place the headers in <code>$THEOS/include/libactivator</code>, the library in <code>$THEOS/lib/</code>.  
There are three steps to follow: adding a plist, implementing the code, and allowing users to change activation methods. Optionally, you can also add a method to register your plugin for a certain event on installation, which allows you to implement libactivator without confusing prior users.


== Describing an Action ==
=== Include directive ===


=== Method One: No Code ===
<source lang="objc">
Create a file in the directory <code>/Library/Activator/Listeners/com.your.packageid/</code> named <code>Info.plist</code> with the following contents:
#import <libactivator/libactivator.h>
</source>
 
=== Makefile ===
 
Add to your Makefile:
 
* <code>activator</code> to the <code>XXX_LIBRARIES</code> variable.
 
=== Packaging ===
 
Packages that provide Activator actions or events (especially if this is their primary purpose) should set up a dependency on the earliest version of Activator they support, add to your package's control file:
 
* <code>, mobilesubstrate (>= 0.9.5000), libactivator (>= 1.8.3)</code> to the <code>Depends</code> field.
 
Packages that work without Activator installed but still provide some level of Activator integration should conflict with older versions of Activator that they are untested with, add to your package's control file:
 
* <code>, mobilesubstrate (>= 0.9.5000)</code> to the <code>Depends</code> field.
* <code>, libactivator (< 1.8.3)</code> to the <code>Conflicts</code> field.
 
== Observing Events (via LAListener) ==
 
There are three steps to follow: describing the action, implementing the code, and allowing users to change activation methods. Optionally, you can also add a method to register your plugin for a certain event on installation, which allows you to implement libactivator without confusing prior users.
 
Listeners can only be registered within SpringBoard so if you need to operate within an application you will need to divide your project into a listener that acts as server and a client that receives data from the server. Use the [[IPC]] method that suits best for your necessities.
 
=== Describing an Action ===
 
Provide information about your listener to libactivator by specifying the group it belongs to, its name and a short description, along with metadata and an icon.
 
==== Title and Description ====
 
Provide all the textual information about your listener. You can do so by using a plist or a tweak-like package.
 
===== Method One: No Code =====
 
Create a file in the directory <code>/Library/Activator/Listeners/your.listener.identifier/</code> named <code>Info.plist</code> with the following contents:
<source lang="javascript">
<source lang="javascript">
{
{
Line 28: Line 63:
</source>
</source>


You can put a 29x29 pixels <code>icon-small.png</code> and <code>[email protected]</code> in the same directory to show an icon in the settings pane.
''To do: add a nice table specifying all the valid keys.''
 
===== Method Two: Some Code =====


=== Method Two: Some Code ===
The <code>LAListener</code> protocol contains a number of optional methods that are queried by Activator. These can be used if the exact title and description of listeners aren't known until runtime.
The <code>LAListener</code> protocol contains a number of optional methods that are queried by Activator. These can be used if the exact title and description of listeners aren't known until runtime.


Line 48: Line 84:
</source>
</source>


== Implementing the Code ==
''To do: add more API methods, and a short commented description about them.''
 
=== Icon ===
 
Provide a visual description of your listener. There are two available options to do so: PNG or PDF.
 
===== Method One: No Code =====
 
Place your icons in <code>/Library/Activator/Listeners/your.listener.identifier/</code>
 
For the PNG, make a 29x29 pixels image named <code>icon-small.png</code>. Optionally an @2x version, <code>[email protected]</code>.
 
For the PDF, make a vector image named <code>glyph.pdf</code>.
 
===== Method Two: Some Code =====
 
The <code>LAListener</code> protocol contains a number of optional methods that are queried by Activator. These can be used if the icon images of listeners aren't known until runtime.
 
<source lang="objc">
//  Fast path that supports scale
- (NSData *)activator:(LAActivator *)activator requiresIconDataForListenerName:(NSString *)listenerName scale:(CGFloat *)scale;
- (NSData *)activator:(LAActivator *)activator requiresSmallIconDataForListenerName:(NSString *)listenerName scale:(CGFloat *)scale;
//  Legacy
- (NSData *)activator:(LAActivator *)activator requiresIconDataForListenerName:(NSString *)listenerName;
- (NSData *)activator:(LAActivator *)activator requiresSmallIconDataForListenerName:(NSString *)listenerName;
//  For cases where PNG data isn't available quickly
- (UIImage *)activator:(LAActivator *)activator requiresIconForListenerName:(NSString *)listenerName scale:(CGFloat)scale;
- (UIImage *)activator:(LAActivator *)activator requiresSmallIconForListenerName:(NSString *)listenerName scale:(CGFloat)scale;
 
- (id)activator:(LAActivator *)activator requiresGlyphImageDescriptorForListenerName:(NSString *)listenerName;
</source>
 
''To do: explain image-provider API.''
 
=== Receiving Raw Events ===
 
Receiving raw events causes the original event behaviour to be suppressed, even in cases where it could leave users "trapped" inside of the application. Listeners that support receiving raw event should specify 'receives-raw-events' = 1; in their listener's Info.plist. Most listeners '''should not''' need to receive raw events.
 
[https://github.com/akeaswaran/ReachWeather/commit/ec0abf61#commitcomment-14259377 rpetrich comments on this and tells us the proper way to do it.]
 
=== Implementing the Code ===
 
Implementing the code can be done in two different methods, depending on your plugin. If you have an object that is always in memory, you can use method one, otherwise, use method two.
Implementing the code can be done in two different methods, depending on your plugin. If you have an object that is always in memory, you can use method one, otherwise, use method two.


=== Method One: Add the Code ===
==== Method One: Add the Code ====
 
First, <code>#import <libactivator/libactivator.h></code> and have your class implement the LAListener protocol.
First, <code>#import <libactivator/libactivator.h></code> and have your class implement the LAListener protocol.


To register for events, you must add a piece of code to your init method, replacing the parts as needed:
To register for events, you must add a piece of code to your init method, replacing the parts as needed:
<source lang="objc">
<source lang="objc">
[LASharedActivator registerListener:YOUR_SHARED_OBJECT forName:@"com.your.packageid"];
[LASharedActivator registerListener:YOUR_SHARED_OBJECT forName:@"your.listener.identifier"];
</source>
</source>


Then, you must also implement two simple delegate methods:
Then, you must also implement two simple delegate methods:
<source lang="objc">
<source lang="objc">
- (void)activator:(LAActivator *)activator receiveEvent:(LAEvent *)event;
- (void)activator:(LAActivator *)activator receiveEvent:(LAEvent *)event;
- (void)activator:(LAActivator *)activator abortEvent:(LAEvent *)event;
- (void)activator:(LAActivator *)activator abortEvent:(LAEvent *)event;
</source>
</source>
In the first method, you should first check if your plugin is already active. If it is active, you should deactivate your plugin and return. Otherwise, just activate your plugin. In addition, in the activator:recieveEvent: method, you must call [event setHandled:YES] if you wish to disable the default OS action for that activation event. If you do not set handled then receive event may be called twice for the same event. In the second method you should simply deactivate your plugin.  
In the first method, you should first check if your plugin is already active. If it is active, you should deactivate your plugin and return. Otherwise, just activate your plugin. In addition, in the activator:recieveEvent: method, you must call [event setHandled:YES] if you wish to disable the default OS action for that activation event. If you do not set handled then receive event may be called twice for the same event. In the second method you should simply deactivate your plugin.  


The implementation of these methods is left completely up to the programmer. The LAEvent objects can be used to discover more information about the event, such as the type, if you wish to perform a different action based on the type of event. Do not use that information to disable certain types of events for your plugin!
The implementation of these methods is left completely up to the programmer. The LAEvent objects can be used to discover more information about the event, such as the type, if you wish to perform a different action based on the type of event. Use the event's <code>userInfo</code> dictionary to get extra information. Do not use that information to disable certain types of events for your plugin!
 
==== Method Two: New Object ====


=== Method Two: New Object ===
The second method of implementing '''libactivator''' is to insert a new class in your code, one instance of which is always initialized, and informs your main classes when an activation event has occurred (so your main plugin can activate). A sample class is provided below with placeholder comments where additional code would be needed:
The second method of implementing '''libactivator''' is to insert a new class in your code, one instance of which is always initialized, and informs your main classes when an activation event has occurred (so your main plugin can activate). A sample class is provided below with placeholder comments where additional code would be needed:
<source lang="objc">
<source lang="objc">
#import <libactivator/libactivator.h>
#import <libactivator/libactivator.h>
Line 79: Line 161:
@implementation LAExample
@implementation LAExample


- (void)activator:(LAActivator *)activator receiveEvent:(LAEvent *)event
- (void)activator:(LAActivator *)activator receiveEvent:(LAEvent *)event {
{
if (/* your plugin is activated */) {
if (/* your plugin is activated */) {
// Dismiss your plugin
// Dismiss your plugin
Line 91: Line 172:
}
}


- (void)activator:(LAActivator *)activator abortEvent:(LAEvent *)event
- (void)activator:(LAActivator *)activator abortEvent:(LAEvent *)event {
{
// Dismiss your plugin
// Dismiss your plugin
}
}


+ (void)load
+ (void)load {
{
if ([LASharedActivator isRunningInsideSpringBoard]) {
if ([LASharedActivator isRunningInsideSpringBoard]) {
[LASharedActivator registerListener:[self new] forName:@"com.your.packageid"];
[LASharedActivator registerListener:[self new] forName:@"your.listener.identifier"];
}
}
}
}
Line 106: Line 185:
</source>
</source>


== Allowing Activator Assignment from a Settings Pane ==
==== Default Activation Methods ====
 
To implement default activation methods, call <code>assignEvent:toListenerWithName:</code> before registering your listener:
 
<source lang="objc">
if (![LASharedActivator hasSeenListenerWithName:@"your.listener.identifier"]) {
[LASharedActivator assignEvent:[LAEvent eventWithName:@"libactivator.motion.shake"] toListenerWithName:@"your.listener.identifier"];
}
</source>
 
=== Allowing Activator Assignment from a Settings Pane ===
 
{{main|PreferenceLoader}}
 
''Modern versions of Activator (1.1 and later) support assigning actions from the settings pane provided via Activator. This section provided for packages that wish to provide integration with their existing settings panes or apps.''
''Modern versions of Activator (1.1 and later) support assigning actions from the settings pane provided via Activator. This section provided for packages that wish to provide integration with their existing settings panes or apps.''
==== Simple Method ====


The simplest method to allow users to change activation methods is if you use [[PreferenceLoader]] and a simple plist format. Then, you can just paste in this code to create a cell that when tapped will allow users to select an activation method (again replacing the package id with the correct one):
The simplest method to allow users to change activation methods is if you use [[PreferenceLoader]] and a simple plist format. Then, you can just paste in this code to create a cell that when tapped will allow users to select an activation method (again replacing the package id with the correct one):
<source lang="xml">
<source lang="xml">
<dict>
<dict>
    <key>cell</key>
<key>cell</key>
    <string>PSLinkCell</string>
<string>PSLinkCell</string>
    <key>label</key>
<key>label</key>
    <string>Activation Methods</string>
<string>Activation Methods</string>
    <key>isController</key>
<key>isController</key>
    <true/>
<true/>
    <key>bundle</key>
<key>bundle</key>
    <string>LibActivator</string>
<string>LibActivator</string>
    <key>activatorListener</key>
<key>activatorListener</key>
    <string>com.your.packageid</string>
<string>your.listener.identifier</string>
</dict>
</dict>
</source>
</source>


If you are assigning a [[flipswitch]], it will be: <code>switch-flip.com.your.packageid</code>
If you are assigning a [[flipswitch]], it will be: <code>switch-flip.your.listener.identifier</code>


A more complex method is to integrate the settings pane directly into your app's navigation controller:
==== Advanced Method ====
 
A more complex method is to integrate the settings pane directly into your application's navigation controller:


<source lang="objc">
<source lang="objc">
LAListenerSettingsViewController *vc = [[[LAListenerSettingsViewController alloc] init] autorelease];
LAListenerSettingsViewController *vc = [[LAListenerSettingsViewController new] autorelease];
vc.listenerName = @"com.your.packageid";
vc.listenerName = @"your.listener.identifier";
[myNavigationController pushViewController:vc animated:YES];
[myNavigationController pushViewController:vc animated:YES];
</source>
</source>


== Default Activation Methods ==
== Sending Events (via LAEvent) ==
To implement default activation methods, call <code>assignEvent:toListenerWithName:</code> before registering your listener:
<source lang="objc">
+ (void)load
{
if (![LASharedActivator hasSeenListenerWithName:@"com.your.packageid"])
[LASharedActivator assignEvent:[LAEvent eventWithName:@"libactivator.motion.shake"] toListenerWithName:@"com.your.packageid"];
[LASharedActivator registerListener:[[self alloc] init] forName:@"com.your.packageid"];
}
</source>
 
= Sending Events (via LAEvent) =
There are 2 steps to follow: dispatching event and providing metadata.
There are 2 steps to follow: dispatching event and providing metadata.


 
=== Dispatching Events ===
== Dispatching Events ==
Custom events can be sent to assigned listeners by constructing an <code>LAEvent</code> object and passing it to the <code>sendEventToListener:</code> method. Activator will take care of looking up which listeners are assigned and delivering the event to them.
Custom events can be sent to assigned listeners by constructing an <code>LAEvent</code> object and passing it to the <code>sendEventToListener:</code> method. Activator will take care of looking up which listeners are assigned and delivering the event to them.


Line 158: Line 242:


- (void)_awayControllerUnlocked:(id)unlocked {
- (void)_awayControllerUnlocked:(id)unlocked {
         LAEvent *event = [LAEvent eventWithName:@"com.your.packageid.springboard.unlocked" mode:[LASharedActivator currentEventMode]];
         LAEvent *event = [LAEvent eventWithName:@"your.event.identifier" mode:[LASharedActivator currentEventMode]];
         [LASharedActivator sendEventToListener:event];
         [LASharedActivator sendEventToListener:event];
         if (event.handled) {
         if (event.handled) {
Line 171: Line 255:
If the device is locked and one of the assigned listeners does not support receiving events at the lock screen, Activator will attempt to unlock the device. If a passcode is set, the user will be prompted to enter it.
If the device is locked and one of the assigned listeners does not support receiving events at the lock screen, Activator will attempt to unlock the device. If a passcode is set, the user will be prompted to enter it.


== Providing Event Metadata ==
=== Providing Event Metadata ===
Activator requires metadata on which events are possible to allow assignment through the settings pane.
Activator requires metadata on which events are possible to allow assignment through the settings pane.


The simplest way to provide event metadata is to create a subfolder in <code>/Library/Activator/Events</code> with the name of your event, and describe it using an <code>Info.plist</code>:
Create a file in the directory /Library/Activator/Events/your.event.identifier/ named Info.plist with the following contents:


<code>/Library/Activator/Events/com.your.packageid.springboard.unlocked/Info.plist</code>:
<source lang="javascript">
<source lang="javascript">
{
{
Line 205: Line 288:
- (id)init {
- (id)init {
         if ((self = [super init])) {
         if ((self = [super init])) {
                 [LASharedActivator registerEventDataSource:self forEventName:@"com.your.packageid.springboard.unlocked"];
                 [LASharedActivator registerEventDataSource:self forEventName:@"your.event.identifier"];
         }
         }
         return self;
         return self;
Line 211: Line 294:


- (void)dealloc {
- (void)dealloc {
         [LASharedActivator unregisterEventDataSourceWithEventName:@"com.your.packageid.springboard.unlocked"];
         [LASharedActivator unregisterEventDataSourceWithEventName:@"your.event.identifier"];
         [super dealloc];
         [super dealloc];
}
}
Line 231: Line 314:


Registering <code>LAEventDataSource</code>s must be performed from within SpringBoard.
Registering <code>LAEventDataSource</code>s must be performed from within SpringBoard.
= Packaging =
Packages that provide Activator actions or events (especially if this is their primary purpose) should set up a dependency on the earliest version of Activator they support, as part of their package's control file:
<source lang="javascript">
Depends: libactivator (>= 1.8.3), mobilesubstrate
</source>
Packages that work without Activator installed but still provide some level of Activator integration should conflict with older versions of Activator that they are untested with:
<source lang="javascript">
Depends: mobilesubstrate
Conflicts: libactivator (<< 1.8.3)
</source>


== Runtime access to Activator ==
== Runtime access to Activator ==
Line 263: Line 331:
</source>
</source>


= References =
== Examples ==


=== Examples of LAListeners ===
=== LAListeners ===


{| class="wikitable"
{| class="wikitable"
Line 283: Line 351:
| [https://github.com/c-ryan747/SpeakTime SpeakTime]
| [https://github.com/c-ryan747/SpeakTime SpeakTime]
| [https://github.com/c-ryan747 Callum Ryan]
| [https://github.com/c-ryan747 Callum Ryan]
|-
| [https://github.com/Sassoty/theos-examples/tree/master/activatorlistener ActivatorPopup]
| [https://github.com/Sassoty Sassoty]
|}
|}


=== Examples of LAEvents ===
=== LAEvents ===


{| class="wikitable"
{| class="wikitable"
Line 293: Line 364:
|-
|-
| [https://github.com/uroboro/UnlockEvents UnlockEvents]
| [https://github.com/uroboro/UnlockEvents UnlockEvents]
| [https://github.com/uroboro Uroboro]
| [https://github.com/uroboro uroboro]
|-
| [https://github.com/r-plus/GeoEvent-for-Activator GeoEvent for Activator]
| [https://github.com/r-plus/ r_plus]
|-
| [https://github.com/MiloSovoy/Dropped Dropped]
| [https://github.com/MiloSovoy/ James-Isaac-Neutron]
|}
|}


== External links ==


== External links ==
* [http://github.com/rpetrich/libactivator/tree/master/ libactivator GitHub]
* [http://github.com/rpetrich/libactivator/tree/master/ libactivator GitHub]
* [http://github.com/rpetrich/libactivator/blob/master/libactivator.h libactivator.h]
* [http://github.com/rpetrich/libactivator/blob/master/libactivator.h libactivator.h]
* [http://jontelang.com/blog/2016/09/06/activator-101.html Tutorial to get started with Activator]


{{Navbox Library}}
{{Navbox Library}}
[[Category:Directories in /Library]]
[[Category:Directories in /Library]]

Latest revision as of 00:20, 31 January 2020


Libactivator
Cydia Package
Developer Ryan Petrich
Package ID libactivator
Latest Version 1.9.0


libactivator is a library that provides a centralized system of activation methods for all of our jailbroken extensions. To accomplish this, the hooks of the activation methods are in one centralized Substrate plugin, which uses small bundles and preference panes to select the activation method for each plugin.

libactivator works by connecting an event (represented by an LAEvent) to one or multiple actions (represented by LAListener). Developers can create new events or actions to extend the functionality of a device.

How to use this library

Headers are available from Activator's GitHub project, and the library can be found at /usr/lib/libactivator.dylib on a device where Activator is installed. If using Theos, place the headers in $THEOS/include/libactivator, the library in $THEOS/lib/.

Include directive

#import <libactivator/libactivator.h>

Makefile

Add to your Makefile:

  • activator to the XXX_LIBRARIES variable.

Packaging

Packages that provide Activator actions or events (especially if this is their primary purpose) should set up a dependency on the earliest version of Activator they support, add to your package's control file:

  • , mobilesubstrate (>= 0.9.5000), libactivator (>= 1.8.3) to the Depends field.

Packages that work without Activator installed but still provide some level of Activator integration should conflict with older versions of Activator that they are untested with, add to your package's control file:

  • , mobilesubstrate (>= 0.9.5000) to the Depends field.
  • , libactivator (< 1.8.3) to the Conflicts field.

Observing Events (via LAListener)

There are three steps to follow: describing the action, implementing the code, and allowing users to change activation methods. Optionally, you can also add a method to register your plugin for a certain event on installation, which allows you to implement libactivator without confusing prior users.

Listeners can only be registered within SpringBoard so if you need to operate within an application you will need to divide your project into a listener that acts as server and a client that receives data from the server. Use the IPC method that suits best for your necessities.

Describing an Action

Provide information about your listener to libactivator by specifying the group it belongs to, its name and a short description, along with metadata and an icon.

Title and Description

Provide all the textual information about your listener. You can do so by using a plist or a tweak-like package.

Method One: No Code

Create a file in the directory /Library/Activator/Listeners/your.listener.identifier/ named Info.plist with the following contents:

{
    group = "My Listener Group";
    title = "My Listener";
    description = "Human Readable Description";
    "compatible-modes" = ("springboard", "lockscreen", "application");
}

To do: add a nice table specifying all the valid keys.

Method Two: Some Code

The LAListener protocol contains a number of optional methods that are queried by Activator. These can be used if the exact title and description of listeners aren't known until runtime.

- (NSString *)activator:(LAActivator *)activator requiresLocalizedGroupForListenerName:(NSString *)listenerName {
	return @"My Listener Group";
}
- (NSString *)activator:(LAActivator *)activator requiresLocalizedTitleForListenerName:(NSString *)listenerName {
	return @"My Listener";
}
- (NSString *)activator:(LAActivator *)activator requiresLocalizedDescriptionForListenerName:(NSString *)listenerName {
	return @"Human Readable Description";
}
- (NSArray *)activator:(LAActivator *)activator requiresCompatibleEventModesForListenerWithName:(NSString *)listenerName {
	return [NSArray arrayWithObjects:@"springboard", @"lockscreen", @"application", nil];
}

To do: add more API methods, and a short commented description about them.

Icon

Provide a visual description of your listener. There are two available options to do so: PNG or PDF.

Method One: No Code

Place your icons in /Library/Activator/Listeners/your.listener.identifier/

For the PNG, make a 29x29 pixels image named icon-small.png. Optionally an @2x version, [email protected].

For the PDF, make a vector image named glyph.pdf.

Method Two: Some Code

The LAListener protocol contains a number of optional methods that are queried by Activator. These can be used if the icon images of listeners aren't known until runtime.

//  Fast path that supports scale
- (NSData *)activator:(LAActivator *)activator requiresIconDataForListenerName:(NSString *)listenerName scale:(CGFloat *)scale;
- (NSData *)activator:(LAActivator *)activator requiresSmallIconDataForListenerName:(NSString *)listenerName scale:(CGFloat *)scale;
//  Legacy
- (NSData *)activator:(LAActivator *)activator requiresIconDataForListenerName:(NSString *)listenerName;
- (NSData *)activator:(LAActivator *)activator requiresSmallIconDataForListenerName:(NSString *)listenerName;
//  For cases where PNG data isn't available quickly
- (UIImage *)activator:(LAActivator *)activator requiresIconForListenerName:(NSString *)listenerName scale:(CGFloat)scale;
- (UIImage *)activator:(LAActivator *)activator requiresSmallIconForListenerName:(NSString *)listenerName scale:(CGFloat)scale;

- (id)activator:(LAActivator *)activator requiresGlyphImageDescriptorForListenerName:(NSString *)listenerName;

To do: explain image-provider API.

Receiving Raw Events

Receiving raw events causes the original event behaviour to be suppressed, even in cases where it could leave users "trapped" inside of the application. Listeners that support receiving raw event should specify 'receives-raw-events' = 1; in their listener's Info.plist. Most listeners should not need to receive raw events.

rpetrich comments on this and tells us the proper way to do it.

Implementing the Code

Implementing the code can be done in two different methods, depending on your plugin. If you have an object that is always in memory, you can use method one, otherwise, use method two.

Method One: Add the Code

First, #import <libactivator/libactivator.h> and have your class implement the LAListener protocol.

To register for events, you must add a piece of code to your init method, replacing the parts as needed:

[LASharedActivator registerListener:YOUR_SHARED_OBJECT forName:@"your.listener.identifier"];

Then, you must also implement two simple delegate methods:

- (void)activator:(LAActivator *)activator receiveEvent:(LAEvent *)event;
- (void)activator:(LAActivator *)activator abortEvent:(LAEvent *)event;

In the first method, you should first check if your plugin is already active. If it is active, you should deactivate your plugin and return. Otherwise, just activate your plugin. In addition, in the activator:recieveEvent: method, you must call [event setHandled:YES] if you wish to disable the default OS action for that activation event. If you do not set handled then receive event may be called twice for the same event. In the second method you should simply deactivate your plugin.

The implementation of these methods is left completely up to the programmer. The LAEvent objects can be used to discover more information about the event, such as the type, if you wish to perform a different action based on the type of event. Use the event's userInfo dictionary to get extra information. Do not use that information to disable certain types of events for your plugin!

Method Two: New Object

The second method of implementing libactivator is to insert a new class in your code, one instance of which is always initialized, and informs your main classes when an activation event has occurred (so your main plugin can activate). A sample class is provided below with placeholder comments where additional code would be needed:

#import <libactivator/libactivator.h>
#import <UIKit/UIKit.h>

@interface LAExample : NSObject <LAListener>
@end

@implementation LAExample

- (void)activator:(LAActivator *)activator receiveEvent:(LAEvent *)event {
	if (/* your plugin is activated */) {
		// Dismiss your plugin
		return;
	}
	
	// Activate your plugin

	[event setHandled:YES]; // To prevent the default OS implementation
}

- (void)activator:(LAActivator *)activator abortEvent:(LAEvent *)event {
	// Dismiss your plugin
}

+ (void)load {
	if ([LASharedActivator isRunningInsideSpringBoard]) {
		[LASharedActivator registerListener:[self new] forName:@"your.listener.identifier"];
	}
}

@end

Default Activation Methods

To implement default activation methods, call assignEvent:toListenerWithName: before registering your listener:

if (![LASharedActivator hasSeenListenerWithName:@"your.listener.identifier"]) {
	[LASharedActivator assignEvent:[LAEvent eventWithName:@"libactivator.motion.shake"] toListenerWithName:@"your.listener.identifier"];
}

Allowing Activator Assignment from a Settings Pane

Modern versions of Activator (1.1 and later) support assigning actions from the settings pane provided via Activator. This section provided for packages that wish to provide integration with their existing settings panes or apps.

Simple Method

The simplest method to allow users to change activation methods is if you use PreferenceLoader and a simple plist format. Then, you can just paste in this code to create a cell that when tapped will allow users to select an activation method (again replacing the package id with the correct one):

<dict>
	<key>cell</key>
	<string>PSLinkCell</string>
	<key>label</key>
	<string>Activation Methods</string>
	<key>isController</key>
	<true/>
	<key>bundle</key>
	<string>LibActivator</string>
	<key>activatorListener</key>
	<string>your.listener.identifier</string>
</dict>

If you are assigning a flipswitch, it will be: switch-flip.your.listener.identifier

Advanced Method

A more complex method is to integrate the settings pane directly into your application's navigation controller:

LAListenerSettingsViewController *vc = [[LAListenerSettingsViewController new] autorelease];
vc.listenerName = @"your.listener.identifier";
[myNavigationController pushViewController:vc animated:YES];

Sending Events (via LAEvent)

There are 2 steps to follow: dispatching event and providing metadata.

Dispatching Events

Custom events can be sent to assigned listeners by constructing an LAEvent object and passing it to the sendEventToListener: method. Activator will take care of looking up which listeners are assigned and delivering the event to them.

Example event dispatch using the _awayControllerUnlocked: method of SBIconController

%hook SBIconController

- (void)_awayControllerUnlocked:(id)unlocked {
        LAEvent *event = [LAEvent eventWithName:@"your.event.identifier" mode:[LASharedActivator currentEventMode]];
        [LASharedActivator sendEventToListener:event];
        if (event.handled) {
                NSLog(@"Event was handled by an assignment in Activator!");
        }
        %orig();
}

%end

If the device is locked and one of the assigned listeners does not support receiving events at the lock screen, Activator will attempt to unlock the device. If a passcode is set, the user will be prompted to enter it.

Providing Event Metadata

Activator requires metadata on which events are possible to allow assignment through the settings pane.

Create a file in the directory /Library/Activator/Events/your.event.identifier/ named Info.plist with the following contents:

{
	title = "Unlock Succeeded";
	group = "Unlocking";
	description = "Device unlock succeeded";
}

Alternatively, event metadata can be setup by passing an object conforming to LAEventDataSource to LAActivator's registerEventDataSource:forEventName: method. This allows for adding and removing events based on runtime conditions.

Equivalent example, performed in code:

@interface SomeDataSource: NSObject <LAEventDataSource>
@end

@implementation SomeDataSource

static SomeDataSource *myDataSource;

+ (void)load
{
        @autoreleasepool {
                myDataSource = [[SomeDataSource alloc] init];
        }
}

- (id)init {
        if ((self = [super init])) {
                [LASharedActivator registerEventDataSource:self forEventName:@"your.event.identifier"];
        }
        return self;
}

- (void)dealloc {
        [LASharedActivator unregisterEventDataSourceWithEventName:@"your.event.identifier"];
        [super dealloc];
}

- (NSString *)localizedTitleForEventName:(NSString *)eventName {
        return @"Unlock Succeeded";
}

- (NSString *)localizedGroupForEventName:(NSString *)eventName {
        return @"Unlocking";
}

- (NSString *)localizedDescriptionForEventName:(NSString *)eventName {
        return @"Device unlock succeeded";
}

@end

Registering LAEventDataSources must be performed from within SpringBoard.

Runtime access to Activator

If you do not want to depend on libactivator being installed, you can use the following snippet as a starting line, to access Activator APIs:

...
dlopen("/usr/lib/libactivator.dylib", RTLD_LAZY);
Class la = objc_getClass("LAActivator");
if (la) { //libactivator is installed
	...
} else {  //libactivator is not installed
	...
}
...

Examples

LAListeners

Project Author
BrightVol HASHBANG
NowNow Nick Frey
slideback twodayslate
SpeakTime Callum Ryan
ActivatorPopup Sassoty

LAEvents

Project Author
UnlockEvents uroboro
GeoEvent for Activator r_plus
Dropped James-Isaac-Neutron

External links