Skip to content
Browse files

Initial Comment

After 3 weeks it's (mostly) bug free and finished.
  • Loading branch information...
0 parents commit 5189b854cc16a9d12549fe9420ed504fc21cf773 @freerunnering committed Sep 7, 2011
Showing with 1,761 additions and 0 deletions.
  1. +6 −0 .gitignore
  2. +34 −0 CSApplication.h
  3. +412 −0 CSApplication.m
  4. +80 −0 CSApplicationController.h
  5. +477 −0 CSApplicationController.m
  6. +36 −0 CSResources.h
  7. +164 −0 CSResources.m
  8. +13 −0 CSScrollView.h
  9. +35 −0 CSScrollView.m
  10. +1 −0 CardSwitcher.plist
  11. +9 −0 Makefile
  12. +29 −0 README.md
  13. +223 −0 Tweak.mm
  14. BIN com.iky1e.cardswitcher_0.9.9.1-9_iphoneos-arm.deb
  15. +10 −0 layout/DEBIAN/control
  16. BIN layout/Library/Activator/Listeners/com.iky1e.cardswitcher/CardSwitcher-small.png
  17. BIN layout/Library/Activator/Listeners/com.iky1e.cardswitcher/CardSwitcher-small@2x.png
  18. BIN layout/Library/Activator/Listeners/com.iky1e.cardswitcher/CardSwitcher.png
  19. BIN layout/Library/Activator/Listeners/com.iky1e.cardswitcher/CardSwitcher@2x.png
  20. +4 −0 layout/Library/Activator/Listeners/com.iky1e.cardswitcher/Info.plist
  21. BIN layout/Library/Application Support/CardSwitcher/UIStatusBarStyleBlackOpaque.png
  22. BIN layout/Library/Application Support/CardSwitcher/UIStatusBarStyleDefault.png
  23. BIN layout/Library/Application Support/CardSwitcher/closebox.png
  24. BIN layout/Library/Application Support/CardSwitcher/closebox@2x.png
  25. BIN layout/Library/PreferenceLoader/Preferences/CardSwitcher/CardSwitcher-small.png
  26. BIN layout/Library/PreferenceLoader/Preferences/CardSwitcher/CardSwitcher-small@2x.png
  27. +194 −0 layout/Library/PreferenceLoader/Preferences/CardSwitcher/CardSwitcher.plist
  28. BIN layout/Library/PreferenceLoader/Preferences/CardSwitcher/CardSwitcher.png
  29. BIN layout/Library/PreferenceLoader/Preferences/CardSwitcher/CardSwitcher@2x.png
  30. +34 −0 make.sh
6 .gitignore
@@ -0,0 +1,6 @@
+.theos/*
+_
+obj
+theos
+_/*
+.DS_Store
34 CSApplication.h
@@ -0,0 +1,34 @@
+//
+// PSAppCard.h
+//
+//
+// Created by Kyle Howells on 06/08/2011.
+// Copyright 2011 Howells Apps. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class SBApplication, SBIconBadge;
+
+@interface CSApplication : UIView
+
+@property (nonatomic, retain) UILabel *label;
+@property (nonatomic, retain) UIImageView *icon;
+@property (nonatomic, retain) UIButton *closeBox;
+@property (nonatomic, retain) SBIconBadge *badge;
+@property (nonatomic, retain) UIImageView *snapshot;
+@property (nonatomic, retain) SBApplication *application;
+@property (nonatomic, retain) UIImage *appImage;
+
+-(id)init;
+-(void)loadImages;
+-(void)reset;
+-(id)initWithApplication:(SBApplication*)application;
+-(void)layoutIcon;
+-(void)launch;
+-(void)exit;
+-(void)quitPressed;
+-(void)launchGesture:(UIGestureRecognizer*)gesture;
+-(void)closeGesture:(UIGestureRecognizer*)gesture;
+
+@end
412 CSApplication.m
@@ -0,0 +1,412 @@
+//
+// CSApplication.m
+//
+// Created by Kyle Howells on 06/08/2011.
+// Copyright 2011 Howells Apps. All rights reserved.
+//////////////////////////////////////////
+// 2 best methods, the SBIcon one will likely be gone with iOS 5.0 so I'm noting down the UIImage method.
+// UIImage *icon = [UIImage _applicationIconImageForBundleIdentifier:[app bundleIdentifier] roleIdentifier:[app roleIdentifier] format:0];
+// "format:" & "getIconImage:" need an int. I think that they both use a typedef something like this.
+/// (Note: must check the icons that are same size at some point to see if they really are the same)
+/*
+ * | typedef enum {
+ * | SBIconTypeSmall = 1, // Settings icon (29*29)
+ * | SBIconTypeLarge = 2, // SpringBoard icon (59*62)
+ * | SBIconTypeMedium = 3, // Don't know? (43*43.5)
+ * | SBIconTypeBig = 4, // SpringBoard icon again? (59*62)
+ * | SBIconTypeMiddle = 5, // Slightly bigger settings icon? (31*37)
+ * | SBIconTypeMid = 6, // Same size as 5, might not be same though? (31*37)
+ * | SBIconTypeError = 7 // Calling this gives an error ("[NSCFString size]: unrecognized selector")
+ * | // Anything above 7 returns nil.
+ * | } SBIconType;
+ */
+
+
+/// SpringBoard headers
+#import <SpringBoard4.0/SBAppSwitcherController.h>
+#import <SpringBoard4.0/SBApplicationIcon.h>
+#import <SpringBoard4.0/SBUIController.h>
+#import <SpringBoard4.0/SBDisplayStack.h>
+#import <SpringBoard4.0/SBApplication.h>
+#import <SpringBoard4.0/SBIconBadge.h>
+#import <SpringBoard4.0/SBIconModel.h>
+#import <SpringBoard4.0/SBIcon.h>
+#import "CSApplicationController.h"
+#import <QuartzCore/QuartzCore.h>
+#import "CSApplication.h"
+#import "CSScrollView.h"
+#import <UIKit/UIKit.h>
+#import <substrate.h>
+
+@interface SBApplication ()
+-(int)suspensionType;
+@end
+
+@interface SBProccess : NSObject {}
+-(void)resume;
+@end
+
+@interface SBIcon (PSText)
+-(NSString*)_PSBadgeText;
+@end
+
+
+//#error TODO: Change over UI from the controller to seperate CSApplication objects.
+
+#define APP self.application
+
+
+@implementation CSApplication
+@synthesize label = _label;
+@synthesize icon = _icon;
+@synthesize badge = _badge;
+@synthesize appImage = _appImage;
+@synthesize closeBox = _closeBox;
+@synthesize snapshot = _snapshot;
+@synthesize application = _application;
+
+
+-(id)init{
+ if ((self = [super initWithFrame:CGRectMake(0, 0, (SCREEN_WIDTH*0.625), [UIScreen mainScreen].bounds.size.height)])) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ self.clipsToBounds = NO;
+ // Autoresizing masks
+ self.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
+ self.application = nil;
+ self.appImage = nil;
+
+ self.snapshot = [[[UIImageView alloc] init] autorelease];
+ self.snapshot.backgroundColor = [UIColor clearColor];
+ self.snapshot.frame = CGRectMake(0, (50*Y_SCALE), (SCREEN_WIDTH*0.625), (SCREEN_HEIGHT*0.625));
+ self.snapshot.userInteractionEnabled = YES;
+ self.snapshot.layer.masksToBounds = YES;
+ self.snapshot.layer.cornerRadius = [CSResources cornerRadius];
+ self.snapshot.layer.borderWidth = 1;
+ self.snapshot.layer.borderColor = [UIColor colorWithRed:0.3 green:0.3 blue:0.3 alpha:1].CGColor;
+ [self addSubview:self.snapshot];
+
+ UISwipeGestureRecognizer *swipeUp = [[[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(closeGesture:)] autorelease];
+ swipeUp.direction = UISwipeGestureRecognizerDirectionUp;
+ [self.snapshot addGestureRecognizer:swipeUp];
+
+ UITapGestureRecognizer *singleTap = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(launchGesture:)] autorelease];
+ [singleTap requireGestureRecognizerToFail:swipeUp];
+ singleTap.numberOfTapsRequired = [CSResources tapsToLaunch];
+ [self addGestureRecognizer:singleTap];
+
+ self.icon = [[[UIImageView alloc] init] autorelease];
+ self.icon.frame = CGRectMake(17, self.snapshot.frame.size.height + self.snapshot.frame.origin.y + 14, self.icon.frame.size.width, self.icon.frame.size.height);
+ [self addSubview:self.icon];
+
+ self.closeBox = [UIButton buttonWithType:UIButtonTypeCustom];
+ self.closeBox.frame = CGRectMake(0, 0, 45, 45);
+ self.closeBox.center = self.snapshot.frame.origin;
+ [self.closeBox setImage:[CSApplicationController sharedController].closeBox forState:UIControlStateNormal];
+ [self.closeBox addTarget:self action:@selector(quitPressed) forControlEvents:UIControlEventTouchUpInside];
+ [self addSubview:self.closeBox];
+ self.closeBox.hidden = ![CSResources showsCloseBox];
+
+ CGRect labelRect;
+ labelRect.origin.x = (self.icon.frame.origin.x + self.icon.frame.size.width + 12);
+ labelRect.origin.y = self.icon.frame.origin.y;
+ labelRect.size.width = (self.snapshot.frame.origin.x + self.snapshot.frame.size.width)-(self.icon.frame.size.width + self.icon.frame.origin.x + 10);
+ labelRect.size.height = self.icon.frame.size.height;
+
+ self.label = [[[UILabel alloc] initWithFrame:labelRect] autorelease];
+ self.label.font = [UIFont boldSystemFontOfSize:17];
+ self.label.backgroundColor = [UIColor clearColor];
+ self.label.textColor = [UIColor whiteColor];
+ self.label.numberOfLines = 0;
+ self.label.text = @"Application";
+ [self addSubview:self.label];
+ self.label.hidden = ![CSResources showsAppTitle];
+
+
+ [[CSApplicationController sharedController].scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:@selector(updateAlpha:)];
+ [self addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:@selector(updateAlpha:)];
+
+ [pool release];
+ }
+
+ return self;
+}
+
+-(void)loadImages{
+ if (self.appImage != nil)
+ return;
+
+ self.appImage = [CSResources cachedScreenShot:APP];
+ self.snapshot.image = self.appImage;
+}
+-(void)reset{
+ self.appImage = nil;
+ self.snapshot.image = nil;
+}
+
+-(id)initWithApplication:(SBApplication*)application
+{
+ if ((self = [super initWithFrame:CGRectMake(0, 0, (SCREEN_WIDTH*0.625), [UIScreen mainScreen].bounds.size.height)])) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ self.clipsToBounds = NO;
+ // Autoresizing masks
+ self.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
+ self.application = application;
+ self.appImage = nil;
+
+ self.snapshot = [[[UIImageView alloc] initWithFrame:CGRectMake(0, (50*Y_SCALE), (SCREEN_WIDTH*0.625), (SCREEN_HEIGHT*0.625))] autorelease];
+ self.snapshot.backgroundColor = [UIColor clearColor];
+ self.snapshot.userInteractionEnabled = YES;
+ self.snapshot.layer.masksToBounds = YES;
+ self.snapshot.layer.cornerRadius = [CSResources cornerRadius];
+ self.snapshot.layer.borderWidth = 1;
+ self.snapshot.layer.borderColor = [UIColor colorWithRed:0.3 green:0.3 blue:0.3 alpha:1].CGColor;
+ [self addSubview:self.snapshot];
+
+ UISwipeGestureRecognizer *swipeUp = [[[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(closeGesture:)] autorelease];
+ swipeUp.direction = UISwipeGestureRecognizerDirectionUp;
+ [self.snapshot addGestureRecognizer:swipeUp];
+
+ UITapGestureRecognizer *singleTap = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(launchGesture:)] autorelease];
+ [singleTap requireGestureRecognizerToFail:swipeUp];
+ singleTap.numberOfTapsRequired = [CSResources tapsToLaunch];
+ [self addGestureRecognizer:singleTap];
+
+ SBApplicationIcon *appIcon = [[objc_getClass("SBIconModel") sharedInstance] applicationIconForDisplayIdentifier:[APP displayIdentifier]];
+ UIImage *icon = [appIcon getIconImage:3];
+ self.icon = [[[UIImageView alloc] initWithImage:icon] autorelease];
+ self.icon.frame = CGRectMake(17, self.snapshot.frame.size.height + self.snapshot.frame.origin.y + 14, self.icon.frame.size.width, self.icon.frame.size.height);
+ [self addSubview:self.icon];
+
+ if ([appIcon hasBadge]) {
+ self.badge = [objc_getClass("SBIconBadge") iconBadgeWithBadgeString:[appIcon _PSBadgeText]]; //[[appIcon badgeView] _PSBadgeText]];
+ self.badge.center = CGPointMake((self.snapshot.frame.size.width - (self.badge.frame.size.width*0.2)), self.snapshot.frame.origin.y + (self.badge.frame.size.height*0.2));
+ [self addSubview:self.badge];
+ }
+
+ self.closeBox = [UIButton buttonWithType:UIButtonTypeCustom];
+ self.closeBox.frame = CGRectMake(0, 0, 45, 45);
+ self.closeBox.center = self.snapshot.frame.origin;
+ [self.closeBox setImage:[CSApplicationController sharedController].closeBox forState:UIControlStateNormal];
+ [self.closeBox addTarget:self action:@selector(quitPressed) forControlEvents:UIControlEventTouchUpInside];
+ [self addSubview:self.closeBox];
+ self.closeBox.hidden = ![CSResources showsCloseBox];
+
+ CGRect labelRect;
+ labelRect.origin.x = (self.icon.frame.origin.x + self.icon.frame.size.width + 12);
+ labelRect.origin.y = self.icon.frame.origin.y;
+ labelRect.size.width = (self.snapshot.frame.origin.x + self.snapshot.frame.size.width)-(self.icon.frame.size.width + self.icon.frame.origin.x + 10);
+ labelRect.size.height = self.icon.frame.size.height;
+
+ self.label = [[[UILabel alloc] initWithFrame:labelRect] autorelease];
+ self.label.font = [UIFont boldSystemFontOfSize:17];
+ self.label.backgroundColor = [UIColor clearColor];
+ self.label.textColor = [UIColor whiteColor];
+ self.label.numberOfLines = 0;
+ self.label.text = [APP displayName];
+ [self addSubview:self.label];
+ self.label.hidden = ![CSResources showsAppTitle];
+
+ [self layoutIcon];
+
+ [[CSApplicationController sharedController].scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:@selector(updateAlpha:)];
+ [self addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:@selector(updateAlpha:)];
+
+ [pool release];
+ }
+
+ return self;
+}
+-(void)layoutIcon{
+ if (self.label.hidden) {
+ self.icon.center = CGPointMake(self.center.x, self.icon.center.y);
+ return;
+ }
+
+
+ int totalWidth = [self.label.text sizeWithFont:self.label.font forWidth:self.label.frame.size.width lineBreakMode:UILineBreakModeClip].width;
+ totalWidth += (self.icon.frame.size.width + 12);
+ int iconX = ((self.frame.size.width-totalWidth)*0.5)-10;
+ int labelX = iconX + self.icon.frame.size.width + 8;
+
+ self.icon.frame = CGRectMake(iconX, self.icon.frame.origin.y, self.icon.frame.size.width, self.icon.frame.size.height);
+ self.label.frame = CGRectMake(labelX, self.label.frame.origin.y, self.label.frame.size.width, self.label.frame.size.height);
+}
+
+
+-(void)launch{
+ [self.superview bringSubviewToFront:self];
+ [self bringSubviewToFront:self.snapshot];
+ [CSApplicationController sharedController].scrollView.userInteractionEnabled = NO;
+
+ [UIView animateWithDuration:0.1 animations:^{
+ self.badge.alpha = 0;
+ self.closeBox.alpha = 0;
+ }completion:^(BOOL finished){}];
+
+ // But either way I want my custom animation.
+ CGRect screenRect = self.frame;
+ screenRect.size.width = SCREEN_WIDTH;
+ screenRect.size.height = SCREEN_HEIGHT;
+ screenRect.origin.x = -[CSApplicationController sharedController].scrollView.frame.origin.x - (([CSApplicationController sharedController].scrollView.frame.size.width-self.frame.size.width)*0.5);
+ screenRect.origin.y = -[CSApplicationController sharedController].scrollView.frame.origin.y;
+
+ if ([SBWActiveDisplayStack topApplication] != nil) {
+ // An app is already open, so use the switcher animation, but first check if this is the same app.
+ if (![[[SBWActiveDisplayStack topApplication] bundleIdentifier] isEqualToString:[APP bundleIdentifier]]) {
+ [CSApplicationController sharedController].shouldAnimate = YES;
+ [(SBUIController*)[objc_getClass("SBUIController") sharedInstance] activateApplicationFromSwitcher:APP];
+ }
+ }
+ else {
+ //Else we are on SpringBoard
+ [(SBUIController*)[objc_getClass("SBUIController") sharedInstance] activateApplicationAnimated:APP];
+ }
+
+ [UIView animateWithDuration:0.55 animations:^{
+ self.snapshot.frame = screenRect;
+ self.snapshot.layer.cornerRadius = 0;
+ } completion:^(BOOL finished){
+ [[CSApplicationController sharedController] setActive:NO animated:NO];
+ [self sendSubviewToBack:self.snapshot];
+ }];
+}
+
+-(void)exit{
+ [self retain];
+
+ //[[CSApplicationController sharedController].ignoreRelaunchID release], [CSApplicationController sharedController].ignoreRelaunchID = nil;
+ //[CSApplicationController sharedController].ignoreRelaunchID = [APP.displayIdentifier retain];
+ [[CSApplicationController sharedController] appQuit:APP];
+ [self removeFromSuperview];
+
+
+ //******************* Proper app quiting code thanks to 'jmeosbn' - start **************//
+ int suspendType = [APP respondsToSelector:@selector(_suspensionType)] ? [APP _suspensionType] : [APP suspensionType];
+
+ // Set app to terminate on suspend then call deactivate
+ // Allows exiting root apps, even if already backgrounded,
+ // but does not exit an app with active background tasks
+ [APP setSuspendType:0];
+ [APP deactivate];
+ [[APP process] resume];
+
+ // Restore previous suspend type
+ [APP setSuspendType:suspendType];
+
+ [APP performSelector:@selector(kill) withObject:nil afterDelay:2];
+ //[[objc_getClass("SBAppSwitcherController") sharedInstance] _quitButtonHit:APP];
+
+ //******************* Proper app quiting code thanks to 'jmeosbn' - end **************//
+
+ [self release];
+
+
+ //if ([self hasNativeBackgrounding]) {
+ // [APP kill];
+ //}
+ //else {
+ // UIApplication *sharedApp = [UIApplication sharedApplication];
+ // /*if ([sharedApp respondsToSelector:@selector(setBackgroundingEnabled:forDisplayIdentifier:)])
+ // [sharedApp setBackgroundingEnabled:NO forDisplayIdentifier:_displayIdentifier];*/
+ // if ([SBWActiveDisplayStack containsDisplay:APP]) {
+ // [APP setDeactivationSetting:0x2 flag:YES]; // animate
+ // [SBWActiveDisplayStack popDisplay:APP];
+ // } else {
+ // [APP setDeactivationSetting:0x2 flag:NO]; // don't animate
+ // }
+ // // Deactivate the application
+ // [APP setActivationSetting:0x2 flag:NO]; // don't animate
+ // [SBWSuspendingDisplayStack pushDisplay:APP];
+ //}
+}
+
+
+-(void)quitPressed{
+ [UIView animateWithDuration:0.2 animations:^{
+ self.icon.alpha = 0;
+ self.label.alpha = 0;
+ self.badge.alpha = 0;
+ self.closeBox.alpha = 0;
+ } completion:^(BOOL finished){}];
+
+ [UIView animateWithDuration:0.4 animations:^{
+ self.snapshot.alpha = 0;
+ self.snapshot.frame = CGRectMake(0, -self.snapshot.frame.size.height, self.snapshot.frame.size.width, self.snapshot.frame.size.height);
+ } completion:^(BOOL finished){
+ [self exit];
+ }];
+}
+
+
+-(void)launchGesture:(UITapGestureRecognizer*)gesture{
+ if (gesture.state != UIGestureRecognizerStateEnded)
+ return;
+
+ [self launch];
+}
+
+-(void)closeGesture:(UIGestureRecognizer*)gesture{
+ if (gesture.state != UIGestureRecognizerStateEnded || ![CSResources swipeCloses])
+ return;
+
+ [UIView animateWithDuration:0.2 animations:^{
+ self.icon.alpha = 0;
+ self.label.alpha = 0;
+ self.badge.alpha = 0;
+ self.closeBox.alpha = 0;
+ } completion:^(BOOL finished){}];
+
+ [UIView animateWithDuration:0.4 animations:^{
+ self.snapshot.alpha = 0;
+ self.snapshot.frame = CGRectMake(0, -self.snapshot.frame.size.height, self.snapshot.frame.size.width, self.snapshot.frame.size.height);
+ } completion:^(BOOL finished){
+ [self exit];
+ }];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+ [self performSelector:(SEL)context withObject:change];
+}
+
+- (void)updateAlpha:(NSDictionary *)change {
+ /*CGFloat offset = [CSApplicationController sharedController].scrollView.contentOffset.x;
+ CGFloat origin = self.frame.origin.x;
+ CGFloat delta = fabs(origin - offset);
+
+ if (delta < self.frame.size.width) {
+ self.alpha = 1 - delta/self.frame.size.width*0.8;
+ } else {
+ self.alpha = 0.3;
+ }*/
+
+ if ([[CSApplicationController sharedController].scrollView viewIsVisible:self]) {
+ [self loadImages];
+ }
+ else {
+ [self reset];
+ }
+}
+
+
+-(void)dealloc{
+ [[CSApplicationController sharedController].scrollView removeObserver:self forKeyPath:@"contentOffset"];
+
+ for (UIView *view in self.subviews) {
+ [view removeFromSuperview];
+ }
+
+ self.application = nil;
+ self.snapshot = nil;
+ self.appImage = nil;
+ self.closeBox = nil;
+ self.badge = nil;
+ self.label = nil;
+ self.icon = nil;
+
+ [super dealloc];
+}
+
+
+@end
80 CSApplicationController.h
@@ -0,0 +1,80 @@
+//
+// CSApplicationController.h
+//
+//
+// Created by Kyle Howells on 21/08/2011.
+// Copyright 2011 Howells Apps. All rights reserved.
+//
+
+//#import <SpringBoard4.0/SpringBoard.h>
+#import <SpringBoard4.0/SBApplication.h>
+#import <libactivator/libactivator.h>
+#import <QuartzCore/QuartzCore.h>
+#import <Foundation/Foundation.h>
+#import "CSScrollView.h"
+#import <UIKit/UIKit.h>
+#import "CSResources.h"
+
+@class SpringBoard, CSApplicationController, CSApplication;
+
+#pragma mark Defines
+#define SBWPreActivateDisplayStack [[CSApplicationController sharedController].displayStacks objectAtIndex:0]
+#define SBWActiveDisplayStack [[CSApplicationController sharedController].displayStacks objectAtIndex:1]
+#define SBWSuspendingDisplayStack [[CSApplicationController sharedController].displayStacks objectAtIndex:2]
+#define SBWSuspendedEventOnlyDisplayStack [[CSApplicationController sharedController].displayStacks objectAtIndex:3]
+#define SBActive ([SBWActiveDisplayStack topApplication] == nil)
+#define SPRINGBOARD [CSApplicationController sharedController].springBoard
+#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
+#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
+#define X_SCALE ([UIScreen mainScreen].bounds.size.width/320)
+#define Y_SCALE ([UIScreen mainScreen].bounds.size.height/480)
+
+
+
+@interface CSApplicationController : UIWindow <LAListener, UIScrollViewDelegate> {
+ UILabel *noAppsLabel;
+ UIPageControl *pageControl;
+
+ UIImageView *backgroundView;
+
+ UIInterfaceOrientation currentOrientation;
+}
+
+@property (nonatomic, readwrite) BOOL isActive;
+@property (nonatomic, readwrite) BOOL isAnimating;
+@property (nonatomic, retain) NSString *ignoreRelaunchID;
+@property (nonatomic, readwrite) BOOL shouldAnimate;
+
+@property (nonatomic, retain) NSMutableArray *displayStacks;
+@property (nonatomic, retain) NSMutableArray *ignoredApps;
+@property (nonatomic, retain) NSMutableArray *runningApps;
+@property (nonatomic, retain) NSMutableArray *ignoredIDs;
+
+@property (nonatomic, retain) CSScrollView *scrollView;
+
+@property (nonatomic, assign) SpringBoard *springBoard;
+@property (nonatomic, retain) UIImage *springBoardImage;
+
+@property (nonatomic, retain) UIImage *closeBox;
+@property (nonatomic, retain) UIImage *statusBarDefault;
+
++(CSApplicationController*)sharedController;
+
+-(void)relayoutSubviews;
+-(void)setRotation:(UIInterfaceOrientation)orientation;
+
+-(CSApplication*)csAppforApplication:(SBApplication*)app;
+
+-(void)appLaunched:(SBApplication*)app;
+-(void)appQuit:(SBApplication*)app;
+
+-(void)deactivateGesture:(UIGestureRecognizer*)gesture;
+
+-(void)setActive:(BOOL)active;
+-(void)setActive:(BOOL)active animated:(BOOL)animated;
+-(void)activateAnimated:(BOOL)animate;
+-(void)deactivateAnimated:(BOOL)animate;
+
+-(void)checkPages;
+
+@end
477 CSApplicationController.m
@@ -0,0 +1,477 @@
+//
+// CSApplicationController.m
+//
+//
+// Created by Kyle Howells on 21/08/2011.
+// Copyright 2011 Howells Apps. All rights reserved.
+//
+
+#import <SpringBoard4.0/SBUIController.h>
+#import <SpringBoard4.0/SBDisplayStack.h>
+#import <SpringBoard4.0/SBApplication.h>
+#import "CSApplicationController.h"
+#import "CSApplication.h"
+#import "CSResources.h"
+
+
+CGImageRef UIGetScreenImage(void);
+
+
+@interface SBAwayController : NSObject {}
++(id)sharedAwayController;
+-(BOOL)isLocked;
+@end
+
+@interface SBWallpaperView : UIImageView {}
+-(UIImage*)uncomposedImage;
+@end
+
+@interface SpringBoard (Backgrounder)
+- (void)setBackgroundingEnabled:(BOOL)backgroundingEnabled forDisplayIdentifier:(NSString *)displayIdentifier;
+@end
+
+
+
+static CSApplicationController *_instance;
+
+@implementation CSApplicationController
+@synthesize springBoardImage = _springBoardImage;
+@synthesize ignoreRelaunchID = _ignoreRelaunchID;
+@synthesize statusBarDefault = _statusBarDefault;
+@synthesize displayStacks = _displayStacks;
+@synthesize shouldAnimate = _shouldAnimate;
+@synthesize isAnimating = _isAnimating;
+@synthesize springBoard = _springBoard;
+@synthesize runningApps = _runningApps;
+@synthesize ignoredApps = _ignoredApps;
+@synthesize ignoredIDs = _ignoredIDs;
+@synthesize scrollView = _scrollView;
+@synthesize closeBox = _closeBox;
+@synthesize isActive = _isActive;
+
++(CSApplicationController*)sharedController{
+ if (!_instance) {
+ _instance = [[CSApplicationController alloc] init];
+ }
+
+ return _instance;
+}
+
+- (id)init {
+ if ((self = [super initWithFrame:[UIScreen mainScreen].bounds])) {
+ // Initialization code here.
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ self.closeBox = [UIImage imageWithContentsOfFile:@"/Library/Application Support/CardSwitcher/closebox.png"];
+ self.statusBarDefault = [UIImage imageWithContentsOfFile:@"/Library/Application Support/CardSwitcher/UIStatusBarStyleDefault.png"];
+ self.backgroundColor = [UIColor blackColor];
+ self.windowLevel = UIWindowLevelStatusBar*42;
+ self.shouldAnimate = NO;
+ self.isAnimating = NO;
+ self.isActive = NO;
+ self.hidden = YES;
+ self.displayStacks = [[[NSMutableArray alloc] init] autorelease];
+ self.runningApps = [[[NSMutableArray alloc] init] autorelease];
+ self.ignoredApps = [[[NSMutableArray alloc] init] autorelease];
+ self.ignoredIDs = nil;
+ //[NSMutableArray arrayWithObjects:@"com.apple.mobileipod-MediaPlayer", @"com.apple.mobilephone", @"com.apple.mobilemail", @"com.apple.mobilesafari", nil];
+
+ pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(0.0f, self.frame.size.height - 28.0f, self.frame.size.width, 20.0f)];
+ pageControl.userInteractionEnabled = NO;
+ pageControl.numberOfPages = 1;
+ pageControl.currentPage = 1;
+ [self addSubview:pageControl];
+
+ int edgeInset = 40;
+ CGRect scrollViewFrame = self.bounds;
+ scrollViewFrame.size.width = (self.bounds.size.width-(edgeInset*2));
+ scrollViewFrame.origin.x = edgeInset;
+ self.scrollView = [[[CSScrollView alloc] initWithFrame:scrollViewFrame] autorelease];
+ self.scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ self.scrollView.showsHorizontalScrollIndicator = NO;
+ self.scrollView.showsVerticalScrollIndicator = NO;
+ self.scrollView.alwaysBounceHorizontal = YES;
+ self.scrollView.pagingEnabled = YES;
+ self.scrollView.clipsToBounds = NO;
+ self.scrollView.scrollsToTop = NO;
+ self.scrollView.delegate = self;
+ [self addSubview:self.scrollView];
+
+ noAppsLabel = nil;
+ backgroundView = nil;
+ [CSResources reloadSettings];
+
+ UISwipeGestureRecognizer *downSwipe = [[[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(deactivateGesture:)] autorelease];
+ downSwipe.direction = UISwipeGestureRecognizerDirectionDown;
+ [self addGestureRecognizer:downSwipe];
+
+ //UIPinchGestureRecognizer *pinch = [[[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(zoomOut:)] autorelease];
+ //[self addGestureRecognizer:pinch];
+
+ /*CAGradientLayer *gradient = [CAGradientLayer layer];
+ gradient.frame = self.bounds;
+ gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor blackColor] CGColor], (id)[[UIColor colorWithRed:0.171 green:0.171 blue:0.171 alpha:1.000] CGColor], nil];
+ [self.layer insertSublayer:gradient atIndex:0];*/
+
+ currentOrientation = UIInterfaceOrientationPortrait;
+
+ [pool release];
+ }
+
+ return self;
+}
+
+-(void)setHidden:(BOOL)_hidden{
+ self.userInteractionEnabled = !_hidden;
+ [super setHidden:_hidden];
+}
+
+
+-(void)setRotation:(UIInterfaceOrientation)orientation{
+/* if (currentOrientation == orientation) return;
+
+ if (orientation == UIInterfaceOrientationPortrait) {
+ self.transform = CGAffineTransformRotate(self.transform,0.0);
+ }
+ else if (orientation == UIDeviceOrientationPortraitUpsideDown) {
+ self.transform = CGAffineTransformRotate(self.transform, 3.1415927);
+ }*/
+}
+
+-(void)relayoutSubviews{
+ //Defaults
+ pageControl.hidden = ![CSResources showsPageControl];
+
+ self.backgroundColor = [UIColor blackColor];
+
+ [backgroundView removeFromSuperview];
+ backgroundView = nil;
+ if ([CSResources backgroundStyle] == 2) {
+ backgroundView = [[[UIImageView alloc] initWithFrame:self.frame] autorelease];
+ backgroundView.image = [[(SBUIController*)[objc_getClass("SBUIController") sharedInstance] wallpaperView] image];
+ [self insertSubview:backgroundView atIndex:0];
+ }
+
+ [noAppsLabel removeFromSuperview];
+ noAppsLabel = nil;
+
+ if ([self.runningApps count] == 0) {
+ noAppsLabel = [[[UILabel alloc] initWithFrame:self.frame] autorelease];
+ noAppsLabel.backgroundColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.3];
+ noAppsLabel.font = [UIFont boldSystemFontOfSize:17];
+ noAppsLabel.textAlignment = UITextAlignmentCenter;
+ noAppsLabel.textColor = [UIColor whiteColor];
+ noAppsLabel.text = @"No Apps Running";
+ [self addSubview:noAppsLabel];
+ self.backgroundColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.8];
+ }
+
+
+ CGSize screenSize = self.scrollView.frame.size;
+ self.scrollView.contentSize = CGSizeMake(screenSize.width * [self.runningApps count], screenSize.height);
+
+ static int i; // Work around for using "for (* in *)" rather then "for (int i = 0; i < array.count; i++)"
+ i = 0;
+ for (SBApplication *app in self.runningApps) {
+ CSApplication *csApp = [[[CSApplication alloc] initWithApplication:app] autorelease];
+ CGRect appRect = csApp.frame;
+ appRect.origin.x = (i * screenSize.width) + ((screenSize.width-appRect.size.width)*0.5);
+ csApp.frame = appRect;
+ csApp.tag = (i + 1000);
+ [csApp reset];
+ [self.scrollView addSubview:csApp];
+
+ i++;
+ }
+}
+
+
+-(CSApplication*)csAppforApplication:(SBApplication*)app{
+ for (CSApplication *csApplication in self.scrollView.subviews) {
+ if ([app.displayIdentifier isEqualToString:csApplication.application.displayIdentifier]) {
+ return csApplication;
+ }
+ }
+
+ return nil;
+}
+
+
+-(void)appLaunched:(SBApplication*)app{
+ for (NSString *string in self.ignoredIDs) {
+ if ([[app displayIdentifier] isEqualToString:string]){
+ if (![self.ignoredApps containsObject:app]) {
+ [self.ignoredApps addObject:app];
+ }
+ return;
+ }
+ }
+
+ if (![self.runningApps containsObject:app]) {
+ [self.runningApps addObject:app];
+ }
+}
+
+-(void)appQuit:(SBApplication*)app{
+ if ([self.ignoredApps containsObject:app]) {
+ [self.ignoredApps removeObject:app];
+ return;
+ }
+ if (![self.runningApps containsObject:app]) { return; }
+
+
+ if (self.isActive) {
+ // Remove from the screen
+ CSApplication *appView = [self csAppforApplication:app];
+ [appView removeFromSuperview];
+
+ // And remove it from the array
+ [self.runningApps removeObject:app];
+
+ // Animate the ScrollView smaller
+ [UIView animateWithDuration:0.2 animations:^{
+ self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * self.runningApps.count, 0);
+ } completion:^(BOOL finished){}];
+ [self checkPages];
+
+ // Animate the apps closer together
+ CGSize screenSize = self.scrollView.frame.size;
+
+ static int i; // Work around for using "for (* in *)" rather then "for (int i = 0; i < array.count; i++)"
+ i = 0;
+ for (CSApplication *psApp in self.scrollView.subviews) {
+ psApp.tag = (1000+i);
+
+ CGRect appRect = psApp.frame;
+ appRect.origin.x = (i * screenSize.width) + ((screenSize.width-appRect.size.width)*0.5);
+ [UIView animateWithDuration:0.2 animations:^{
+ psApp.frame = appRect;
+ } completion:^(BOOL finished){}];
+
+ i++;
+ }
+
+ if (self.runningApps.count == 0) {
+ [self setActive:NO];
+ }
+
+ return;
+ }
+
+ [self.runningApps removeObject:app];
+}
+
+
+-(void)deactivateGesture:(UIGestureRecognizer*)gesture{
+ if (gesture.state != UIGestureRecognizerStateEnded)
+ return;
+
+ [self setActive:NO];
+}
+
+
+#pragma mark Active & deactive
+
+- (void)setActive:(BOOL)active{
+ [self setActive:active animated:YES];
+}
+
+- (void)setActive:(BOOL)active animated:(BOOL)animated{
+ if (active == self.isActive || self.isAnimating) { return; } //We are already active/inactive
+
+ if (active)
+ [self activateAnimated:animated];
+ else
+ [self deactivateAnimated:animated];
+}
+
+-(void)activateAnimated:(BOOL)animate{
+ self.isActive = YES;
+
+ self.layer.transform = CATransform3DIdentity;
+
+ self.frame = [UIScreen mainScreen].bounds;
+
+ [self checkPages];
+ [self relayoutSubviews];
+ [self checkPages];
+
+ // Setup first then animation
+ self.hidden = NO;
+ self.alpha = 0.0f;
+ self.isAnimating = YES;
+ self.userInteractionEnabled = YES;
+ self.scrollView.userInteractionEnabled = YES;
+ self.layer.transform = CATransform3DMakeScale(3.5f, 3.5f, 1.0f);
+
+ [UIView animateWithDuration:(animate ? 0.4 : 0.0) animations:^{
+ self.alpha = 1;
+ self.layer.transform = CATransform3DIdentity;
+ } completion:^(BOOL finished){
+ self.isAnimating = NO;
+ [self checkPages];
+ //if (spoke) { spoke = NO, [speaker startSpeakingString:@"Welcome to CardSwitcher"]; }
+ }];
+}
+
+-(void)deactivateAnimated:(BOOL)animate{
+ if (animate && !SBActive) {
+ SBApplication *app = [SBWActiveDisplayStack topApplication];
+
+ if ([SPRINGBOARD respondsToSelector:@selector(setBackgroundingEnabled:forDisplayIdentifier:)] && [CSResources autoBackgroundApps]){
+ [SPRINGBOARD setBackgroundingEnabled:YES forDisplayIdentifier:app.displayIdentifier];
+
+ }
+
+ [app setDeactivationSetting:0x2 flag:NO];
+ [SBWActiveDisplayStack popDisplay:app];
+ [SBWSuspendingDisplayStack pushDisplay:app];
+ app = nil;
+ }
+
+ self.isActive = NO;
+ self.userInteractionEnabled = NO;
+ self.scrollView.userInteractionEnabled = NO;
+ self.layer.transform = CATransform3DIdentity;
+
+ [self setRotation:[[UIDevice currentDevice] orientation]];
+
+ [UIView animateWithDuration:(animate ? 0.4 : 0.0) animations:^{
+ self.isAnimating = YES;
+ self.layer.transform = CATransform3DMakeScale(2.5f, 2.5f, 1.0f);
+ self.alpha = 0.0f;
+ } completion:^(BOOL finished){
+ self.hidden = YES;
+ self.isAnimating = NO;
+
+ [CSResources reset];
+
+ for (UIView *view in self.scrollView.subviews) {
+ [view removeFromSuperview];
+ }
+
+
+ [backgroundView removeFromSuperview];
+ backgroundView = nil;
+
+ [noAppsLabel removeFromSuperview];
+ noAppsLabel = nil;
+ }];
+}
+
+
+
+#pragma mark UIScrollViewDelegate methods
+
+- (void)scrollViewDidScroll:(UIScrollView *)sender {
+ [self checkPages];
+ //#error SCRAPING 3 visibleApps and adding LAZY image loading.
+}
+
+-(void)checkPages{
+ CGFloat pageWidth = self.scrollView.frame.size.width;
+ int page = (floor((self.scrollView.contentOffset.x - pageWidth / 2) / pageWidth)) + 1;
+
+ pageControl.numberOfPages = [self.runningApps count];
+ pageControl.currentPage = page;
+
+ self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * pageControl.numberOfPages, 0);
+}
+
+
+#pragma mark libactivator delegate
+
+- (void)activator:(LAActivator *)activator receiveEvent:(LAEvent *)event {
+ if ([(SBAwayController*)[objc_getClass("SBAwayController") sharedAwayController] isLocked] || self.isAnimating)
+ return;
+
+ // Set the event handled
+ [event setHandled:YES];
+ BOOL newActive = ![self isActive];
+ //[self setActive:newActive];
+
+ // SpringBoard is active, just activate
+ if (SBActive) {
+ [self setActive:newActive];
+ [self scrollViewDidScroll:self.scrollView];
+ return;
+ }
+
+ CGImageRef screen = UIGetScreenImage();
+ [CSResources setCurrentAppImage:[UIImage imageWithCGImage:screen]];
+ CGImageRelease(screen);
+
+
+ if (newActive && [[[SBWActiveDisplayStack topApplication] displayIdentifier] length]) {
+ [self setActive:YES animated:NO];
+
+ SBApplication *application = [SBWActiveDisplayStack topApplication];
+ int index = [self.runningApps indexOfObject:application];
+ [self.scrollView setContentOffset:CGPointMake((index*self.scrollView.frame.size.width), 0) animated:NO];
+ [self scrollViewDidScroll:self.scrollView];
+
+ self.scrollView.userInteractionEnabled = NO;
+
+ CSApplication *psApp = [self csAppforApplication:application];
+ UIImageView *snapshot = psApp.snapshot;
+
+ float oldRadius = snapshot.layer.cornerRadius;
+ snapshot.layer.cornerRadius = 0;
+
+ CGRect targetRect = snapshot.frame;
+
+ CGRect screenRect = psApp.frame;
+ screenRect.size.width = SCREEN_WIDTH;
+ screenRect.size.height = SCREEN_HEIGHT;
+ screenRect.origin.x = -self.scrollView.frame.origin.x - ((self.scrollView.frame.size.width-psApp.frame.size.width)*0.5);
+ screenRect.origin.y = -self.scrollView.frame.origin.y;
+ snapshot.frame = screenRect;
+
+ [self.scrollView bringSubviewToFront:psApp];
+ [psApp bringSubviewToFront:psApp.snapshot];
+
+ [UIView animateWithDuration:0.38 animations:^{
+ snapshot.frame = targetRect;
+ snapshot.layer.cornerRadius = oldRadius;
+ } completion:^(BOOL finished){
+ [psApp sendSubviewToBack:psApp.snapshot];
+ self.scrollView.userInteractionEnabled = YES;
+ }];
+ }
+ else {
+ // Fancy animation
+ [self setActive:NO];
+ }
+}
+
+- (void)activator:(LAActivator *)activator abortEvent:(LAEvent *)event{
+ if (self.isActive == NO || self.isAnimating) { return; }
+
+ [self setActive:NO animated:NO];
+}
+
+- (void)activator:(LAActivator *)activator receiveDeactivateEvent:(LAEvent *)event{
+ if (self.isActive == NO || self.isAnimating) { return; }
+
+ [event setHandled:YES];
+ [self setActive:NO];
+}
+
+
+-(void)dealloc{
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
+
+ for (UIView *view in self.scrollView.subviews) {
+ [view removeFromSuperview];
+ }
+ for (UIView *view in self.subviews) {
+ [view removeFromSuperview];
+ }
+
+ [noAppsLabel release], noAppsLabel = nil;
+ [pageControl release], pageControl = nil;
+
+ [super dealloc];
+}
+
+@end
36 CSResources.h
@@ -0,0 +1,36 @@
+//
+// CSResources.h
+//
+//
+// Created by Kyle Howells on 22/08/2011.
+// Copyright 2011 Howells Apps. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+@class SBApplication;
+
+@interface CSResources : NSObject
+
+// Images
++(UIImage*)currentAppImage;
++(void)setCurrentAppImage:(UIImage*)image;
++(void)reset;
++(BOOL)cachScreenShot:(UIImage*)screenshot forApp:(SBApplication*)app;
++(UIImage*)cachedScreenShot:(SBApplication*)app;
++(UIImage*)appScreenShot:(SBApplication*)app;
++(void)didReceiveMemoryWarning;
+
+// Settings
++(BOOL)swipeCloses;
++(BOOL)showsCloseBox;
++(BOOL)showsAppTitle;
++(BOOL)showsPageControl;
++(BOOL)autoBackgroundApps;
++(int)cornerRadius;
++(int)tapsToLaunch;
++(int)backgroundStyle;
++(void)reloadSettings;
+
+@end
164 CSResources.m
@@ -0,0 +1,164 @@
+//
+// CSResources.m
+//
+//
+// Created by Kyle Howells on 22/08/2011.
+// Copyright 2011 Howells Apps. All rights reserved.
+//
+
+#import <SpringBoard4.0/SBUIController.h>
+#import <SpringBoard4.0/SBDisplayStack.h>
+#import <SpringBoard4.0/SBApplication.h>
+#import "CSApplicationController.h"
+#import "CSResources.h"
+
+CGImageRef UIGetScreenImage(void);
+
+static NSMutableDictionary *cache = nil;
+static NSDictionary *settings = nil;
+static UIImage *currentImage = nil;
+
+@implementation CSResources
+
+
++(UIImage*)currentAppImage{
+ return currentImage;
+}
++(void)setCurrentAppImage:(UIImage*)image{
+ [currentImage release];
+ currentImage = nil;
+ currentImage = [image retain];
+}
+
+
++(void)reset{
+ [cache removeAllObjects];
+ [currentImage release];
+ currentImage = nil;
+}
+
++(void)didReceiveMemoryWarning{
+ [cache removeAllObjects];
+ [cache release];
+ cache = nil;
+}
+
+
++(BOOL)cachScreenShot:(UIImage*)screenshot forApp:(SBApplication*)app{
+ if (!cache)
+ cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/User/Library/Caches/CardSwitcher/cache.plist"];
+ if (!cache)
+ cache = [[NSMutableDictionary alloc] init];
+
+ [screenshot retain];
+
+ [[NSFileManager defaultManager] createDirectoryAtPath:@"/User/Library/Caches/CardSwitcher" withIntermediateDirectories:YES attributes:nil error:nil];
+ //NSString *pngPath = [NSString stringWithFormat:@"/User/Library/Caches/CardSwitcher/%@", [app displayIdentifier]];
+ //BOOL success = [UIImagePNGRepresentation(screenshot) writeToFile:pngPath atomically:YES];
+
+ //if (success)
+ [cache setObject:screenshot forKey:[app displayIdentifier]];
+ [cache writeToFile:@"/User/Library/Caches/CardSwitcher/cache.plist" atomically:YES];
+
+ [screenshot release];
+
+ return YES;
+}
+
++(UIImage*)cachedScreenShot:(SBApplication*)app{
+ if (currentImage && [[[SBWActiveDisplayStack topApplication] displayIdentifier] isEqualToString:[app displayIdentifier]]) {
+ return currentImage;
+ }
+
+ if (!cache)
+ cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/User/Library/Caches/CardSwitcher/cache.plist"];
+ if (!cache)
+ cache = [[NSMutableDictionary alloc] init];
+ if (cache) {
+ if ([cache objectForKey:[app displayIdentifier]])
+ return [cache objectForKey:[app displayIdentifier]];
+ }
+
+ /*NSString *pngPath = [NSString stringWithFormat:@"/User/Library/Caches/CardSwitcher/%@", [app displayIdentifier]];
+ UIImage *img = [UIImage imageWithContentsOfFile:pngPath];
+ if (img) return img;*/
+
+ return [self appScreenShot:app];
+}
+
++(UIImage*)appScreenShot:(SBApplication*)app{
+ if (!app)
+ return nil;
+
+ // If the app doesn't display the status bar (or a see through one) just return it's snapshot.
+ if ([app defaultStatusBarHidden] || [app statusBarStyle] == UIStatusBarStyleBlackTranslucent) {
+ UIImage *img = [app defaultImage:NULL];
+ [self cachScreenShot:img forApp:app];
+ return img;
+ }
+
+ // Else to avoid weirdness we need to render a fake status bar above the snapshot
+ UIGraphicsBeginImageContext([UIScreen mainScreen].bounds.size);
+
+ if ([app statusBarStyle] == UIStatusBarStyleBlackOpaque) {
+ [[UIColor blackColor] set];
+ UIRectFill([UIScreen mainScreen].bounds);
+ }
+ else {
+ [[CSApplicationController sharedController].statusBarDefault drawInRect:CGRectMake(0, 0, SCREEN_WIDTH, 20)];
+ }
+
+ [[app defaultImage:NULL] drawInRect:CGRectMake(0, 20, SCREEN_WIDTH, SCREEN_HEIGHT-20)];
+
+ UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+
+ [self cachScreenShot:img forApp:app];
+ return img;
+}
+
+
+
+#pragma mark Settings
+
++(BOOL)swipeCloses{
+ id temp = [settings objectForKey:@"CSSwipeClose"];
+ return (temp ? [temp boolValue] : YES);
+}
++(BOOL)showsCloseBox{
+ id temp = [settings objectForKey:@"CSShowCloseButtons"];
+ return (temp ? [temp boolValue] : YES);
+}
++(BOOL)showsAppTitle{
+ id temp = [settings objectForKey:@"CSShowAppTitle"];
+ return (temp ? [temp boolValue] : YES);
+}
++(BOOL)showsPageControl{
+ id temp = [settings objectForKey:@"CSShowPageDots"];
+ return (temp ? [temp boolValue] : YES);
+}
++(BOOL)autoBackgroundApps{
+ id temp = [settings objectForKey:@"CSAutoBackground"];
+ return (temp ? [temp boolValue] : NO);
+}
++(int)cornerRadius{
+ id temp = [settings objectForKey:@"CSCornerRadius"];
+ return (temp ? [temp intValue] : 10);
+}
++(int)tapsToLaunch{
+ id temp = [settings objectForKey:@"CSTapsActivate"];
+ return (temp ? [temp intValue] : 1);
+}
++(int)backgroundStyle{
+ id temp = [settings objectForKey:@"CSBackground"];
+ return (temp ? [temp intValue] : 1);
+}
+
++(void)reloadSettings{
+ [settings release];
+ settings = nil;
+ settings = [[NSDictionary alloc] initWithContentsOfFile:@"/var/mobile/Library/Preferences/com.iky1e.cardswitcher.plist"];
+}
+
+
+@end
13 CSScrollView.h
@@ -0,0 +1,13 @@
+//
+// CSScrollView.h
+//
+//
+// Created by Kyle Howells on 29/08/2011.
+// Copyright 2011 Howells Apps. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface CSScrollView : UIScrollView
+-(BOOL)viewIsVisible:(UIView*)view;
+@end
35 CSScrollView.m
@@ -0,0 +1,35 @@
+//
+// CSScrollView.m
+//
+//
+// Created by Kyle Howells on 29/08/2011.
+// Copyright 2011 Howells Apps. All rights reserved.
+//
+
+#import "CSScrollView.h"
+
+@implementation CSScrollView
+
+-(BOOL)viewIsVisible:(UIView*)view{
+ CGRect visibleRect;
+ visibleRect.origin = self.contentOffset;
+ visibleRect.size = self.bounds.size;
+ visibleRect.origin.x -= 50;
+ visibleRect.size.width += 100;
+
+ return CGRectIntersectsRect(visibleRect, view.frame);
+}
+
+- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
+ CGPoint parentLocation = [self convertPoint:point toView:self.superview];
+ /*CGRect responseRect = self.frame;
+ responseRect.origin.x -= responseInsets.left;
+ responseRect.origin.y -= responseInsets.top;
+ responseRect.size.width += (responseInsets.left + responseInsets.right);
+ responseRect.size.height += (responseInsets.top + responseInsets.bottom);
+ return CGRectContainsPoint(responseRect, parentLocation);*/
+
+ return [self.superview pointInside:parentLocation withEvent:event];
+}
+
+@end
1 CardSwitcher.plist
@@ -0,0 +1 @@
+{ Filter = { Bundles = ( "com.apple.springboard" ); }; }
9 Makefile
@@ -0,0 +1,9 @@
+include theos/makefiles/common.mk
+
+TWEAK_NAME = CardSwitcher
+CardSwitcher_FILES = Tweak.xm CSApplicationController.m CSApplication.m CSResources.m CSScrollView.m
+CardSwitcher_FRAMEWORKS = Foundation UIKit QuartzCore CoreGraphics
+CardSwitcher_PRIVATE_FRAMEWORKS = GraphicsServices
+CardSwitcher_LDFLAGS = -lactivator
+
+include $(THEOS_MAKE_PATH)/tweak.mk
29 README.md
@@ -0,0 +1,29 @@
+The UI uses only public API's so shouldn't break with iOS5.
+
+The potential breakpoints are.
+
+* Methods used
+CGImageRef UIGetScreenImage(void);
+//[[SBAppSwitcherController +sharedInstance] -_quitButtonHit:APP]
+[[SBIconModel +sharedInstance] -applicationIconForDisplayIdentifier:ID];
+[SBIcon -getIconImage:3];
+[SBIconBadge +iconBadgeWithBadgeString:STRING];
+[SBApplication -defaultImage:NULL];
+[SBApplication -kill];
+GSSystemHasCapability(kGSMultitaskingCapability) /// GraphicsServices.framework
+[[SBAwayController +sharedAwayController] -isLocked];
+
+* Methods hooked
+[SBAppSwitcherController -applicationLaunched:APP]
+[SBAppSwitcherController -applicationDied:APP]
+[SBApplication -launch];
+[SBApplication -exitedCommon];
+[SBIcon -_PSBadgeText]; //Created new method to get the label's string (4.0-4.1 is different to 3.x and 4.2+)
+[SBAwayController -lock];
+
+* And of cause the SBDisplayStack order
+#define SBWPreActivateDisplayStack [displayStacks objectAtIndex:0]
+#define SBWActiveDisplayStack [displayStacks objectAtIndex:1]
+#define SBWSuspendingDisplayStack [displayStacks objectAtIndex:2]
+#define SBWSuspendedEventOnlyDisplayStack [displayStacks objectAtIndex:3]
+#define SBActive ([SBWActiveDisplayStack topApplication] == nil)
223 Tweak.mm
@@ -0,0 +1,223 @@
+#import <SpringBoard4.0/SBApplicationIcon.h>
+#import <GraphicsServices/GSCapability.h>
+#import <SpringBoard4.0/SBApplication.h>
+#import <SpringBoard4.0/SBIconBadge.h>
+#import <libactivator/libactivator.h>
+#import "CSApplicationController.h"
+#import <QuartzCore/QuartzCore.h>
+#import <UIKit/UIKit.h>
+
+
+#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
+#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
+#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
+#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
+#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
+
+static NSString * const CARDSWITCHER_ID = @"com.iky1e.cardswitcher";
+
+
+%hook SBAppSwitcherController
+
+-(void)applicationLaunched:(SBApplication*)app{
+ %orig;
+
+ [[CSApplicationController sharedController] appLaunched:app];
+}
+
+-(void)applicationDied:(SBApplication*)app{
+ [[CSApplicationController sharedController] appQuit:app];
+
+ %orig;
+}
+
+%end
+
+
+%hook SBApplicationIcon
+
+-(void)launch{
+ %orig;
+
+ [[CSApplicationController sharedController] appLaunched:[self application]];
+}
+
+%end
+
+
+%group OldDevices
+%hook SBApplication
+
+-(void)launch{
+ %orig;
+
+ [[CSApplicationController sharedController] appLaunched:self];
+}
+
+%end
+%end
+
+%hook SBApplication
+
+-(void)exitedCommon{
+ [[CSApplicationController sharedController] appQuit:self];
+
+ %orig;
+}
+
+-(void)_relaunchAfterExitIfNecessary{
+ if ([self.displayIdentifier isEqualToString:[CSApplicationController sharedController].ignoreRelaunchID]) {
+ [[CSApplicationController sharedController].ignoreRelaunchID release], [CSApplicationController sharedController].ignoreRelaunchID = nil;
+ return;
+ }
+
+ %orig;
+}
+
+%end
+
+
+%hook SBDisplayStack
+-(id)init{
+ if ((self = %orig)) {
+ [[CSApplicationController sharedController].displayStacks addObject:self];
+ }
+ return self;
+}
+
+-(void)dealloc{
+ [[CSApplicationController sharedController].displayStacks removeObject:self];
+
+ %orig;
+}
+%end
+
+
+%hook SBIcon
+
+%new
+-(NSString*)_PSBadgeText{
+ if ([[self badgeNumberOrString] isMemberOfClass:[NSNumber class]]) {
+ return [[self badgeNumberOrString] stringValue];
+ }
+ else if ([[self badgeNumberOrString] isMemberOfClass:[NSString class]]) {
+ return [self badgeNumberOrString];
+ }
+
+ return [[NSNumber numberWithInt:[self badgeValue]] stringValue];
+}
+
+%end
+
+
+/*%hook SBIconBadge
+
+%new
+-(NSString*)_PSBadgeText{
+ if (SYSTEM_VERSION_GREATER_THAN(@"4.1")) {
+ NSString *labelText = MSHookIvar<NSString *>(self, "_badge");
+ return [[labelText copy] autorelease];
+ }
+
+ // If 4.1
+ UILabel *label = MSHookIvar<UILabel *>(self, "_badgeLabel");
+ return [[label.text copy] autorelease];
+}
+
+%end*/
+
+
+%hook SBAwayController
+
+-(void)lock{
+ [[CSApplicationController sharedController] setActive:NO animated:NO];
+
+ %orig;
+}
+
+%end
+
+
+%hook SpringBoard
+
+- (void)applicationDidFinishLaunching:(UIApplication *)application{
+ %orig;
+
+ [CSApplicationController sharedController].springBoard = self;
+
+ if (![[LAActivator sharedInstance] hasSeenListenerWithName:CARDSWITCHER_ID])
+ [[LAActivator sharedInstance] assignEvent:[LAEvent eventWithName:LAEventNameMenuPressDouble] toListenerWithName:CARDSWITCHER_ID];
+
+ [[LAActivator sharedInstance] registerListener:[CSApplicationController sharedController] forName:CARDSWITCHER_ID];
+
+
+ if (!GSSystemHasCapability(kGSMultitaskingCapability)) {
+ %init(OldDevices);
+ }
+}
+
+%end
+
+
+/*%group IPAD
+%hook SBUIController
+- (void)window:(id)arg1 willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(double)arg3{
+ %orig;
+
+ [[CSApplicationController sharedController] setRotation:orientation];
+}
+%end
+%end*/
+
+
+/*%hook SBAppDosadoView
+-(void)beginTransition{
+ if ([CSApplicationController sharedController].shouldAnimate) {
+ CALayer *to = MSHookIvar<CALayer *>(self, "_stopLayer");
+ [self.layer addSublayer:to];
+ [UIView beginAnimations:nil context:NULL];
+ [UIView setAnimationDuration:0.1];
+ [UIView setAnimationDelegate:self];
+ [UIView setAnimationDidStopSelector:@selector(_animationDidStop:)];
+ [UIView commitAnimations];
+ return;
+ }
+
+ %orig;
+ return;
+}
+
+-(void)_beginTransition {
+ if ([CSApplicationController sharedController].shouldAnimate) {
+ UIView *to = MSHookIvar<UIView *>(self, "_toView");
+ [self addSubview:to];
+ [UIView beginAnimations:nil context:NULL];
+ [UIView setAnimationDuration:0.1];
+ [UIView setAnimationDelegate:self];
+ [UIView setAnimationDidStopSelector:@selector(_animationDidStop:)];
+ [UIView commitAnimations];
+ return;
+ }
+
+ %orig;
+ return;
+}
+-(void)_animationDidStop:(id)_animation{
+ [CSApplicationController sharedController].shouldAnimate = NO;
+
+ %orig;
+}
+%end*/
+
+static void CSSettingsChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
+{
+ [CSResources reloadSettings];
+}
+
+%ctor
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ %init;
+ CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, CSSettingsChanged, CFSTR("com.iky1e.cardswitcher/settingschanged"), NULL, CFNotificationSuspensionBehaviorCoalesce);
+ [pool release];
+}
BIN com.iky1e.cardswitcher_0.9.9.1-9_iphoneos-arm.deb
Binary file not shown.
10 layout/DEBIAN/control
@@ -0,0 +1,10 @@
+Package: com.iky1e.cardswitcher
+Name: CardSwitcher
+Depends: mobilesubstrate, preferenceloader (>= 2.0.2), libactivator (>= 1.5), firmware (>= 4.0)
+Version: 0.9.9.1
+Architecture: iphoneos-arm
+Description: iOS 4.x compatible rewrite of ProSwitcher!
+Maintainer: Kyle Howells <freerunnering@gmail.com>
+Author: Kyle Howells <freerunnering@gmail.com>
+Section: Tweaks
+dev: iky1e
BIN layout/Library/Activator/Listeners/com.iky1e.cardswitcher/CardSwitcher-small.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN layout/Library/Activator/Listeners/com.iky1e.cardswitcher/CardSwitcher-small@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN layout/Library/Activator/Listeners/com.iky1e.cardswitcher/CardSwitcher.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN layout/Library/Activator/Listeners/com.iky1e.cardswitcher/CardSwitcher@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 layout/Library/Activator/Listeners/com.iky1e.cardswitcher/Info.plist
@@ -0,0 +1,4 @@
+{
+ title = "CardSwitcher";
+ description = "Card style multitasking interface";
+}
BIN layout/Library/Application Support/CardSwitcher/UIStatusBarStyleBlackOpaque.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN layout/Library/Application Support/CardSwitcher/UIStatusBarStyleDefault.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN layout/Library/Application Support/CardSwitcher/closebox.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN layout/Library/Application Support/CardSwitcher/closebox@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN layout/Library/PreferenceLoader/Preferences/CardSwitcher/CardSwitcher-small.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN layout/Library/PreferenceLoader/Preferences/CardSwitcher/CardSwitcher-small@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
194 layout/Library/PreferenceLoader/Preferences/CardSwitcher/CardSwitcher.plist
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>title</key>
+ <string>CardSwitcher</string>
+ <key>entry</key>
+ <dict>
+ <key>cell</key>
+ <string>PSLinkCell</string>
+ <key>label</key>
+ <string>CardSwitcher</string>
+ <key>icon</key>
+ <string>CardSwitcher-small.png</string>
+ </dict>
+ <key>items</key>
+ <array>
+ <dict>
+ <key>cell</key>
+ <string>PSGroupCell</string>
+ </dict>
+ <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.iky1e.cardswitcher</string>
+ </dict>
+ <dict>
+ <key>cell</key>
+ <string>PSGroupCell</string>
+ <key>label</key>
+ <string>Interface</string>
+ <key>footerText</key>
+ <string>CardSwitcher is © 2011 Kyle Howells</string>
+ </dict>
+ <dict>
+ <key>cell</key>
+ <string>PSSwitchCell</string>
+ <key>default</key>
+ <true/>
+ <key>defaults</key>
+ <string>com.iky1e.cardswitcher</string>
+ <key>key</key>
+ <string>CSSwipeClose</string>
+ <key>label</key>
+ <string>Swipe to Close</string>
+ <key>PostNotification</key>
+ <string>com.iky1e.cardswitcher/settingschanged</string>
+ </dict>
+ <dict>
+ <key>cell</key>
+ <string>PSSwitchCell</string>
+ <key>default</key>
+ <true/>
+ <key>defaults</key>
+ <string>com.iky1e.cardswitcher</string>
+ <key>key</key>
+ <string>CSShowCloseButtons</string>
+ <key>label</key>
+ <string>Show Close Buttons</string>
+ <key>PostNotification</key>
+ <string>com.iky1e.cardswitcher/settingschanged</string>
+ </dict>
+ <dict>
+ <key>cell</key>
+ <string>PSSwitchCell</string>
+ <key>default</key>
+ <true/>
+ <key>defaults</key>
+ <string>com.iky1e.cardswitcher</string>
+ <key>key</key>
+ <string>CSShowAppTitle</string>
+ <key>label</key>
+ <string>Show App Title</string>
+ <key>PostNotification</key>
+ <string>com.iky1e.cardswitcher/settingschanged</string>
+ </dict>
+ <dict>
+ <key>cell</key>
+ <string>PSSwitchCell</string>
+ <key>default</key>
+ <true/>
+ <key>defaults</key>
+ <string>com.iky1e.cardswitcher</string>
+ <key>key</key>
+ <string>CSShowPageDots</string>
+ <key>label</key>
+ <string>Page Dots</string>
+ <key>PostNotification</key>
+ <string>com.iky1e.cardswitcher/settingschanged</string>
+ </dict>
+ <dict>
+ <key>cell</key>
+ <string>PSLinkListCell</string>
+ <key>default</key>
+ <integer>10</integer>
+ <key>detail</key>
+ <string>PSListItemsController</string>
+ <key>key</key>
+ <string>CSCornerRadius</string>
+ <key>label</key>
+ <string>Corner Radius</string>
+ <key>validTitles</key>
+ <array>
+ <string>None</string>
+ <string>Small</string>
+ <string>Medium</string>
+ <string>Large</string>
+ </array>
+ <key>validValues</key>
+ <array>
+ <integer>0</integer>
+ <integer>5</integer>
+ <integer>10</integer>
+ <integer>20</integer>
+ </array>
+ <key>defaults</key>
+ <string>com.iky1e.cardswitcher</string>
+ <key>PostNotification</key>
+ <string>com.iky1e.cardswitcher/settingschanged</string>
+ </dict>
+ <dict>
+ <key>cell</key>
+ <string>PSLinkListCell</string>
+ <key>default</key>
+ <integer>1</integer>
+ <key>detail</key>
+ <string>PSListItemsController</string>
+ <key>key</key>
+ <string>CSTapsActivate</string>
+ <key>label</key>
+ <string>Taps to Activate</string>
+ <key>validTitles</key>
+ <array>
+ <string>One</string>
+ <string>Two</string>
+ </array>
+ <key>validValues</key>
+ <array>
+ <integer>1</integer>
+ <integer>2</integer>
+ </array>
+ <key>defaults</key>
+ <string>com.iky1e.cardswitcher</string>
+ <key>PostNotification</key>
+ <string>com.iky1e.cardswitcher/settingschanged</string>
+ </dict>
+ <dict>
+ <key>cell</key>
+ <string>PSLinkListCell</string>
+ <key>default</key>
+ <integer>1</integer>
+ <key>detail</key>
+ <string>PSListItemsController</string>
+ <key>key</key>
+ <string>CSBackground</string>
+ <key>label</key>
+ <string>Background</string>
+ <key>validTitles</key>
+ <array>
+ <string>Black</string>
+ <string>Wallpaper</string>
+ </array>
+ <key>validValues</key>
+ <array>
+ <integer>1</integer>
+ <integer>2</integer>
+ </array>
+ <key>defaults</key>
+ <string>com.iky1e.cardswitcher</string>
+ <key>PostNotification</key>
+ <string>com.iky1e.cardswitcher/settingschanged</string>
+ </dict>
+ <!--<dict>
+ <key>cell</key>
+ <string>PSGroupCell</string>
+ <key>isStaticText</key>
+ <true/>
+ </dict>
+ <dict>
+ <key>cell</key>
+ <string>PSGroupCell</string>
+ <key>footerText</key>
+ <string>CardSwitcher is © 2011 Kyle Howells</string>
+ </dict>-->
+ </array>
+</dict>
+</plist>
BIN layout/Library/PreferenceLoader/Preferences/CardSwitcher/CardSwitcher.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN layout/Library/PreferenceLoader/Preferences/CardSwitcher/CardSwitcher@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 make.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+ARGS=$*
+
+# Initial set up
+
+# Dump Public Frameworks
+
+#for fw in $SDK/System/Library/Frameworks/*/
+#do export GO_EASY_ON_ME=1
+# class-dump -H -o ~/Desktop/HeaderDump/Frameworks/$(perl -e "print substr(substr('$fw', 0, -1), rindex(substr('$fw', 0, -1), \"/\") +1, (rindex(substr('$fw', 0, -1), \".\"))-(rindex(substr('$fw', 0, -1), \"/\") +1))";)/ $fw$(perl -e "print substr(substr('$fw', 0, -1), rindex(substr('$fw', 0, -1), \"/\") +1, (rindex(substr('$fw', 0, -1), \".\"))-(rindex(substr('$fw', 0, -1), \"/\") +1))";)
+#done
+
+
+# Rename the files
+cp ./Tweak.mm ./Tweak.xm
+echo "Copied Tweak"
+
+# Build
+echo ""
+echo "||---- Building..."
+echo ""
+make $ARGS
+echo ""
+echo "||---- Built!"
+echo ""
+
+
+# Rename the files
+rm ./Tweak.xm
+echo "Deleted Tweak.xm"
+
+
+exit 0

0 comments on commit 5189b85

Please sign in to comment.
Something went wrong with that request. Please try again.