Search Results for '6.0'
Frappliance 101
/*
Today I am going to start what I hope becomes a frequent series of coding tutorials on creating appliances for the AppleTV 2G. We are going to start this series with a step by step set of instructions on how to make your first frappliance (from scratch!) in Xcode, its going to be nearly identical to the HelloWorld appliance that I am hosting on github, not going to bore you with a bunch of information in this introduction section, lets just dig in!!
Pre-requisites:
Intel Mac Running 10.6.x
Xcode 3.2.x
an AppleTV 2G
Objective-C Experience highly recommended!
*/
Step By Step
File – > New Project
Mac OS X – > Framework & Library – > Bundle
Click on target in Groups & Files
Click Info button
Build Settings:
Architetecture Setting:
Base SDK: Latest iOS
Build Options:
Run Static Analyzer (optional – read section of static anaylzer)
Deployment:
Deployment Location (ON)
Installation Build Products Location: _/Applications/AppleTV.app/Appliances/
Linking:
OpenMP Linker Flags: -undefined dynamic_lookup -fopenmp
Other Linker Flags: -undefined dynamic_lookup
Packaging:
Wrapper Extension: frappliance
Search Paths:
Framework Search Paths: theos/** Classes/**
Header Search Paths: theos/** Classes/**
Done Build Settings.
Frameworks: remove CoreFoundation Framework
Add Foundation.framework
open terminal application
cd {your_project_folder}
git clone git://github.com/DHowett/theos.git
edit theos/Prefix.pch
Change #import <UIKit/UIKit.h>
to #import <BackRow/BackRow.h>
Add nlist.h and BackRow headers to theos/include
download from here
New Empty File: BackRowExtras.h
#import <objc/runtime.h>
template <typename Type_>
static inline Type_ &MSHookIvar(id self, const char *name) {
Ivar ivar(class_getInstanceVariable(object_getClass(self), name));
void *pointer(ivar == NULL ? NULL : reinterpret_cast<char *>(self) + ivar_getOffset(ivar));
return *reinterpret_cast<Type_ *>(pointer);
}
New Obj-C class: HWAppliance.mm (mm is crucial for the ivar access)
Remove All Cocoa Imports!!
/*
### EDITING HWAppliance.h
add our header files (we haven’t created HWBasicMenu yet, but add it anyway) the BackRowExtras.h is used to easily grab private variables through nlist / mobilesubstrate
*/
#import “BackRowExtras.h”
#import “HWBasicMenu.h”
/*
we use this code to grab a _productImage variable from a BRTopShelf view, this is still a throwback from 4-5 months ago when we first delved into frappliance development for the AppleTV 2G, there /should/ be a more elegant way to do this and probably is, for now you have to live with this
, by having access to the “productImage” variable we can use our own when overriding topShelfView in our HWTopShelfController we have the interface and implementation of HWTopShelfController in our Appliance header because its such a short section of code that it would be ridiculous to make ONE more header file or header / implementation file combo for this tiny amount of code
*/
@interface BRTopShelfView (specialAdditions)
- (BRImageControl *)productImage;
@end
@implementation BRTopShelfView (specialAdditions)
- (BRImageControl *)productImage
{
return MSHookIvar<BRImageControl *>(self, “_productImage”); //gimme that private var!!
}
@end
@interface HWTopShelfController : NSObject {
}
- (void)refresh; //4.2.1
- (void)selectCategoryWithIdentifier:(id)identifier;
- (id)topShelfView;
@end
@implementation HWTopShelfController
-(void)refresh
{
//needed for 4.2.1 compat to keep AppleTV.app from endless reboot cycle
}
- (void)selectCategoryWithIdentifier:(id)identifier {
//leave this entirely empty for controllerForIdentifier:args to work in Appliance subclass
}
- (BRTopShelfView *)topShelfView {
BRTopShelfView *topShelf = [[BRTopShelfView alloc] init];
BRImageControl *imageControl = [topShelf productImage]; //this is why we need MSHookIvar to hook the private variable
//BRImage *theImage = [BRImage imageWithPath:[[NSBundle bundleForClass:[HWBasicMenu class]] pathForResource:@”ApplianceIcon” ofType:@”png”]]; // roll your own
BRImage *theImage = [[BRThemeInfo sharedTheme] largeGeniusIconWithReflection];
[imageControl setImage:theImage];
return topShelf;
}
@end
@interface HWAppliance: BRBaseAppliance { //your interface class name may be different!!
HWTopShelfController *_topShelfController;
NSArray *_applianceCategories;
}
@property(nonatomic, readonly, retain) id topShelfController;
@end
/*
### EDITING HWAppliance.mm
Now we are moving on to editing the implementation file where we handle the way the AppleTV main menu interacts with our plugin
these #define’s aren’t really 100% necessary but serve as handy shortcuts to slim down future code, lending to easier legibility
right now HELLO_CAT is our only category listed in our main menu interface, if you want more than one category (most plugins have 3-4 at least) you would add them here
*/
(before implementation)
#define HELLO_ID @“hwHello”
#define HELLO_CAT [BRApplianceCategory categoryWithName:BRLocalizedString(@"Hello World", @"Hello World") identifier:HELLO_ID preferredOrder:0]
after @implementation
@synthesize topShelfController = _topShelfController;
/* meet and potatoes
if you don’t understand obj-c well enough to know how an init function works, please turn back now ;-P
_topShelfController CAN be a complex class filled with different options (like the movie or internet frappliances) Or it can just be a boring simple placeholder (like the ones i always use) and display a graphic. the graphic handling (as you should remember) was handled in the header class.
in our init function we handle the initialization of the _topShelfController (which we only use to display that one image) and our _applianceCategories variable (notice the #define used earlier cutting down the code here)
right now we are JUST using one category (think of the category just as you should, each appliance on the AppleTV has a list of categories, this is where you are handling how many are displayed) so the _applianceCategories = [[NSArray alloc] initWithObjects:HELLO_CAT,nil]; line would be modified after initWithObjects, appending extra categories after HELLO_CAT. (i shouldn’t need to get this granular, and won’t for the remainder of the tutorial)
*/
- (id)init {
if((self = [super init]) != nil) {
_topShelfController = [[HWTopShelfController alloc] init];
_applianceCategories = [[NSArray alloc] initWithObjects:HELLO_CAT,nil];
} return self;
}
/*
Consider this your control center for your main appliance class, whenever you select a category in the main menu, LowTide is going to look here to determine which controller to push onto the stack, right now we only have one category HELLO_CAT, so we are only checking for that particular identifier, you would append extra else if’s on to account for multiple identifiers.
*/
- (id)controllerForIdentifier:(id)identifier args:(id)args
{
id menuController = nil;
if ([identifier isEqualToString:HELLO_ID])
{
/*
HWBasicMenu is just going to be your standard BRMediaMenuController subclass, more often than not this will be the class you will be subclassing for your different category controller classes.
*/
menuController = [[HWBasicMenu alloc] init]; //we haven’t created this class yet
}
return menuController;
}
- (id)applianceCategories {
return _applianceCategories;
}
@end
New Obj-C class: HWBasicMenu.m
/*
### EDITING HWBasicMenu.h
Now we are going to be working on the new controller class that we push on the stack from the main menu from our sole HELLO_CAT category. As mentioned earlier you will predominately be subclassing from BRMediaMenuController with all of your categorically pushed controllers. Many of us have different styles on how to provide a data source to our controllers it doesn’t make one way or the other wrong, if you find another style in other sample code you like, use that instead, (sorry for the tangent)
*/
@interface HWBasicMenu : BRMediaMenuController {
NSMutableArray *_names;
}
//list provider
- (float)heightForRow:(long)row;
- (long)itemCount;
- (id)itemForRow:(long)row;
- (BOOL)rowSelectable:(long)selectable;
- (id)titleForRow:(long)row;
@end
/*
### EDITING HWBasicMenu.m
This class is pretty cut and dry, there are 9 functions and only 5 of them ever need to be tweaked for a new controller: init, previewControlForItem, itemForRow, itemSelected and itemForRow (and of course dealloc if you alloc any global variables that don’t release themselves.)
*/
#import “HWBasicMenu.h”
@implementation HWBasicMenu
- (id) init
{
if((self = [super init]) != nil) {
//NSLog(@”%@ %s”, self, _cmd);
[self setListTitle:@"Hello World!"];
/*
if you wanted to load your own image from your own resources folder you would use something like line 29 and 30, here we are just loading from BRThemeInfo
*/
//NSString *settingsPng = [[NSBundle bundleForClass:[HWBasicMenu class]] pathForResource:@”picture” ofType:@”png”];
//BRImage *sp = [BRImage imageWithPath:settingsPng];
BRImage *sp = [[BRThemeInfo sharedTheme] gearImage];
[self setListIcon:sp horizontalOffset:0.0 kerningFactor:0.15];
//these are our menu options in our new controller
_names = [[NSMutableArray alloc] init];
[_names addObject:@"First Object"];
[_names addObject:@"Second Object"];
[[self list] setDatasource:self];
return ( self );
}
return ( self );
}
/*
these are the previews that are displayed on your left when a menu item has been selected, we use a basic case statement and return different images according to index
*/
- (id)previewControlForItem:(long)item
{
BRImage *theImage = nil;
switch (item) {
case 0: //item one
theImage = [[BRThemeInfo sharedTheme] largeGeniusIconWithReflection];
break;
case 1: //item two
theImage = [[BRThemeInfo sharedTheme] networkPhotosImage];
break;
}
/*
there are more complex suppliers available for previewControllers, this one is just a basic one that returns and image with a reflection, we will delve into more complex examples in future tutorials.
*/
BRImageAndSyncingPreviewController *obj = [[BRImageAndSyncingPreviewController alloc] init];
[obj setImage:theImage];
return ([obj autorelease]);
}
/*
here we handle selecting an object from the list, right now we just log out to the syslog which item was selected, you can obviously be more complex than that
*/
- (void)itemSelected:(long)selected; {
NSDictionary *currentObject = [_names objectAtIndex:selected];
NSLog(@”item selected: %@”, currentObject);
}
/*
Here we handle what kind of items are displayed in our list, we set the title from our _names variable and add different accessories (think of UITableView item accessories, same kind of idea)
*/
- (id)itemForRow:(long)row {
if(row > [_names count])
return nil;
BRMenuItem * result = [[BRMenuItem alloc] init];
NSString *theTitle = [_names objectAtIndex:row];
[result setText:theTitle withAttributes:[[BRThemeInfo sharedTheme] menuItemTextAttributes]];
switch (row) {
case 0:
[result addAccessoryOfType:0];
break;
case 1:
[result addAccessoryOfType:1]; //add a > to the menuItem on the far right.
break;
default:
[result addAccessoryOfType:0];
break;
}
return [result autorelease];
}
- (BOOL)rowSelectable:(long)selectable {
return TRUE;
}
- (float)heightForRow:(long)row {
return 0.0f;
}
- (long)itemCount {
return [_names count];
}
- (id)titleForRow:(long)row {
return [_names objectAtIndex:row];
}
-(void)dealloc
{
[_names release];
[super dealloc];
}
@end
/*
Now we are editing the Info.plist to make sure our plugin loads, some of this stuff may be frivilous but can’t hurt to have it. Make sure the Info.plist file as actually added to the Project as well, by default it may not be selected.
*/
Editing Info.plist
Add object: 0324 key: DTXcode
Add object: All key: FRApplianceDataSourceType
Add array: 3,2 key:UIDeviceFamily
Bundle creator OS Type Code: fnrw
Add array: iPhoneOS key: CFBundleSupportedPlatforms
Add object: iphoneos key: DTPlatformName
Add object: 1 key: FRAppliancePreferedOrderValue
Add bool: NO key: FRHideIfNoCategories
Add bool: YES key: FRRemoteAppliance
Add object: 1 key: LSRequiresIPhoneOS
Add object: 3.0 key: MinimumOSVersion
Add Object: HWAppliance key: Principal class
delete all keys starting with “Plug-in”
/*
Last, but certainly not least, we need to edit English.lproj/InfoPlist.strings to add CFBundleName as the name that actually displays for our plugin in the main menu
*/
Editing English.lproj/InfoPlist.strings
/* Localized versions of Info.plist keys */
CFBundleName = “HelloWorld”;
NSHumanReadableCopyright = “Copyright © 2011, mygreatcompany llc”;
/*
You are done!! the plugin is ready to build and install, with a little extra work you can use theos to build your dpkg file that is necessary to distribute on a repo for AppleTV 2G frappliance development!
for additional research I highly recommend looking at more sample code, tomcool’s github has the legendary SMFramework that is heavily used in the latest nitoTV and is a great source of information to learn more about BackRow.
*/

