NightwindDev / Preference-Bundle-Example

Some information about preference bundles and the different cells in there.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Preference Bundle Example

Universal Keys

PostNotification - adds a way for the tweak to communicate with the preference bundle

<key>PostNotification</key>
<string>com.nightwind.prefbundleexampleprefs-updated</string>

height - determines the height of the cell

<key>height</key>
<string>66</string>

id - gives a unique identifier to the cell

<key>id</key>
<string>testCellId</string>

key - unique identifier to the cell, which can later be used when linking the cells to the main tweak file.

<key>key</key>
<string>testCellKey</string>

PSButtonCell

This is a cell that - when pressed, does a certain action. This action can be specified in your XXXRootListController.m file.

Root.plist:

<dict>
	<key>action</key>
	<string>killPhoneApp</string>
	<key>cell</key>
	<string>PSButtonCell</string>
	<key>label</key>
	<string>Kill Phone App</string>
	<key>defaults</key>
	<string>com.nightwind.prefbundleexampleprefs</string>
</dict>

XXXRootListController.m:

- (void)killPhoneApp {
	pid_t pid;
	const char* args[] = {"killall", "MobilePhone", NULL};
	posix_spawn(&pid, ROOT_PATH("/usr/bin/killall"), NULL, NULL, (char* const*)args, NULL);
}

PSEditTextCell

This is a cell where text can be inputted and then later used in your tweak somewhere.

Root.plist:

<dict>
	<key>cell</key>
	<string>PSEditTextCell</string>
	<key>defaults</key>
	<string>com.nightwind.prefbundleexampleprefs</string>
	<key>label</key>
	<string>Text:</string>
	<key>default</key>
	<string>default text</string>
</dict>

PSSecureEditTextCell

This is a cell where text can be inputted, secured in a password-style manner, and then later used in your tweak somewhere.

Root.plist:

<dict>
	<key>cell</key>
	<string>PSSecureEditTextCell</string>
	<key>defaults</key>
	<string>com.nightwind.prefbundleexampleprefs</string>
	<key>label</key>
	<string>Text:</string>
	<key>default</key>
	<string>default text</string>
</dict>

(The screenshot does not pick it up, but there are dots where the normal text characters of the text field should be).

PSEditTextViewCell

This cell is like PSEditTextCell, in the way that it is also an area for text input. Unlike PSEditTextCell, this cell expands the text input area to fit the whole cell.

Root.plist:

<dict>
	<key>cell</key>
	<string>PSEditTextViewCell</string>
	<key>defaults</key>
	<string>com.nightwind.prefbundleexampleprefs</string>
	<key>default</key>
	<string>default text</string>
</dict>

PSGiantCell

This is a cell that is larger than normal cells. This cell can take an action assigned to it, just like PSButtonCell.

Root.plist:

<dict>
	<key>cell</key>
	<string>PSGiantCell</string>
	<key>label</key>
	<string>Test</string>
	<key>action</key>
	<string>respring</string>
</dict>

XXXRootListController.m:

- (void)respring {
	pid_t pid;
	const char* args[] = {"killall", "SpringBoard", NULL};
	posix_spawn(&pid, ROOT_PATH("/usr/bin/killall"), NULL, NULL, (char* const*)args, NULL);
}

PSGiantIconCell

This cell is similar to PSGiantCell in the terms of size, however it has an option to put an icon into it. Your icon should be placed in your Resources folder and should be named accordingly to your plist. This cell also allows for an action, just like the PSGiantCell mentioned above.

Root.plist:

<dict>
	<key>cell</key>
	<string>PSGiantIconCell</string>
	<key>label</key>
	<string>Test</string>
	<key>icon</key>
	<string>testicon.png</string>
	<key>action</key>
	<string>killSettingsApp</string>
</dict>

XXXRootListController.m:

- (void)killSettingsApp {
	pid_t pid;
	const char* args[] = {"killall", "Preferences", NULL};
	posix_spawn(&pid, ROOT_PATH("/usr/bin/killall"), NULL, NULL, (char* const*)args, NULL);
}

In this case, the icon is named testicon.png, so the image in your Resources folder should be named testicon.png as well. If you want to look at the icon in Filza on device, you can find it in /var/jb/Library/PreferenceBundles/YourBundleName.bundle/ on rootless and /Library/PreferenceBundles/YourBundleName.bundle on non-rootless ("rootful").

PSGroupCell

PSGroupCell is a really useful cell that allows for seperation of large clusters of cells.

Root.plist:

<dict>
	<key>cell</key>
	<string>PSGroupCell</string>
	<key>label</key>
	<string>PSGroupCell Test</string>
</dict>

(Pictured here: two PSGiantIconCells, which are the ones with the icons, and PSGroupCells above them).

PSLinkCell

This cell is used to link to a different view controller. For example in this code snippet, the cell leads to PSUIPrefsListController which is the main Settings app page.

Root.plist:

<dict>
	<key>cell</key>
	<string>PSLinkCell</string>
	<key>detail</key>
	<string>PSUIPrefsListController</string>
	<key>icon</key>
	<string>icon.png</string>
	<key>isController</key>
	<true/>
	<key>label</key>
	<string>test label</string>
</dict>

PSLinkListCell

This cell is used to link to a predefined view controller, which has the cells inside of it defined here.

Root.plist:

<dict>
	<key>cell</key>
	<string>PSLinkListCell</string>
	<key>detail</key>
	<string>PSListItemsController</string>
	<key>label</key>
	<string>Test 1</string>
	<key>validTitles</key>
	<array>
		<string>List Test 1</string>
		<string>List Test 2</string>
	</array>
	<key>validValues</key>
	<array>
		<integer>0</integer>
		<integer>1</integer>
	</array>
</dict>

PSSegmentCell

This is a segmented cell, which can have multiple values inputted into it.

Root.plist:

<dict>
	<key>cell</key>
	<string>PSSegmentCell</string>
	<key>default</key>
	<integer>0</integer>
	<key>label</key>
	<string>Test</string>
	<key>validTitles</key>
	<array>
		<string>test 1</string>
		<string>test 2</string>
		<string>test 3</string>
	</array>
	<key>validValues</key>
	<array>
		<integer>0</integer>
		<integer>1</integer>
		<integer>2</integer>
	</array>
</dict>

PSSliderCell

This is a cell which contains a slider. The slider can be dragged and have multiple output values based on where the knob is located.

Root.plist:

<dict>
	<key>cell</key>
	<string>PSSliderCell</string>
	<key>default</key>
	<real>66</real>
	<key>min</key>
	<integer>0</integer>
	<key>max</key>
	<integer>50</integer>
</dict>

PSSpinnerCell

This cell is a cell which has a spinner inside of it. This cell is meant to be inserted before an actual cell is loaded and then deleted.

Root.plist:

<dict>
	<key>cell</key>
	<string>PSSpinnerCell</string>
	<key>label</key>
	<string>Test</string>
</dict>

PSStaticTextCell

This is a cell which just has text.

Root.plist:

<dict>
	<key>cell</key>
	<string>PSStaticTextCell</string>
	<key>label</key>
	<string>Test</string>
</dict>

PSSwitchCell

This is a cell which has a switch in it, and the value of the switch can be used in the tweak.

Root.plist:

<dict>
	<key>cell</key>
	<string>PSSwitchCell</string>
	<key>default</key>
	<true/>
	<key>label</key>
	<string>Test</string>
</dict>


Linking Cells to Tweak

The code below shoould be put above %hooks and, if present, %groups as well.

static BOOL testSwitchKey; // PSSwitchCell
static NSInteger testSegmentkey; // PSSegmentCell
static NSInteger testSliderKey; // PSSliderCell
static NSString *testEditTextKey; // PSEditTextCell or PSSecureEditTextCell

The code below should be put in the main Tweak.x/Tweak.xm file.

static void preferencesChanged() {
	NSUserDefaults *const prefs = [[NSUserDefaults alloc] initWithSuiteName:@"com.nightwind.prefbundleexampleprefs"];

	testSwitchKey = [prefs objectForKey:@"testSwitchKey"] ? [prefs boolForKey:@"testSwitchKey"] : YES; // PSSwitchCell
	testSegmentKey = [prefs objectForKey:@"testSegmentKey"] ? [prefs integerForKey:@"testSegmentKey"] : 0; // PSSegmentCell
	testSliderKey = [prefs objectForKey:@"testSliderKey"] ? [prefs floatForKey:@"testSliderKey"] : 30.0f; // PSSliderCell
	testEditTextKey = [prefs objectForKey:@"testEditTextKey"] ? [prefs stringForKey:@"testEditTextKey"] : @""; // PSEditTextCell or PSSecureEditTextCell
}

%ctor {
	preferencesChanged();

	CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, (CFNotificationCallback)preferencesChanged, CFSTR("com.nightwind.prefbundleexampleprefs-updated"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
}

The key in the Tweak.x/Tweak.xm file should of course correspond to the key in the .plist file.

So for example say there's a enable tweak switch in the preference bundle, which looks like this:

<dict>
	<key>cell</key>
	<string>PSSwitchCell</string>
	<key>default</key>
	<true/>
	<key>label</key>
	<string>Enable tweak</string>
	<key>key</key>
	<string>tweakEnabled</string>
	<key>PostNotification</key>
	<string>com.nightwind.prefbundleexampleprefs-updated</string>
</dict>

In the Tweak.x/Tweak.xm file, there should be this at the top:

static BOOL tweakEnabled;

...and this at the bottom:

tweakEnabled = [prefs objectForKey:@"tweakEnabled"] ? [prefs boolForKey:@"tweakEnabled"] : YES;

Note: If the default in the switch in the preference bundle is true, then the default should be YES in the Tweak.x/Tweak.xm file as well. If the default is set to false, then the default in the Tweak.x/Tweak.xm file should be NO.

Root.plist:

<key>default</key>
<true/>

Tweak.x:

tweakEnabled = [prefs objectForKey:@"tweakEnabled"] ? [prefs boolForKey:@"tweakEnabled"] : YES;

It says YES at the very end so that corresponds to the .plist file.

Further Information

https://theapplewiki.com/wiki/Dev:Preferences_specifier_plist

About

Some information about preference bundles and the different cells in there.