diff --git a/README.md b/README.md index 66121b8..ecc2dbf 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ This is a lovely feature for most apps, but if your app displays sensitive infor This plugin flags your app so that it doesn't show your users' sensitive data in the task switcher. It sets the [FLAG_SECURE](http://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_SECURE) flag in Android (which also prevents manual screenshots from being taken) and hides the window in iOS. -On iOS this plugin will show your splashscreen in the app switcher, but it requires your splashscreens basesname to be named `Default`. E.g. `Default~iphone.png` and `Default-568@2x~iphone.png`. +On iOS this plugin will try to show your splashscreen in the app switcher. It will search for splashscreens prefixed by `Default` or the value of the key `UILaunchImageFile` in your .plist file. +If it fails to find a splashscreen for a specific device or orientation (portrait or landscape), a black screen is shown instead. Installation ------------ diff --git a/plugin.xml b/plugin.xml index dec0b69..d2a743b 100644 --- a/plugin.xml +++ b/plugin.xml @@ -1,5 +1,5 @@ - + PrivacyScreenPlugin Secures your app from displaying a screenshot in task switchers under Android and iOS. Keeps sensitive information private. diff --git a/src/ios/AppDelegate+privacyscreen.h b/src/ios/AppDelegate+privacyscreen.h index 7efe49a..99fe7e2 100644 --- a/src/ios/AppDelegate+privacyscreen.h +++ b/src/ios/AppDelegate+privacyscreen.h @@ -6,8 +6,18 @@ */ #import "AppDelegate.h" +typedef struct { + BOOL iPhone; + BOOL iPad; + BOOL iPhone5; + BOOL iPhone6; + BOOL iPhone6Plus; + BOOL retina; + +} CDV_iOSDevice; + @interface AppDelegate (privacyscreen) - - (void)applicationWillResignActive:(UIApplication *)application; - - (void)applicationDidBecomeActive:(UIApplication *)application; +- (void)applicationWillResignActive:(UIApplication *)application; +- (void)applicationDidBecomeActive:(UIApplication *)application; -@end +@end \ No newline at end of file diff --git a/src/ios/AppDelegate+privacyscreen.m b/src/ios/AppDelegate+privacyscreen.m index db25a87..fefe81a 100644 --- a/src/ios/AppDelegate+privacyscreen.m +++ b/src/ios/AppDelegate+privacyscreen.m @@ -11,63 +11,114 @@ @implementation AppDelegate (privacyscreen) -// Taken from https://github.com/phonegap-build/PushPlugin/blob/master/src/ios/AppDelegate%2Bnotification.m -// its dangerous to override a method from within a category. -// Instead we will use method swizzling. we set this up in the load call. -+ (void)load +- (void)applicationDidBecomeActive:(UIApplication *)application { - Method original, swizzled; - - original = class_getInstanceMethod(self, @selector(init)); - swizzled = class_getInstanceMethod(self, @selector(swizzled_init)); - method_exchangeImplementations(original, swizzled); + if (imageView == NULL) { + self.window.hidden = NO; + } else { + [imageView removeFromSuperview]; + } } -- (AppDelegate *)swizzled_init +- (void)applicationWillResignActive:(UIApplication *)application { - if ([UIApplication respondsToSelector:@selector(ignoreSnapshotOnNextApplicationLaunch:)]) { - [[UIApplication sharedApplication] ignoreSnapshotOnNextApplicationLaunch]; - // Add any notification observers here... - } - // This actually calls the original init method over in AppDelegate. Equivilent to calling super - // on an overrided method, this is not recursive, although it appears that way. neat huh? - return [self swizzled_init]; + NSString *imgName = [self getImageName:self.viewController.interfaceOrientation delegate:(id)self.viewController device:[self getCurrentDevice]]; + UIImage *splash = [UIImage imageNamed:imgName]; + if (splash == NULL) { + self.window.hidden = YES; + } else { + imageView = [[UIImageView alloc]initWithFrame:[self.viewController.view bounds]]; + [imageView setImage:splash]; + [self.viewController.view addSubview:imageView]; + } } -- (void)applicationDidBecomeActive:(UIApplication *)application +// Code below borrowed from the CDV splashscreen plugin @ https://github.com/apache/cordova-plugin-splashscreen +// Made some adjustments though, becuase landscape splashscreens are not available for iphone < 6 plus +- (CDV_iOSDevice) getCurrentDevice { - if (imageView == NULL && [[UIApplication sharedApplication] respondsToSelector:@selector(ignoreSnapshotOnNextApplicationLaunch:)]) { - [[UIApplication sharedApplication] ignoreSnapshotOnNextApplicationLaunch]; - self.window.hidden = NO; - } else { - [imageView removeFromSuperview]; - } + CDV_iOSDevice device; + + UIScreen* mainScreen = [UIScreen mainScreen]; + CGFloat mainScreenHeight = mainScreen.bounds.size.height; + CGFloat mainScreenWidth = mainScreen.bounds.size.width; + + int limit = MAX(mainScreenHeight,mainScreenWidth); + + device.iPad = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad); + device.iPhone = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone); + device.retina = ([mainScreen scale] == 2.0); + device.iPhone5 = (device.iPhone && limit == 568.0); + // note these below is not a true device detect, for example if you are on an + // iPhone 6/6+ but the app is scaled it will prob set iPhone5 as true, but + // this is appropriate for detecting the runtime screen environment + device.iPhone6 = (device.iPhone && limit == 667.0); + device.iPhone6Plus = (device.iPhone && limit == 736.0); + + return device; } -- (void)applicationWillResignActive:(UIApplication *)application +- (NSString*)getImageName:(UIInterfaceOrientation)currentOrientation delegate:(id)orientationDelegate device:(CDV_iOSDevice)device { - // for now, assuming 'Default' is the basename of the splashscreen, with a fallback to hiding the window - UIImage *splash = [self imageNamedForDevice: @"Default"]; - if (splash == NULL && [[UIApplication sharedApplication] respondsToSelector:@selector(ignoreSnapshotOnNextApplicationLaunch:)]) { - [[UIApplication sharedApplication] ignoreSnapshotOnNextApplicationLaunch]; - self.window.hidden = YES; + // Use UILaunchImageFile if specified in plist. Otherwise, use Default. + NSString* imageName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UILaunchImageFile"]; + + NSUInteger supportedOrientations = [orientationDelegate supportedInterfaceOrientations]; + + // Checks to see if the developer has locked the orientation to use only one of Portrait or Landscape + BOOL supportsLandscape = (supportedOrientations & UIInterfaceOrientationMaskLandscape); + BOOL supportsPortrait = (supportedOrientations & UIInterfaceOrientationMaskPortrait || supportedOrientations & UIInterfaceOrientationMaskPortraitUpsideDown); + // this means there are no mixed orientations in there + BOOL isOrientationLocked = !(supportsPortrait && supportsLandscape); + + if (imageName) { + imageName = [imageName stringByDeletingPathExtension]; + } else { + imageName = @"Default"; + } + + BOOL isLandscape = supportsLandscape && + (currentOrientation == UIInterfaceOrientationLandscapeLeft || currentOrientation == UIInterfaceOrientationLandscapeRight); + + if (device.iPhone5) { // does not support landscape + imageName = isLandscape ? nil : [imageName stringByAppendingString:@"-568h"]; + } else if (device.iPhone6) { // does not support landscape + imageName = isLandscape ? nil : [imageName stringByAppendingString:@"-667h"]; + } else if (device.iPhone6Plus) { // supports landscape + if (isOrientationLocked) { + imageName = [imageName stringByAppendingString:(supportsLandscape ? @"-Landscape" : @"")]; } else { - imageView = [[UIImageView alloc]initWithFrame:[self.window frame]]; - [imageView setImage:splash]; - [UIApplication.sharedApplication.keyWindow.subviews.lastObject addSubview:imageView]; + switch (currentOrientation) { + case UIInterfaceOrientationLandscapeLeft: + case UIInterfaceOrientationLandscapeRight: + imageName = [imageName stringByAppendingString:@"-Landscape"]; + break; + default: + break; + } } -} - -- (UIImage*)imageNamedForDevice:(NSString*)name -{ - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { - if (([UIScreen mainScreen].bounds.size.height * [UIScreen mainScreen].scale) >= 1136.0f) { - name = [name stringByAppendingString:@"-568h@2x"]; - } - } else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { - name = [name stringByAppendingString:@"-Portrait"]; + imageName = [imageName stringByAppendingString:@"-736h"]; + + } else if (device.iPad) { // supports landscape + if (isOrientationLocked) { + imageName = [imageName stringByAppendingString:(supportsLandscape ? @"-Landscape" : @"-Portrait")]; + } else { + switch (currentOrientation) { + case UIInterfaceOrientationLandscapeLeft: + case UIInterfaceOrientationLandscapeRight: + imageName = [imageName stringByAppendingString:@"-Landscape"]; + break; + + case UIInterfaceOrientationPortrait: + case UIInterfaceOrientationPortraitUpsideDown: + default: + imageName = [imageName stringByAppendingString:@"-Portrait"]; + break; + } } - return [UIImage imageNamed: name]; + } + + return imageName; } -@end +@end \ No newline at end of file