blob: af931347aa71e6cc96e6e53f8d1d48b9d8a9b776 [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
/*
NOTE: plugman/cordova cli should have already installed this,
but you need the value UIViewControllerBasedStatusBarAppearance
in your Info.plist as well to set the styles in iOS 7
*/
#import "CDVStatusBar.h"
#import <objc/runtime.h>
#import <Cordova/CDVViewController.h>
static const void *kHideStatusBar = &kHideStatusBar;
static const void *kStatusBarStyle = &kStatusBarStyle;
@interface CDVViewController (StatusBar)
@property (nonatomic, retain) id sb_hideStatusBar;
@property (nonatomic, retain) id sb_statusBarStyle;
@end
@implementation CDVViewController (StatusBar)
@dynamic sb_hideStatusBar;
@dynamic sb_statusBarStyle;
- (id)sb_hideStatusBar {
return objc_getAssociatedObject(self, kHideStatusBar);
}
- (void)setSb_hideStatusBar:(id)newHideStatusBar {
objc_setAssociatedObject(self, kHideStatusBar, newHideStatusBar, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)sb_statusBarStyle {
return objc_getAssociatedObject(self, kStatusBarStyle);
}
- (void)setSb_statusBarStyle:(id)newStatusBarStyle {
objc_setAssociatedObject(self, kStatusBarStyle, newStatusBarStyle, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL) prefersStatusBarHidden {
return [self.sb_hideStatusBar boolValue];
}
- (UIStatusBarStyle)preferredStatusBarStyle
{
return (UIStatusBarStyle)[self.sb_statusBarStyle intValue];
}
@end
@implementation CDVStatusBar
- (id)settingForKey:(NSString*)key
{
return [self.commandDelegate.settings objectForKey:[key lowercaseString]];
}
- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context
{
if ([keyPath isEqual:@"statusBarHidden"]) {
NSNumber* newValue = [change objectForKey:NSKeyValueChangeNewKey];
BOOL boolValue = [newValue boolValue];
[self.commandDelegate evalJs:[NSString stringWithFormat:@"StatusBar.isVisible = %@;", boolValue? @"false" : @"true" ]];
}
}
- (void)pluginInitialize
{
BOOL isiOS7 = (IsAtLeastiOSVersion(@"7.0"));
// init
NSNumber* uiviewControllerBasedStatusBarAppearance = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"];
_uiviewControllerBasedStatusBarAppearance = (uiviewControllerBasedStatusBarAppearance == nil || [uiviewControllerBasedStatusBarAppearance boolValue]) && isiOS7;
// observe the statusBarHidden property
[[UIApplication sharedApplication] addObserver:self forKeyPath:@"statusBarHidden" options:NSKeyValueObservingOptionNew context:NULL];
_statusBarOverlaysWebView = YES; // default
[self initializeStatusBarBackgroundView];
[self styleLightContent:nil]; // match default backgroundColor of #000000
self.viewController.view.autoresizesSubviews = YES;
NSString* setting;
setting = @"StatusBarOverlaysWebView";
if ([self settingForKey:setting]) {
self.statusBarOverlaysWebView = [(NSNumber*)[self settingForKey:setting] boolValue];
}
setting = @"StatusBarBackgroundColor";
if ([self settingForKey:setting]) {
[self _backgroundColorByHexString:[self settingForKey:setting]];
}
}
- (void) _ready:(CDVInvokedUrlCommand*)command
{
// set the initial value
[self.commandDelegate evalJs:[NSString stringWithFormat:@"StatusBar.isVisible = %@;", [UIApplication sharedApplication].statusBarHidden? @"false" : @"true" ]];
}
- (void) initializeStatusBarBackgroundView
{
CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
if (UIDeviceOrientationIsLandscape(self.viewController.interfaceOrientation)) {
// swap width and height. set origin to zero
statusBarFrame = CGRectMake(0, 0, statusBarFrame.size.height, statusBarFrame.size.width);
}
_statusBarBackgroundView = [[UIView alloc] initWithFrame:statusBarFrame];
_statusBarBackgroundView.backgroundColor = _statusBarBackgroundColor;
_statusBarBackgroundView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin);
_statusBarBackgroundView.autoresizesSubviews = YES;
}
- (void) setStatusBarOverlaysWebView:(BOOL)statusBarOverlaysWebView
{
// we only care about the latest iOS version or a change in setting
if (!IsAtLeastiOSVersion(@"7.0") || statusBarOverlaysWebView == _statusBarOverlaysWebView) {
return;
}
CGRect bounds = [[UIScreen mainScreen] bounds];
if (statusBarOverlaysWebView) {
[_statusBarBackgroundView removeFromSuperview];
if (UIDeviceOrientationIsLandscape(self.viewController.interfaceOrientation)) {
self.webView.frame = CGRectMake(0, 0, bounds.size.height, bounds.size.width);
} else {
self.webView.frame = bounds;
}
} else {
CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
[self initializeStatusBarBackgroundView];
CGRect frame = self.webView.frame;
if (UIDeviceOrientationIsLandscape(self.viewController.interfaceOrientation)) {
frame.origin.y = statusBarFrame.size.width;
frame.size.height -= statusBarFrame.size.width;
} else {
frame.origin.y = statusBarFrame.size.height;
frame.size.height -= statusBarFrame.size.height;
}
self.webView.frame = frame;
[self.webView.superview addSubview:_statusBarBackgroundView];
}
_statusBarOverlaysWebView = statusBarOverlaysWebView;
}
- (BOOL) statusBarOverlaysWebView
{
return _statusBarOverlaysWebView;
}
- (void) overlaysWebView:(CDVInvokedUrlCommand*)command
{
id value = [command.arguments objectAtIndex:0];
if (!([value isKindOfClass:[NSNumber class]])) {
value = [NSNumber numberWithBool:YES];
}
self.statusBarOverlaysWebView = [value boolValue];
}
- (void) refreshStatusBarAppearance
{
SEL sel = NSSelectorFromString(@"setNeedsStatusBarAppearanceUpdate");
if ([self.viewController respondsToSelector:sel]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self.viewController performSelector:sel withObject:nil];
#pragma clang diagnostic pop
}
}
- (void) setStyleForStatusBar:(UIStatusBarStyle)style
{
if (_uiviewControllerBasedStatusBarAppearance) {
CDVViewController* vc = (CDVViewController*)self.viewController;
vc.sb_statusBarStyle = [NSNumber numberWithInt:style];
[self refreshStatusBarAppearance];
} else {
[[UIApplication sharedApplication] setStatusBarStyle:style];
}
}
- (void) setStatusBarStyle:(NSString*)statusBarStyle
{
// default, lightContent, blackTranslucent, blackOpaque
NSString* lcStatusBarStyle = [statusBarStyle lowercaseString];
if ([lcStatusBarStyle isEqualToString:@"default"]) {
[self styleDefault:nil];
} else if ([lcStatusBarStyle isEqualToString:@"lightcontent"]) {
[self styleLightContent:nil];
} else if ([lcStatusBarStyle isEqualToString:@"blacktranslucent"]) {
[self styleBlackTranslucent:nil];
} else if ([lcStatusBarStyle isEqualToString:@"blackopaque"]) {
[self styleBlackOpaque:nil];
}
}
- (void) styleDefault:(CDVInvokedUrlCommand*)command
{
[self setStyleForStatusBar:UIStatusBarStyleDefault];
}
- (void) styleLightContent:(CDVInvokedUrlCommand*)command
{
[self setStyleForStatusBar:UIStatusBarStyleLightContent];
}
- (void) styleBlackTranslucent:(CDVInvokedUrlCommand*)command
{
[self setStyleForStatusBar:UIStatusBarStyleBlackTranslucent];
}
- (void) styleBlackOpaque:(CDVInvokedUrlCommand*)command
{
[self setStyleForStatusBar:UIStatusBarStyleBlackOpaque];
}
- (void) backgroundColorByName:(CDVInvokedUrlCommand*)command
{
id value = [command.arguments objectAtIndex:0];
if (!([value isKindOfClass:[NSString class]])) {
value = @"black";
}
SEL selector = NSSelectorFromString([value stringByAppendingString:@"Color"]);
if ([UIColor respondsToSelector:selector]) {
_statusBarBackgroundView.backgroundColor = [UIColor performSelector:selector];
}
}
- (void) _backgroundColorByHexString:(NSString*)hexString
{
unsigned int rgbValue = 0;
NSScanner* scanner = [NSScanner scannerWithString:hexString];
[scanner setScanLocation:1];
[scanner scanHexInt:&rgbValue];
_statusBarBackgroundColor = [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 blue:(rgbValue & 0xFF)/255.0 alpha:1.0];
_statusBarBackgroundView.backgroundColor = _statusBarBackgroundColor;
}
- (void) backgroundColorByHexString:(CDVInvokedUrlCommand*)command
{
NSString* value = [command.arguments objectAtIndex:0];
if (!([value isKindOfClass:[NSString class]])) {
value = @"#000000";
}
if (![value hasPrefix:@"#"] || [value length] < 7) {
return;
}
[self _backgroundColorByHexString:value];
}
- (void) hideStatusBar
{
if (_uiviewControllerBasedStatusBarAppearance) {
CDVViewController* vc = (CDVViewController*)self.viewController;
vc.sb_hideStatusBar = [NSNumber numberWithBool:YES];
[self refreshStatusBarAppearance];
} else {
UIApplication* app = [UIApplication sharedApplication];
[app setStatusBarHidden:YES];
}
}
- (void) hide:(CDVInvokedUrlCommand*)command
{
UIApplication* app = [UIApplication sharedApplication];
if (!app.isStatusBarHidden)
{
self.viewController.wantsFullScreenLayout = YES;
CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
[self hideStatusBar];
if (IsAtLeastiOSVersion(@"7.0")) {
[_statusBarBackgroundView removeFromSuperview];
}
if (!_statusBarOverlaysWebView) {
CGRect frame = self.webView.frame;
frame.origin.y = 0;
if (UIDeviceOrientationIsLandscape(self.viewController.interfaceOrientation)) {
frame.size.height += statusBarFrame.size.width;
} else {
frame.size.height += statusBarFrame.size.height;
}
self.webView.frame = frame;
}
_statusBarBackgroundView.hidden = YES;
}
}
- (void) showStatusBar
{
if (_uiviewControllerBasedStatusBarAppearance) {
CDVViewController* vc = (CDVViewController*)self.viewController;
vc.sb_hideStatusBar = [NSNumber numberWithBool:NO];
[self refreshStatusBarAppearance];
} else {
UIApplication* app = [UIApplication sharedApplication];
[app setStatusBarHidden:NO];
}
}
- (void) show:(CDVInvokedUrlCommand*)command
{
UIApplication* app = [UIApplication sharedApplication];
if (app.isStatusBarHidden)
{
BOOL isIOS7 = (IsAtLeastiOSVersion(@"7.0"));
self.viewController.wantsFullScreenLayout = isIOS7;
[self showStatusBar];
if (isIOS7) {
CGRect frame = self.webView.frame;
self.viewController.view.frame = [[UIScreen mainScreen] bounds];
CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
if (!self.statusBarOverlaysWebView) {
// there is a possibility that when the statusbar was hidden, it was in a different orientation
// from the current one. Therefore we need to expand the statusBarBackgroundView as well to the
// statusBar's current size
CGRect sbBgFrame = _statusBarBackgroundView.frame;
if (UIDeviceOrientationIsLandscape(self.viewController.interfaceOrientation)) {
frame.origin.y = statusBarFrame.size.width;
frame.size.height -= statusBarFrame.size.width;
sbBgFrame.size = CGSizeMake(statusBarFrame.size.height, statusBarFrame.size.width);
} else {
frame.origin.y = statusBarFrame.size.height;
frame.size.height -= statusBarFrame.size.height;
sbBgFrame.size = statusBarFrame.size;
}
_statusBarBackgroundView.frame = sbBgFrame;
[self.webView.superview addSubview:_statusBarBackgroundView];
}
self.webView.frame = frame;
} else {
CGRect bounds = [[UIScreen mainScreen] applicationFrame];
self.viewController.view.frame = bounds;
}
_statusBarBackgroundView.hidden = NO;
}
}
- (void) dealloc
{
[[UIApplication sharedApplication] removeObserver:self forKeyPath:@"statusBarHidden"];
}
@end