Libactivator

From iPhone Development Wiki
Jump to: navigation, search
Libactivator
Cydia Package
Developer Ryan Petrich
Package ID libactivator
Latest Version 1.8.3


libactivator is a library used to have 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 MobileSubstrate plugin, that 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.

Contents

How to use this library

Download the headers from here into a local folder named libactivator and grab a copy of the library from your device located at /usr/lib/libactivator.dylib. If using Theos, place the headers folder in $THEOS/include/ and the library in $THEOS/lib/.

In your makefile, add activator to XXX_LIBRARIES, XXX being your tweak name. If it doesn't exist, make a new line and add XXX_LIBRARIES = activator.

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

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

Observing Events (via LAListener)

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

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

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

You can put a 29x29 pixels icon-small.png and icon-small@2x.png in the same directory to show an icon in the settings pane.

Alternatively, LAListener 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.

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:@"com.your.packageid"];

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. 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:@"com.your.packageid"];
	}
}
 
@end

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.

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>com.your.packageid</string>
</dict>

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

LAListenerSettingsViewController *vc = [[[LAListenerSettingsViewController alloc] init] autorelease];
vc.listenerName = @"com.your.packageid";
[myNavigationController pushViewController:vc animated:YES];

Default Activation Methods

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

+ (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"];
}

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:@"com.your.packageid.springboard.unlocked" 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.

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

<code>/Library/Activator/Events/com.your.packageid.springboard.unlocked/Info.plist:

{
	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:@"com.your.packageid.springboard.unlocked"];
        }
        return self;
}
 
- (void)dealloc {
        [LASharedActivator unregisterEventDataSourceWithEventName:@"com.your.packageid.springboard.unlocked"];
        [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.

Packaging

Packages that provide Activator actions or events as their primary purpose should setup a dependency on the earliest version of Activator they support as part of their package's control file:

Depends: libactivator (>= 1.8.3), mobilesubstrate

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:

Depends: mobilesubstrate
Conflicts: libactivator (<< 1.8.3)

Example Projects

Examples of LAListeners

Project Author
BrightVol HASHBANG
NowNow Nick Frey

Examples of LAEvents

Project Author
UnlockEvents Uroboro


External links