diff --git a/AveNoAnimationsInMailPlugin.xcodeproj/project.pbxproj b/AveNoAnimationsInMailPlugin.xcodeproj/project.pbxproj index fefdbdb..6882b61 100644 --- a/AveNoAnimationsInMailPlugin.xcodeproj/project.pbxproj +++ b/AveNoAnimationsInMailPlugin.xcodeproj/project.pbxproj @@ -7,12 +7,14 @@ objects = { /* Begin PBXBuildFile section */ + 495BBFFB1BC16D1800FC37B9 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 495BBFFA1BC16D1800FC37B9 /* QuartzCore.framework */; }; B2662CE4181909FE00783601 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B2662CE3181909FE00783601 /* Cocoa.framework */; }; B2662CEE181909FE00783601 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = B2662CEC181909FE00783601 /* InfoPlist.strings */; }; B2662CF718190A6600783601 /* AveHookBootstrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = B2662CF618190A6600783601 /* AveHookBootstrapper.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 495BBFFA1BC16D1800FC37B9 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; B2662CE0181909FD00783601 /* AveNoAnimationsInMailPlugin.mailbundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AveNoAnimationsInMailPlugin.mailbundle; sourceTree = BUILT_PRODUCTS_DIR; }; B2662CE3181909FE00783601 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; B2662CE6181909FE00783601 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -30,6 +32,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 495BBFFB1BC16D1800FC37B9 /* QuartzCore.framework in Frameworks */, B2662CE4181909FE00783601 /* Cocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -57,6 +60,7 @@ B2662CE2181909FE00783601 /* Frameworks */ = { isa = PBXGroup; children = ( + 495BBFFA1BC16D1800FC37B9 /* QuartzCore.framework */, B2662CE3181909FE00783601 /* Cocoa.framework */, B2662CE5181909FE00783601 /* Other Frameworks */, ); @@ -164,7 +168,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [ \"${CONFIGURATION}\" = \"Release\" ]; then\n cp -r $BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME $PROJECT_DIR/Output/\n zip -r -X $PROJECT_DIR/Output/$(basename \"$FULL_PRODUCT_NAME\").zip $PROJECT_DIR/Output/$FULL_PRODUCT_NAME\n cd $PROJECT_DIR/Output/\n /bin/sh $PROJECT_DIR/Output/repackage.sh\nfi"; + shellScript = "if [ \"${CONFIGURATION}\" = \"Release\" ]; then\n cp -r \"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME\" \"$PROJECT_DIR/Output/\"\n zip -r -X \"$PROJECT_DIR/Output/$(basename \"$FULL_PRODUCT_NAME\").zip\" \"$PROJECT_DIR/Output/$FULL_PRODUCT_NAME\"\n cd \"$PROJECT_DIR/Output/\"\n /bin/sh \"$PROJECT_DIR/Output/repackage.sh\"\nfi"; }; /* End PBXShellScriptBuildPhase section */ diff --git a/AveNoAnimationsInMailPlugin/AveHookBootstrapper.m b/AveNoAnimationsInMailPlugin/AveHookBootstrapper.m index 7fe41fa..795a60c 100644 --- a/AveNoAnimationsInMailPlugin/AveHookBootstrapper.m +++ b/AveNoAnimationsInMailPlugin/AveHookBootstrapper.m @@ -7,12 +7,195 @@ // #import "AveHookBootstrapper.h" +#import #import @implementation AveHookBootstrapper -#pragma mark shouldDoPopOutAnimation +static BOOL elCapitanOverrideTransactionDurationToZero = NO; + ++(BOOL)isElCapitan +{ + static BOOL isElCapitan = NO; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSProcessInfo* processInfo = [NSProcessInfo processInfo]; + if([processInfo respondsToSelector:@selector(isOperatingSystemAtLeastVersion:)]) + { + NSOperatingSystemVersion version = {0}; + version.majorVersion = 10; + version.minorVersion = 11; // el cap + isElCapitan = [processInfo isOperatingSystemAtLeastVersion:version]; + } + }); + + return isElCapitan; +} + +static IMP AveReplaceMethod(Class class, BOOL isMetaClass, SEL sel, id block) { + + NSString* printableMethodDescription = [NSString stringWithFormat:@"%@[%@ %@]", isMetaClass ? @"+" : @"-", NSStringFromClass(class), NSStringFromSelector(sel)]; + + // get original method + Method method = class_getInstanceMethod(class, sel); + if(method == nil) + { + NSLog(@"Ave: Method not found %@", printableMethodDescription); + } + + NSLog(@"Ave: %@ -> %s", printableMethodDescription, method_getTypeEncoding(method)); + + IMP newIMP = imp_implementationWithBlock(block); + + if(class_addMethod(class, sel, newIMP, method_getTypeEncoding(method))) + { + NSLog(@"Ave: Added %@", printableMethodDescription); + return method_getImplementation(method); + } + else + { + NSLog(@"Ave: Replaced %@", printableMethodDescription); + return method_setImplementation(method, newIMP); + } +} + +static IMP AveReplaceInstanceMethod(Class class, SEL sel, id block) { + return AveReplaceMethod(class, NO, sel, block); +} + +static IMP AveReplaceClassMethod(Class class, SEL sel, id block) { + Class metaClass = object_getClass(class); + return AveReplaceMethod(metaClass, YES, sel, block); +} + +#pragma mark - El Capitan + ++(void)aveElCapitanSwizzleCATransactionSetAnimationDuration +{ + Class class = [CATransaction class]; + SEL sel = @selector(setAnimationDuration:); + __block IMP originalImplementation = AveReplaceClassMethod(class, sel, ^(id obj, NSTimeInterval duration){ + + if([NSThread isMainThread] && elCapitanOverrideTransactionDurationToZero) + duration = 0.01; + + if(originalImplementation != NULL) + { + ((void(*)(id, SEL, NSTimeInterval))originalImplementation)(obj, sel, duration); + } + }); +} + ++(void)aveElCapitanSwizzleNSAnimationContextSetDuration +{ + Class class = [NSAnimationContext class]; + SEL sel = @selector(setDuration:); + __block IMP originalImplementation = AveReplaceInstanceMethod(class, sel, ^(id obj, NSTimeInterval duration){ + + if([NSThread isMainThread] && elCapitanOverrideTransactionDurationToZero) + duration = 0.0; + + if(originalImplementation != NULL) + { + ((void(*)(id, SEL, NSTimeInterval))originalImplementation)(obj, sel, duration); + } + }); +} + ++(void)aveElCapitanSwizzleComposeWindowControllerPerformSendAnimation +{ + Class class = NSClassFromString(@"ComposeWindowController"); + SEL sel = @selector(_performSendAnimation); + __block IMP originalImplementation = AveReplaceInstanceMethod(class, sel, ^(id obj){ + elCapitanOverrideTransactionDurationToZero = YES; + if(originalImplementation != NULL) + { + ((void(*)(id, SEL))originalImplementation)(obj, sel); + } + + elCapitanOverrideTransactionDurationToZero = NO; + }); +} + ++(void)aveElCapitanSwizzlePopoutAnimationController_animateFrom_to_withCompletion +{ + Class class = NSClassFromString(@"PopoutAnimationController"); + SEL sel = @selector(animateFrom:to:withCompletion:); + __block IMP originalImplementation = AveReplaceInstanceMethod(class, sel, ^(id obj, id from, id to, id completion){ + elCapitanOverrideTransactionDurationToZero = YES; + if(originalImplementation != NULL) + { + ((void(*)(id, SEL, id, id, id))originalImplementation)(obj, sel, from, to, completion); + } + + elCapitanOverrideTransactionDurationToZero = NO; + }); +} + ++(void)aveElCapitanswizzlePopoutAnimationController_internalTransitionAnimationWithDestination_fadeOut +{ + Class class = NSClassFromString(@"PopoutAnimationController"); + SEL sel = @selector(_internalTransitionAnimationWithDestination:fadeOut:); + /*__block IMP originalImplementation = */AveReplaceInstanceMethod(class, sel, ^id(id obj, CGRect rc, BOOL fadeOut){ + + return nil; + /* + if(originalImplementation != NULL) + { + CAAnimationGroup* animationGroup = ((CAAnimationGroup*(*)(id, SEL, CGRect, BOOL))originalImplementation)(obj, sel, rc, fadeOut); + return animationGroup; + } + */ + }); +} + ++(void)aveElCapitanSwizzleWindowTransformAnimation__animationDurationForAnimationType +{ + Class class = NSClassFromString(@"WindowTransformAnimation"); + SEL sel = @selector(_animationDurationForAnimationType:); + AveReplaceInstanceMethod(class, sel, ^NSTimeInterval(id obj, NSInteger type){ + return 0.0; + }); +} + ++(void)aveElCapitanSwizzleFullScreenWindowController_animateModalWindowClose +{ + Class class = NSClassFromString(@"FullScreenWindowController"); + SEL sel = @selector(_animateModalWindowClose:); + __block IMP originalImplementation = AveReplaceInstanceMethod(class, sel, ^(id obj, id sender){ + elCapitanOverrideTransactionDurationToZero = YES; + if(originalImplementation != NULL) + { + ((void(*)(id, SEL, id))originalImplementation)(obj, sel, sender); + } + + elCapitanOverrideTransactionDurationToZero = NO; + }); +} + + +// ensure the selectors exist +-(CAAnimationGroup*)_internalTransitionAnimationWithDestination:(CGRect)rc fadeOut:(BOOL)fadeOut +{ + return nil; +} +-(void)animateFrom:(id)from to:(id)to withCompletion:(id)completion +{ +} +-(NSTimeInterval)_animationDurationForAnimationType:(NSInteger)type +{ + return 0.0; +} + +-(void)_animateModalWindowClose:(id)sender +{ +} + +#pragma mark - Mavericks and Yosemite + +#pragma mark shouldDoPopOutAnimation // use the same names as the method we swizzle, so the selectors exist -(BOOL)shouldDoPopOutAnimation { @@ -25,7 +208,7 @@ +(void)aveSwizzlePopoutAnimation Class class = NSClassFromString(@"DocumentEditor"); Method orig = class_getInstanceMethod(class, @selector(shouldDoPopOutAnimation)); Method repl = class_getInstanceMethod(self, @selector(shouldDoPopOutAnimation)); - NSLog(@"Ave: Swizzle shouldDoPopOutAnimation: %p -> %p", orig, repl); + NSLog(@"Ave: Swizzle %@ shouldDoPopOutAnimation: %p -> %p", class, orig, repl); method_exchangeImplementations(orig, repl); } @@ -58,8 +241,14 @@ +(void)swizzlePerformSendAnimation // -[DocumentEditor _sendAnimationCompleted] Class class = NSClassFromString(@"DocumentEditor"); Method orig = class_getInstanceMethod(class, @selector(_performSendAnimation)); + if(nil == orig || nil == class) + { + class = NSClassFromString(@"ComposeWindowController"); + orig = class_getInstanceMethod(class, @selector(_performSendAnimation)); + } + Method repl = class_getInstanceMethod(self, @selector(_performSendAnimation)); - NSLog(@"Ave: Swizzle _performSendAnimation: %p -> %p", orig, repl); + NSLog(@"Ave: Swizzle %@ _performSendAnimation: %p -> %p", class, orig, repl); method_exchangeImplementations(orig, repl); } @@ -87,9 +276,23 @@ +(void)aveSwizzleWindowTransformAnimation +(void)load { NSLog(@"Ave: Loaded, time to start swizzling"); - [self aveSwizzlePopoutAnimation]; - [self swizzlePerformSendAnimation]; - [self aveSwizzleWindowTransformAnimation]; + if([self isElCapitan]) + { + [self aveElCapitanSwizzleCATransactionSetAnimationDuration]; + [self aveElCapitanSwizzleNSAnimationContextSetDuration]; + [self aveElCapitanSwizzleComposeWindowControllerPerformSendAnimation]; + [self aveElCapitanSwizzlePopoutAnimationController_animateFrom_to_withCompletion]; + [self aveElCapitanswizzlePopoutAnimationController_internalTransitionAnimationWithDestination_fadeOut]; + [self aveElCapitanSwizzleWindowTransformAnimation__animationDurationForAnimationType]; + [self aveElCapitanSwizzleFullScreenWindowController_animateModalWindowClose]; + } + else + { + [self aveSwizzlePopoutAnimation]; + [self swizzlePerformSendAnimation]; + [self aveSwizzleWindowTransformAnimation]; + } + } #pragma mark MVMailBundle class methods diff --git a/AveNoAnimationsInMailPlugin/AveNoAnimationsInMailPlugin-Info.plist b/AveNoAnimationsInMailPlugin/AveNoAnimationsInMailPlugin-Info.plist index 41b52e3..624e43f 100644 --- a/AveNoAnimationsInMailPlugin/AveNoAnimationsInMailPlugin-Info.plist +++ b/AveNoAnimationsInMailPlugin/AveNoAnimationsInMailPlugin-Info.plist @@ -21,9 +21,9 @@ CFBundleSignature ???? CFBundleVersion - 1.1.2 + 1.2 NSHumanReadableCopyright - Copyright © 2014 AveApps. All rights reserved. + Copyright © 2015 AveApps. All rights reserved. NSPrincipalClass AveHookBootstrapper SupportedPluginCompatibilityUUIDs diff --git a/Output/AveNoAnimationsInMailPlugin.mailbundle.zip b/Output/AveNoAnimationsInMailPlugin.mailbundle.zip index fa0369a..b7cb0ef 100644 Binary files a/Output/AveNoAnimationsInMailPlugin.mailbundle.zip and b/Output/AveNoAnimationsInMailPlugin.mailbundle.zip differ diff --git a/Output/AveNoAnimationsInMailPlugin.mailbundle/Contents/Info.plist b/Output/AveNoAnimationsInMailPlugin.mailbundle/Contents/Info.plist index bd62429..7c01d12 100644 --- a/Output/AveNoAnimationsInMailPlugin.mailbundle/Contents/Info.plist +++ b/Output/AveNoAnimationsInMailPlugin.mailbundle/Contents/Info.plist @@ -3,7 +3,7 @@ BuildMachineOSBuild - 15B17c + 15A282b CFBundleDevelopmentRegion English CFBundleExecutable @@ -25,7 +25,7 @@ MacOSX CFBundleVersion - 1.1.2 + 1.2 DTCompiler com.apple.compilers.llvm.clang.1_0 DTPlatformBuild @@ -41,7 +41,7 @@ DTXcodeBuild 7A220 NSHumanReadableCopyright - Copyright © 2014 AveApps. All rights reserved. + Copyright © 2015 AveApps. All rights reserved. NSPrincipalClass AveHookBootstrapper SupportedPluginCompatibilityUUIDs diff --git a/Output/AveNoAnimationsInMailPlugin.mailbundle/Contents/MacOS/AveNoAnimationsInMailPlugin b/Output/AveNoAnimationsInMailPlugin.mailbundle/Contents/MacOS/AveNoAnimationsInMailPlugin index 31303c5..44984aa 100755 Binary files a/Output/AveNoAnimationsInMailPlugin.mailbundle/Contents/MacOS/AveNoAnimationsInMailPlugin and b/Output/AveNoAnimationsInMailPlugin.mailbundle/Contents/MacOS/AveNoAnimationsInMailPlugin differ diff --git a/Output/DisableMailAnimationsForOSX.pkg b/Output/DisableMailAnimationsForOSX.pkg index c41e003..89a2494 100644 Binary files a/Output/DisableMailAnimationsForOSX.pkg and b/Output/DisableMailAnimationsForOSX.pkg differ diff --git a/Output/repackage.sh b/Output/repackage.sh index b837e1f..f1111c7 100644 --- a/Output/repackage.sh +++ b/Output/repackage.sh @@ -17,7 +17,7 @@ cat /tmp/Package.unpkg/avenoanimationsinmailplugin.pkg/Payload | gzip -d | cpio # copying files echo "Copying files" -cp -r $CWD/AveNoAnimationsInMailPlugin.mailbundle /tmp/Package-inner.pkg/ +cp -r "$CWD/AveNoAnimationsInMailPlugin.mailbundle" /tmp/Package-inner.pkg/ rm /tmp/Package.unpkg/avenoanimationsinmailplugin.pkg/Payload rm /tmp/Package.unpkg/avenoanimationsinmailplugin.pkg/Bom @@ -36,7 +36,7 @@ echo "$NUMFILES files, $NUMKBYTES kb" sed -i '' -e 's/installKBytes="[-0-9]*"/installKBytes="'$NUMKBYTES'"/' /tmp/Package.unpkg/avenoanimationsinmailplugin.pkg/PackageInfo sed -i '' -e 's/numberOfFiles="[-0-9]*"/numberOfFiles="'$NUMFILES'"/' /tmp/Package.unpkg/avenoanimationsinmailplugin.pkg/PackageInfo -pkgutil --flatten /tmp/Package.unpkg/ $CWD/DisableMailAnimationsForOSX.pkg +pkgutil --flatten /tmp/Package.unpkg/ "$CWD/DisableMailAnimationsForOSX.pkg" rm -r /tmp/Package.unpkg 2> /dev/null rm -r /tmp/Package-inner.pkg 2> /dev/null