Saturday, May 08, 2010

Tutorial: How to Change the Charging Battery Icon on a Jailbreak iPhone with Winterboard

Here's how to make a custom icon to replace the big green battery that displays on the lock screen when your iPhone is charging. This tutorial assumes that you have Jailbreaked your phone and have installed OpenSSH and WinterBoard on your phone (via the Cydia app) and have an SSH client installed on your PC/Mac (such as WinSPC for Windows or Terminal for Mac/Linux).

Step 1 - Create the Theme Directory



  1. Open an SSH connection from your PC to your iPhone and navigate to the root directory

  2. Navigate to Library/Themes. Note that the Themes folder is actually a shortcut, and will take you to the following physical directory: /private/var/stash/Themes.XXXXXX (XXXXXX is a series of letters and numbers that differs on each phone).

  3. Within the /private/var/stash/Themes.XXXXXX folder, create a new folder and name it whatever you want your theme name to be, ending with .theme, for example My Custom Battery.theme

  4. Click into the folder you just created and create a folder named Bundles

  5. Click into the Bundles folder and create a folder named com.apple.springboard


Step 2 - Create Images


The charging battery icon is actually a series of 17 .png files. As the phone charges, the phone displays the proper image.

  1. Find or create a series of 17 images. If they are not already in .png format, save them as such using your favorite image editor such as Photoshop, GIMP (free), or Aviary (free).

  2. The image size of the files should be 264 pixels wide by 129 pixels high. This will ensure that the icon is properly centered and sized on the screen.

  3. The files must be named in series, from BatteryBG_1.png to BatteryBG_17.png

  4. If you do not want a reflection of your icon to show up underneath it on the lock screen, create a fully-black .png file that is 1 pixel wide by 129 pixels high and name it BatteryReflectionMask.png


Step 3 - Move File and Use Your Theme


  1. Using your SSH client, copy all of the .png files into the
    /private/var/stash/Themes.XXXXXX/>My Custom Battery.theme/Bundles/com.apple.springboard directory you created in step 1.

  2. Open the WinterBoard app on your iPhone, and go to the Select Themes section. You should see your new theme at the top of the list.

  3. Select the theme and close WinterBoard. This will cause your phone to respring. After it does, your new theme is in use.

  4. Plug your charging cable into your phone and you'll see your new battery indicator.

  5. Enjoy

Friday, April 09, 2010

Client-Side Validation: How to Validate Controls in Each Pane of an AjaxControlToolkit Accordion

I'm using the AjaxControlToolKit Accordion, and I needed to validate the controls in each pane before allowing one to navigate to the other panes. I wanted everything to be client-side, because I don't want any post-backs until the whole form is validated. Since I didn't have any luck finding any good examples, here's one I put together. It uses the RequiredFieldValidator control as well as the ValidatorCalloutExtender. If you don't like the fly-out validation, just remove the ValidatorCalloutExtender controls as well as the Display="Hidden" attribute in the RequiredFieldValidators.

Sorry about the funky formatting. A copy/paste into a new aspx file should work with no modifications. I also have a downloadable self-contained aspx file. Enjoy.


<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="ajaxToolkit" %>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Accordion</title>
<style type="text/css">
.accordionHeader
{
border: 1px solid #2F4F4F;
color: white;
background-color: #2E4d7B;
font-family: Arial, Sans-Serif;
font-size: 12px;
font-weight: bold;
padding: 5px;
margin-top: 5px;
cursor: pointer;
}

.accordionHeader button
{
cursor: pointer;
}

.accordionContent
{
background-color: #D3DEEF;
border: 1px dashed #2F4F4F;
border-top: none;
padding: 5px;
padding-top: 10px;
}

.accordionLink
{
background-color: #D3DEEF;
color: #ffffff:
}

.invalidField
{
background-color:#fffacd; /* same color as the background of the validation popup */
}
</style>
</head>
<body>
<script type="text/javascript" language="javascript">
var acc;
var header;

function pageLoad() {
// get a reference to the accordion
acc = $find("MyAccordion_AccordionExtender");

/* for each of the panes, remove its click handler, and add our own;
using different methods for each pane would be unnecessary in our example
if we could include parameters in the method name, for example:
$addHandler(header, "click", navigateToPane(0));

we can't do that, so we set up a method name for each one; */
var index = 0;
header = acc.get_Pane(index).header;
$removeHandler(header, "click", acc._headerClickHandler);
$addHandler(header, "click", navigateToNamePane);

index = 1;
header = acc.get_Pane(index).header;
$removeHandler(header, "click", acc._headerClickHandler);
$addHandler(header, "click", navigateToContactPane);

index = 2;
header = acc.get_Pane(index).header;
$removeHandler(header, "click", acc._headerClickHandler);
$addHandler(header, "click", navigateToDemographicsPane);
}

// this function has to be here or there will be an error when leaving the page
function pageUnload() {
$addHandler(header, "click", acc._headerClickHandler);
}

function navigateToPane(paneIndex) {
// get a reference to the accordion
var pane = $find('MyAccordion_AccordionExtender');

// validate the controls in the current pane, using the ValidationGroup
// of the pane, e.g. "grp0" or "grp1", etc; if it's valid, navigate to
// the pane that corresponds to the index passed in
if (Page_ClientValidate('grp' + pane.get_SelectedIndex()))
pane.set_SelectedIndex(paneIndex);
}

function navigateToNamePane() {
navigateToPane(0);
}

function navigateToContactPane() {
navigateToPane(1);
}

function navigateToDemographicsPane() {
navigateToPane(2);
}
</script>

<form id="form1" runat="server">
<ajaxToolkit:ToolkitScriptManager runat="server" ID="ScriptManager1" />

<ajaxToolkit:Accordion ID="MyAccordion" runat="server" HeaderCssClass="accordionHeader"
ContentCssClass="accordionContent" FramesPerSecond="40" TransitionDuration="250"
AutoSize="None" SelectedIndex="0" RequireOpenedPane="false" SuppressHeaderPostbacks="false">
<Panes>
<ajaxToolkit:AccordionPane ID="AccordionPane0" runat="server">
<Header>Name</Header>
<Content>
Last Name: <asp:TextBox ID="txtLastName" runat="server" />
<asp:RequiredFieldValidator ID="reqLastname" runat="server" ValidationGroup="grp0" ControlToValidate="txtLastName" Text="" ErrorMessage="Last Name is required" Display="None" />
<ajaxToolkit:ValidatorCalloutExtender runat="Server" id="vceLastName" TargetControlID="reqLastName" HighlightCssClass="invalidField" />
<br />
First Name: <asp:TextBox ID="txtFirstName" runat="server" />
<asp:RequiredFieldValidator ID="reqFirstName" runat="server" ValidationGroup="grp0" ControlToValidate="txtFirstName" Text="" ErrorMessage="First Name is required" Display="None" />
<ajaxToolkit:ValidatorCalloutExtender runat="Server" id="vceFirstName" TargetControlID="reqFirstName" HighlightCssClass="invalidField" />
<br />

<!-- the "Continue" button isn't necessary; it's just an added item to make navigation a bit more intuitive -->
<button onclick="navigateToPane(1);" style="float:right;" title="Continue to Contact Info">Continue</button>
</Content>
</ajaxToolkit:AccordionPane>
<ajaxToolkit:AccordionPane ID="AccordionPane2" runat="server">
<Header>Contact Info</Header>
<Content>
Phone: <asp:TextBox ID="txtPhone" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator ID="reqPhone" runat="server" ValidationGroup="grp1" ControlToValidate="txtPhone" Text="" ErrorMessage="Phone is required" Display="None" />
<ajaxToolkit:ValidatorCalloutExtender runat="Server" id="vcePhone" TargetControlID="reqPhone" HighlightCssClass="invalidField" />
<br />
Email: <asp:TextBox ID="txtEmail" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator ID="reqEmail" runat="server" ValidationGroup="grp1" ControlToValidate="txtEmail" Text="" ErrorMessage="Eamil is required" Display="None" />
<ajaxToolkit:ValidatorCalloutExtender runat="Server" id="vceEmail" TargetControlID="reqEmail" HighlightCssClass="invalidField" />
<br />

<!-- the "Continue" button isn't necessary; it's just an added item to make navigation a bit more intuitive -->
<button onclick="navigateToPane(2);" style="float:right;" title="Continue to Demographics">Continue</button>
</Content>
</ajaxToolkit:AccordionPane>
<ajaxToolkit:AccordionPane ID="AccordionPane3" runat="server">
<Header>Demographics</Header>
<Content>
Age: <asp:TextBox ID="txtAge" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator ID="reqAge" runat="server" ValidationGroup="grp2" ControlToValidate="txtAge" Text="" ErrorMessage="Age is required" Display="None" />
<ajaxToolkit:ValidatorCalloutExtender runat="Server" id="vce" TargetControlID="reqAge" HighlightCssClass="invalidField" />
<br />
Salary: <asp:TextBox ID="txtSalary" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator ID="reqSalary" runat="server" ValidationGroup="grp2" ControlToValidate="txtSalary" Text="" ErrorMessage="Salary is required" Display="None" />
<ajaxToolkit:ValidatorCalloutExtender runat="Server" id="vceSalary" TargetControlID="reqSalary" HighlightCssClass="invalidField" />
</Content>
</ajaxToolkit:AccordionPane>
</Panes>
</ajaxToolkit:Accordion>
</form>
</body>
</html>

Thursday, January 21, 2010

Christian Hero Quote of the Day

Burn down their synagogues, forbid all that I enumerated earlier, force them to work, and deal harshly with them, as Moses did... If this does not help we must drive them out like mad dogs. - Martin Luther, in his book On the Jews and Their Lies

Tuesday, January 12, 2010

Random Thought of the Day

Don't fret precious, I'm here. Step away from the window and go back to sleep.

Monday, January 11, 2010

Random Thought of the Day

I work like I drink. Alone. Or with a monkey watching.