diff --git a/Frank.xcodeproj/project.pbxproj b/Frank.xcodeproj/project.pbxproj index 6f007cc9..8f1f03d6 100644 --- a/Frank.xcodeproj/project.pbxproj +++ b/Frank.xcodeproj/project.pbxproj @@ -71,6 +71,8 @@ 30E82E3417128AB500E5BC7C /* ResolutionCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 30E82E3117128AB500E5BC7C /* ResolutionCommand.m */; }; 30E82E3517128AB500E5BC7C /* ResolutionCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 30E82E3117128AB500E5BC7C /* ResolutionCommand.m */; }; 387BFA7D17036DDA007B9F17 /* UISlider+PublicAutomation.m in Sources */ = {isa = PBXBuildFile; fileRef = 387BFA7C17036DDA007B9F17 /* UISlider+PublicAutomation.m */; }; + 400632C717C61767005BAE46 /* SaveScreenshotInCameraRollCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 400632C517C61767005BAE46 /* SaveScreenshotInCameraRollCommand.h */; }; + 400632C817C61767005BAE46 /* SaveScreenshotInCameraRollCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 400632C617C61767005BAE46 /* SaveScreenshotInCameraRollCommand.m */; }; 4C1DD76C12BADFE100E10B8C /* OrientationCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1DD76812BADFE100E10B8C /* OrientationCommand.m */; }; 4C1DD76D12BADFE100E10B8C /* OrientationCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C1DD76912BADFE100E10B8C /* OrientationCommand.h */; }; 4C1DD76E12BADFE100E10B8C /* AppCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1DD76A12BADFE100E10B8C /* AppCommand.m */; }; @@ -313,6 +315,8 @@ 30E82E3017128AB500E5BC7C /* ResolutionCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResolutionCommand.h; sourceTree = ""; }; 30E82E3117128AB500E5BC7C /* ResolutionCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ResolutionCommand.m; sourceTree = ""; }; 387BFA7C17036DDA007B9F17 /* UISlider+PublicAutomation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UISlider+PublicAutomation.m"; sourceTree = ""; }; + 400632C517C61767005BAE46 /* SaveScreenshotInCameraRollCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SaveScreenshotInCameraRollCommand.h; sourceTree = ""; }; + 400632C617C61767005BAE46 /* SaveScreenshotInCameraRollCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SaveScreenshotInCameraRollCommand.m; sourceTree = ""; }; 4C1DD76812BADFE100E10B8C /* OrientationCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OrientationCommand.m; sourceTree = ""; }; 4C1DD76912BADFE100E10B8C /* OrientationCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OrientationCommand.h; sourceTree = ""; }; 4C1DD76A12BADFE100E10B8C /* AppCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppCommand.m; sourceTree = ""; }; @@ -699,6 +703,8 @@ C189EF1D16BB5A8000F8236D /* VersionCommand.m */, 30E82E3017128AB500E5BC7C /* ResolutionCommand.h */, 30E82E3117128AB500E5BC7C /* ResolutionCommand.m */, + 400632C517C61767005BAE46 /* SaveScreenshotInCameraRollCommand.h */, + 400632C617C61767005BAE46 /* SaveScreenshotInCameraRollCommand.m */, ); name = Commands; sourceTree = ""; @@ -866,6 +872,7 @@ 303CFCE416C80B830004BD05 /* DeviceCommand.h in Headers */, 30E82E3217128AB500E5BC7C /* ResolutionCommand.h in Headers */, 0626BFAE174300240020B33B /* DumpCommandRoute.h in Headers */, + 400632C717C61767005BAE46 /* SaveScreenshotInCameraRollCommand.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1240,6 +1247,7 @@ 387BFA7D17036DDA007B9F17 /* UISlider+PublicAutomation.m in Sources */, 0626BFAF174300240020B33B /* DumpCommandRoute.m in Sources */, 3078E33C175417AE0016889C /* NSObject+FrankAutomation.m in Sources */, + 400632C817C61767005BAE46 /* SaveScreenshotInCameraRollCommand.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/gem/lib/frank-cucumber/core_frank_steps.rb b/gem/lib/frank-cucumber/core_frank_steps.rb index f452ce95..c21bbe11 100644 --- a/gem/lib/frank-cucumber/core_frank_steps.rb +++ b/gem/lib/frank-cucumber/core_frank_steps.rb @@ -112,6 +112,10 @@ element_exists_and_is_not_hidden.should be_false end +# -- Save screenshots in the camera roll -- # +Given /^the device saves (\d+) screenshot(s)? in the camera roll$/ do |number,_| + frankly_save_screenshot_in_camera_roll number +end # -- Rotate -- # Given /^the device is in (a )?(landscape|portrait) orientation$/ do |_,orientation| diff --git a/gem/lib/frank-cucumber/frank_helper.rb b/gem/lib/frank-cucumber/frank_helper.rb index bdf7f6a6..e5757681 100644 --- a/gem/lib/frank-cucumber/frank_helper.rb +++ b/gem/lib/frank-cucumber/frank_helper.rb @@ -368,6 +368,14 @@ def frankly_set_orientation(orientation) return Gateway.evaluate_frankly_response( res, "set_orientation #{orientation}" ) end + # takes an screenshot and saves it an specific number of times in the camera roll. + # @param number the number of times the screenshot will be saved. + # @return always success, because there is no way of knowing wether the screenshots were succesfully saved or not. + def frankly_save_screenshot_in_camera_roll(number) + res = frank_server.send_post( 'save_screenshot_in_camera_roll', number ) + return Gateway.evaluate_frankly_response( res, "save_screenshot_in_camera_roll #{number}" ) + end + # @return [Boolean] Does the device running the application have accessibility enabled. # If accessibility is not enabled then a lot of Frank functionality will not work. def frankly_is_accessibility_enabled diff --git a/src/FrankServer.m b/src/FrankServer.m index 7a50f2d8..ddc216d8 100644 --- a/src/FrankServer.m +++ b/src/FrankServer.m @@ -28,6 +28,7 @@ #import "OrientationCommand.h" #import "LocationCommand.h" #import "IOSKeyboardCommand.h" +#import "SaveScreenshotInCameraRollCommand.h" #else #import "OSXKeyboardCommand.h" #endif @@ -73,6 +74,7 @@ - (id) initWithStaticFrankBundleNamed:(NSString *)bundleName [frankCommandRoute registerCommand:[[[OrientationCommand alloc]init]autorelease] withName:@"orientation"]; [frankCommandRoute registerCommand:[[[LocationCommand alloc]init]autorelease] withName:@"location"]; [frankCommandRoute registerCommand:[[[IOSKeyboardCommand alloc] init]autorelease] withName:@"type_into_keyboard"]; + [frankCommandRoute registerCommand:[[[SaveScreenshotInCameraRollCommand alloc] init]autorelease] withName:@"save_screenshot_in_camera_roll"]; #else [frankCommandRoute registerCommand:[[[SuccessCommand alloc]init]autorelease] withName:@"orientation"]; [frankCommandRoute registerCommand:[[[SuccessCommand alloc]init]autorelease] withName:@"location"]; diff --git a/src/SaveScreenshotInCameraRollCommand.h b/src/SaveScreenshotInCameraRollCommand.h new file mode 100644 index 00000000..8c4a026e --- /dev/null +++ b/src/SaveScreenshotInCameraRollCommand.h @@ -0,0 +1,15 @@ +// +// SaveScreenshotInCameraRollCommand.h +// Frank +// +// Created by Sergio Padrino on 22/08/13. +// +// + +#import + +#import "FrankCommandRoute.h" + +@interface SaveScreenshotInCameraRollCommand : NSObject + +@end diff --git a/src/SaveScreenshotInCameraRollCommand.m b/src/SaveScreenshotInCameraRollCommand.m new file mode 100644 index 00000000..4181d295 --- /dev/null +++ b/src/SaveScreenshotInCameraRollCommand.m @@ -0,0 +1,62 @@ +// +// SaveScreenshotInCameraRollCommand.m +// Frank +// +// Created by Sergio Padrino on 22/08/13. +// +// + +#import "SaveScreenshotInCameraRollCommand.h" + +#import "FranklyProtocolHelper.h" +#import "UIImage+Frank.h" + +@interface SaveScreenshotInCameraRollCommand () + +@property (nonatomic, assign) NSUInteger numberOfPhotos; +@property (nonatomic, retain) UIImage *screenshot; + +@end + +@implementation SaveScreenshotInCameraRollCommand + +- (void)dealloc +{ + [_screenshot release]; + + [super dealloc]; +} + +- (NSString *)handleCommandWithRequestBody:(NSString *)requestBody { + self.numberOfPhotos = [requestBody integerValue]; + + self.screenshot = [UIImage imageFromApplication:YES + resultInPortrait:UIDeviceOrientationIsPortrait([[UIDevice currentDevice] orientation])]; + + // This code is running in the main thread and UIImageWriteToSavedPhotosAlbum also calls its callback in the main + // thread. Therefore, it's impossible to make this process completely synchronous as needed (waiting for all the + // images to be saved and then return the results), so all we can do is start a chain of calls for saving the images + // and hope for the app to live long enough so that all the images are written in the camera roll. + [self saveScreenshotInPhotosAlbum]; + + return [FranklyProtocolHelper generateSuccessResponseWithoutResults]; +} + +- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo { + self.numberOfPhotos -= 1; + + // Continue saving images until all have been saved + if (self.numberOfPhotos > 0) { + [self saveScreenshotInPhotosAlbum]; + } + else { + self.screenshot = nil; + } +} + +- (void)saveScreenshotInPhotosAlbum +{ + UIImageWriteToSavedPhotosAlbum(self.screenshot, self, @selector(image:didFinishSavingWithError:contextInfo:), nil); +} + +@end