I've been trying to use SBSettings toggles to turn things on or off--like Airplane Mode, WiFi, SSH, etc--but I just can't figure out why my code works for some of these toggles and not all. Granted, I'm only talking about "simple" toggles; not toggles that bring up their own window with their own controls like for volume or brightness. I've been able to successfully turn on/off 3G, data, my ringer, the mywi toggle... things like that, but I can't figure why some of the toggles--Airplane Mode, Bluetooth, WiFi--on my phone won't respond to the same code that works with the other things mentioned above.
Here's some of the code I'm using:
//.h
#import <UIKit/UIKit.h>
#import <sys/stat.h>
#include "/usr/include/dlfcn.h"
typedef enum toggleTypes
{
SIMPLE,
NOT_SIMPLE
} ToggleTypes;
typedef bool (*BoolFn)();
typedef void (*VoidBoolFn)(bool b);
@interface Toggle : NSObject
@property (strong, nonatomic) NSString *toggleName;
@property (nonatomic) ToggleTypes toggleType;
- (Toggle *) initWithFullPath:(NSString *) togglePath;
- (BOOL) isEnabled;
- (BOOL) isCapable;
- (BOOL) getStateFast;
- (void) setState:(BOOL) state;
@end
//.m
@implementation Toggle {
@private
void *_dylibHandle;
BoolFn _isCapable;
BoolFn _isEnabled;
BoolFn _getStateFast;
VoidBoolFn _setState;
}
@synthesize toggleName = _toggleName;
@synthesize toggleType = _toggleType;
- (Toggle *) initWithFullPath:(NSString *) togglePath
{
self.toggleName = [[togglePath stringByDeletingLastPathComponent] lastPathComponent];
const char *fullName = [togglePath UTF8String];
struct stat fstat;
if( stat(fullName, &fstat) != 0 )
{
NSLog(@"Reading error for file %s", fullName);
return nil;
}
dlerror();
_dylibHandle = dlopen(fullName, RTLD_LAZY | RTLD_LOCAL );
if( !_dylibHandle)
{
NSLog(@"dlopen encountered an error and did not open file: %s", fullName);
return nil;
}
dlerror();
_isCapable = dlsym(_dylibHandle, "isCapable");
char *error = dlerror();
if( !_isCapable )
{
NSLog(@"An error was encountered while loading symbol \"isCapable\"\nFile: %s\nError: %s", fullName, error);
return nil;
}
_isEnabled = dlsym(_dylibHandle, "isEnabled");
error = dlerror();
if( !_isCapable )
{
NSLog(@"An error was encountered while loading symbol \"isEnabled\"\nFile: %s\nError: %s", fullName, error);
return nil;
}
_getStateFast = dlsym(_dylibHandle, "getStateFast");
error = dlerror();
if( !_isCapable )
{
NSLog(@"An error was encountered while loading symbol \"getStateFast\"\nFile: %s\nError: %s", fullName, error);
return nil;
}
_setState = dlsym(_dylibHandle, "setState");
error = dlerror();
if( !_isCapable )
{
NSLog(@"An error was encountered while loading symbol \"getStateFast\"\nFile: %s\nError: %s", fullName, error);
return nil;
}
NSArray *windowsBefore = [[[[UIApplication sharedApplication] windows] objectAtIndex:0] subviews];
[self setState:[self isEnabled]];
NSArray *windowsAfter = [[[[UIApplication sharedApplication] windows] objectAtIndex:0] subviews];
if( [windowsAfter count] > [windowsBefore count] ) {
self.toggleType = NOT_SIMPLE;
for (UIView *view in windowsAfter) {
if( ![windowsBefore containsObject:view] )
[view removeFromSuperview];
}
} else {
self.toggleType = SIMPLE;
}
return self;
}
- (BOOL) isEnabled
{
return _isEnabled();
}
- (BOOL) isCapable
{
return _isCapable();
}
- (BOOL) getStateFast
{
return _getStateFast();
}
- (void) setState:(BOOL) state
{
_setState(state);
}
- (void) dealloc
{
dlclose(_dylibHandle);
}
@end
The code that calls initWithFullPath
:
if( [directoryToggleNames objectForKey:toggleName] == nil ) {
if ((strlen(SBTOGGLES_PATH) + strlen([toggleName UTF8String] + strlen("/Toggle.dylib")) + 1) > _POSIX_PATH_MAX) {
NSLog(@"Toggle %@ has a path name that is too long", toggleName);
return nil;
}
char fullName[_POSIX_PATH_MAX + 1];
strcpy(fullName, SBTOGGLES_PATH);
strcat(fullName, [toggleName UTF8String]);
strcat(fullName, "/Toggle.dylib");
NSString *fullPath = [NSString stringWithUTF8String:fullName];
return [[OSToggle alloc] initWithFullPath:fullPath];
}
return [[OSToggle alloc] initWithFullPath:[directoryToggleNames objectForKey:toggleName]];
The code that uses the code above and the toggle class itself:
ToggleScanner *scanner = [ToggleScanner getInstance];
NSDictionary *toggleDict = [self getToggleDictionary];
for (ToggleBase *t in [toggleDict allValues] ) {
OSToggle *toggle = [scanner getToggleByName:t.name];
if( [t.type isEqualToString:@"simple"] ) {
NSLog(@"isCapable: %@; isEnabled: %@", ( [toggle isCapable] ? @"YES" : @"NO" ), ( [toggle isEnabled] ? @"YES" : @"NO" ));
[toggle setState:t.state.boolValue];
NSLog(@"Set toggle, %@, to %@; result isEnabled: %@", t.name, ( t.state.boolValue ? @"YES" : @"NO" ), ( [toggle isEnabled] ? @"YES" : @"NO" ) );
} else {
NSLog(@"Toggle is not a simple type");
}
}
ToggleScanner just looks in the SBSettings toggle directory and builds a dictionary based on the toggles it finds there. getToggleDict is a method that takes a NSData object out of CoreData and turns it into a set of toggles and state that I can, in turn, execute. ToggleBase is just an information holding class; it has no methods.
Its rough, but it works on some of the toggles. The question is: Is there something I'm doing wrong here? Both the Airplane Mode and the 3G toggle are simple on/off switches, yet this code works on the 3G toggle but not the Airplane Mode one. Any ideas, comments, and/or suggestions as to why this is?
Edit 1: Added code that calls the toggle class and some descriptions about it.