You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by GitBox <gi...@apache.org> on 2018/09/16 18:12:36 UTC

[GitHub] janpio closed pull request #95: Show splashscreen everytime a new page is loaded.

janpio closed pull request #95: Show splashscreen everytime a new page is loaded.
URL: https://github.com/apache/cordova-plugin-splashscreen/pull/95
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/LICENSE_MMMaterialDesignSpinner b/LICENSE_MMMaterialDesignSpinner
new file mode 100644
index 00000000..bfaa969e
--- /dev/null
+++ b/LICENSE_MMMaterialDesignSpinner
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Mike Maxwell <mm...@vertical.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/README.md b/README.md
index 71930dae..b5fe2577 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,11 @@ Report issues with this plugin on the [Apache Cordova issue tracker][Apache Cord
     // you may also install directly from this repo
     cordova plugin add https://github.com/apache/cordova-plugin-splashscreen.git
 
+## Platform specific UI Notes
+### iOS
+The Material Design inspired loading indicator developed by Mike Maxwell has been included and enabled on iOS 7+ if the user enables support for it in the plugin.xml configuration.
+iPad Pro splashscreen support has been added and some rotation bugs on iOS 8 have been addressed.
+
 ## Supported Platforms
 
 - Amazon Fire OS
@@ -89,25 +94,60 @@ Please notice that the value of the "src" attribute is relative to the project d
     <rim:splash src="res/screen/blackberry/splashscreen.png"/>
 </platform>
 
-<preference name="SplashScreenDelay" value="10000" />
+<preference name="SplashScreenDelay" value="3000" />
 ```
 
 ## Preferences
 
 #### config.xml
 
--  __AutoHideSplashScreen__ (boolean, default to `true`). Indicates wherether hide splash screen automatically or not. Splash screen hidden after amount of time specified in the `SplashScreenDelay` preference.
+-  __AutoHideSplashScreen__ (boolean, default to `true`). Indicates wether hide splash screen automatically or not after page load. Splash screen hidden after amount of time specified in the `SplashScreenDelay` preference.
 
 ```xml
     <preference name="AutoHideSplashScreen" value="true" />
 ```
 
--  __SplashScreenDelay__ (number, default to 3000). Amount of time in milliseconds to wait before automatically hide splash screen.
-
+-  __SplashScreenDelay__ (number, default to 3000). Amount of time in milliseconds to wait before automatically hide splash screen after page load.
 ```xml
     <preference name="SplashScreenDelay" value="3000" />
 ```
 
+-  __SplashScreenTimeout__ (number, default to 20000). Amount of time in milliseconds to wait before automatically hide splash screen.
+
+```xml
+    <preference name="SplashScreenTimeout" value="20000" />
+```
+
+-  __MaterialLikeSpinner__ (boolean, default to `true`). Indicates whether we should use the Material Design inspired spinner on iOS 7+ or the default Activity Indicator.
+
+```xml
+    <preference name="MaterialLikeSpinner" value="true" />
+```
+
+-  __MaterialLikeSpinner__ (boolean, default to `true`). Indicates whether we should use the Material Design inspired spinner on iOS 7+ or the default Activity Indicator.
+
+```xml
+    <preference name="MaterialLikeSpinner" value="true" />
+```
+
+-  __MaterialLikeSpinnerTrackRadius__ (number, default to 20). Radius of the material design inspired spinner track.
+
+```xml
+    <preference name="MaterialLikeSpinnerTrackRadius" value="20" />
+```
+
+-  __MaterialLikeSpinnerTrackWidth__ (number, default to 2). Width of the material design inspired spinner track.
+
+```xml
+    <preference name="MaterialLikeSpinnerTrackWidth" value="2" />
+```
+
+-  __MaterialLikeSpinnerTrackTintHexColor__ (number, default to #d3d3d3). Hex color for the material design inspired spinner track.
+
+```xml
+    <preference name="MaterialLikeSpinnerTrackTintHexColor" value="#d3d3d3" />
+```
+
 ### Android Quirks
 
 In your `config.xml`, you need to add the following preferences:
diff --git a/plugin.xml b/plugin.xml
index 574289c3..a6a41e87 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -34,6 +34,7 @@
 
     <js-module src="www/splashscreen.js" name="SplashScreen">
         <clobbers target="navigator.splashscreen" />
+        <clobbers target="window.nativeWrapper.splashscreen" />
     </js-module>
 
     <!-- android -->
@@ -74,10 +75,14 @@
 		    </feature>
         </config-file>
 
+        <header-file src="src/ios/CDVSplashScreenSystemVersion.h" />
         <header-file src="src/ios/CDVSplashScreen.h" />
         <source-file src="src/ios/CDVSplashScreen.m" />
         <header-file src="src/ios/CDVViewController+SplashScreen.h" />
         <source-file src="src/ios/CDVViewController+SplashScreen.m" />
+        <header-file src="src/ios/MMMaterialDesignSpinner.h" />
+        <source-file src="src/ios/MMMaterialDesignSpinner.m" />
+        <header-file src="src/ios/ActivityTracking.h" />
 
 	    <framework src="CoreGraphics.framework" />
     </platform>
diff --git a/src/android/SplashScreen.java b/src/android/SplashScreen.java
index d9ac66a2..a55f82cd 100644
--- a/src/android/SplashScreen.java
+++ b/src/android/SplashScreen.java
@@ -51,12 +51,13 @@ Licensed to the Apache Software Foundation (ASF) under one
     // Cordova 3.x.x has a copy of this plugin bundled with it (SplashScreenInternal.java).
     // Enable functionality only if running on 4.x.x.
     private static final boolean HAS_BUILT_IN_SPLASH_SCREEN = Integer.valueOf(CordovaWebView.CORDOVA_VERSION.split("\\.")[0]) < 4;
-    private static final int DEFAULT_SPLASHSCREEN_DURATION = 3000;
     private static Dialog splashDialog;
     private static ProgressDialog spinnerDialog;
     private static boolean firstShow = true;
     private static boolean lastHideAfterDelay; // https://issues.apache.org/jira/browse/CB-9094
 
+    private static boolean splashShown;
+
     /**
      * Displays the splash drawable.
      */
@@ -81,8 +82,7 @@ protected void pluginInitialize() {
         if (HAS_BUILT_IN_SPLASH_SCREEN) {
             return;
         }
-        // Make WebView invisible while loading URL
-        getView().setVisibility(View.INVISIBLE);
+
         int drawableId = preferences.getInteger("SplashDrawableId", 0);
         if (drawableId == 0) {
             String splashResource = preferences.getString("SplashScreen", "screen");
@@ -116,8 +116,7 @@ private boolean isMaintainAspectRatio () {
     }
 
     private int getFadeDuration () {
-        int fadeSplashScreenDuration = preferences.getBoolean("FadeSplashScreen", true) ?
-            preferences.getInteger("FadeSplashScreenDuration", DEFAULT_SPLASHSCREEN_DURATION) : 0;
+        int fadeSplashScreenDuration = preferences.getBoolean("FadeSplashScreen", true) ? preferences.getInteger("FadeSplashScreenDuration", 0) : 0;
 
         if (fadeSplashScreenDuration < 30) {
             // [CB-9750] This value used to be in decimal seconds, so we will assume that if someone specifies 10
@@ -153,15 +152,31 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo
         if (action.equals("hide")) {
             cordova.getActivity().runOnUiThread(new Runnable() {
                 public void run() {
+                    splashShown = false;
                     webView.postMessage("splashscreen", "hide");
                 }
             });
         } else if (action.equals("show")) {
             cordova.getActivity().runOnUiThread(new Runnable() {
                 public void run() {
+                    splashShown = true;
                     webView.postMessage("splashscreen", "show");
                 }
             });
+        } else if (action.equals("onPageLoading")) {
+        	cordova.getActivity().runOnUiThread(new Runnable() {
+                    public void run() {
+                    splashShown = true;
+                    webView.postMessage("splashscreen", "onPageLoading");
+                    }
+                });
+        } else if (action.equals("onPageLoaded")) {
+            cordova.getActivity().runOnUiThread(new Runnable() {
+                public void run() {
+                    splashShown = false;
+                    webView.postMessage("splashscreen", "onPageLoaded");
+                }
+            });
         } else {
             return false;
         }
@@ -177,9 +192,15 @@ public Object onMessage(String id, Object data) {
         }
         if ("splashscreen".equals(id)) {
             if ("hide".equals(data.toString())) {
-                this.removeSplashScreen(false);
-            } else {
+                if(splashShown){
+                    this.removeSplashScreen(false);
+                }
+            } else if ("show".equals(data.toString())){
+                this.showSplashScreen(false);
+            } else if ("onPageLoading".equals(data.toString())) {
                 this.showSplashScreen(false);
+            } else if ("onPageLoaded".equals(data.toString())) {
+                this.removeSplashScreen(false);
             }
         } else if ("spinner".equals(id)) {
             if ("stop".equals(data.toString())) {
@@ -255,11 +276,10 @@ public void onAnimationRepeat(Animation animation) {
      */
     @SuppressWarnings("deprecation")
     private void showSplashScreen(final boolean hideAfterDelay) {
-        final int splashscreenTime = preferences.getInteger("SplashScreenDelay", DEFAULT_SPLASHSCREEN_DURATION);
         final int drawableId = preferences.getInteger("SplashDrawableId", 0);
 
         final int fadeSplashScreenDuration = getFadeDuration();
-        final int effectiveSplashDuration = Math.max(0, splashscreenTime - fadeSplashScreenDuration);
+        final int maxSplashDuration = preferences.getInteger("SplashScreenTimeout", 25000 - fadeSplashScreenDuration);
 
         lastHideAfterDelay = hideAfterDelay;
 
@@ -267,7 +287,7 @@ private void showSplashScreen(final boolean hideAfterDelay) {
         if (splashDialog != null && splashDialog.isShowing()) {
             return;
         }
-        if (drawableId == 0 || (splashscreenTime <= 0 && hideAfterDelay)) {
+        if (drawableId == 0 || (maxSplashDuration <= 0 && hideAfterDelay)) {
             return;
         }
 
@@ -323,7 +343,7 @@ public void run() {
                                 removeSplashScreen(false);
                             }
                         }
-                    }, effectiveSplashDuration);
+                    }, maxSplashDuration);
                 }
             }
         });
diff --git a/src/ios/ActivityTracking.h b/src/ios/ActivityTracking.h
new file mode 100644
index 00000000..6199cfe0
--- /dev/null
+++ b/src/ios/ActivityTracking.h
@@ -0,0 +1,33 @@
+/*
+ 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.
+ */
+
+#import <UIKit/UIKit.h>
+
+@protocol ActivityTracking
+
+@property(nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle; // default is UIActivityIndicatorViewStyleWhite
+@property(nonatomic) BOOL                         hidesWhenStopped;           // default is YES. calls -setHidden when animating gets set to NO
+
+@property (nullable, readwrite, nonatomic, strong) UIColor *color NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;
+
+- (void)startAnimating;
+- (void)stopAnimating;
+- (BOOL)isAnimating;
+
+@end
diff --git a/src/ios/CDVSplashScreen.h b/src/ios/CDVSplashScreen.h
index ec5d6022..148c4fb7 100644
--- a/src/ios/CDVSplashScreen.h
+++ b/src/ios/CDVSplashScreen.h
@@ -6,9 +6,9 @@
  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
@@ -23,6 +23,7 @@
 typedef struct {
     BOOL iPhone;
     BOOL iPad;
+    BOOL iPadPro;
     BOOL iPhone4;
     BOOL iPhone5;
     BOOL iPhone6;
@@ -31,8 +32,10 @@ typedef struct {
     
 } CDV_iOSDevice;
 
+@protocol ActivityTracking;
+
 @interface CDVSplashScreen : CDVPlugin {
-    UIActivityIndicatorView* _activityView;
+    UIView<ActivityTracking>* _activityView;
     UIImageView* _imageView;
     NSString* _curImageName;
     BOOL _visible;
diff --git a/src/ios/CDVSplashScreen.m b/src/ios/CDVSplashScreen.m
index 1d7b9554..c56d016c 100644
--- a/src/ios/CDVSplashScreen.m
+++ b/src/ios/CDVSplashScreen.m
@@ -6,9 +6,9 @@ Licensed to the Apache Software Foundation (ASF) under one
  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
@@ -22,15 +22,31 @@ Licensed to the Apache Software Foundation (ASF) under one
 #import <Cordova/CDVScreenOrientationDelegate.h>
 #import "CDVViewController+SplashScreen.h"
 
+#import "MMMaterialDesignSpinner.h"
+#import "ActivityTracking.h"
+#import "CDVSplashScreenSystemVersion.h"
+
 #define kSplashScreenDurationDefault 3000.0f
+#define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
+
 
+@interface CDVSplashScreen ()
+@property (readwrite, assign) BOOL isIOS8;
+@property (assign) BOOL firstPageLoaded;
+@property (assign) BOOL removeKVOListeners;
 
+@end
 @implementation CDVSplashScreen
 
 - (void)pluginInitialize
 {
+    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0") && SYSTEM_VERSION_LESS_THAN(@"9.0")) {
+        self.isIOS8 = YES;
+    }
+    
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageStartedLoading) name:CDVPluginResetNotification object:nil];
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad) name:CDVPageDidLoadNotification object:nil];
-
+    
     [self setVisible:YES];
 }
 
@@ -41,13 +57,35 @@ - (void)show:(CDVInvokedUrlCommand*)command
 
 - (void)hide:(CDVInvokedUrlCommand*)command
 {
+    [CDVSplashScreen cancelPreviousPerformRequestsWithTarget:self selector:@selector(hide:) object:nil];
     [self setVisible:NO andForce:YES];
 }
 
+- (void)onPageLoading:(CDVInvokedUrlCommand*)command
+{
+    [self pageStartedLoading];
+}
+
+- (void)onPageLoaded:(CDVInvokedUrlCommand*)command
+{
+    [self pageDidLoad];
+}
+
+- (void)pageStartedLoading
+{
+    [self setVisible:YES];
+    
+    id autoHideSplashScreenTimeoutValue = [self.commandDelegate.settings objectForKey:[@"SplashScreenTimeout" lowercaseString]];
+    if (autoHideSplashScreenTimeoutValue) {
+        float autoHideSplashScreenTimeout = [autoHideSplashScreenTimeoutValue floatValue] / 1000;
+        [self performSelector:@selector(hide:) withObject:nil afterDelay:autoHideSplashScreenTimeout];
+    }
+}
+
 - (void)pageDidLoad
 {
     id autoHideSplashScreenValue = [self.commandDelegate.settings objectForKey:[@"AutoHideSplashScreen" lowercaseString]];
-
+    
     // if value is missing, default to yes
     if ((autoHideSplashScreenValue == nil) || [autoHideSplashScreenValue boolValue]) {
         [self setVisible:NO];
@@ -69,19 +107,34 @@ - (void)createViews
      *     gray       = UIActivityIndicatorViewStyleGray
      *
      */
-
+    
     // Determine whether rotation should be enabled for this device
     // Per iOS HIG, landscape is only supported on iPad and iPhone 6+
     CDV_iOSDevice device = [self getCurrentDevice];
     BOOL autorotateValue = (device.iPad || device.iPhone6Plus) ?
-        [(CDVViewController *)self.viewController shouldAutorotateDefaultValue] :
-        NO;
+    [(CDVViewController *)self.viewController shouldAutorotateDefaultValue] :
+    NO;
+    
+    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0") && SYSTEM_VERSION_LESS_THAN(@"8.4")) { // there is a bug for rotation for iPhones on iOS 8 so enabling autorotation together with the fixes implemented in the methods updateBounds and setVisible solved the bug
+        
+        BOOL isIOS8_0 = SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0") && SYSTEM_VERSION_LESS_THAN(@"8.1");
+        BOOL shouldDeviceRotate = (!device.iPhone5 && !device.iPhone4 && !device.iPhone6);
+        autorotateValue = (shouldDeviceRotate || isIOS8_0)? YES : autorotateValue;
+    }
     
     [(CDVViewController *)self.viewController setEnabledAutorotation:autorotateValue];
+    self.viewController.view.userInteractionEnabled = NO;  // disable user interaction while splashscreen is shown
+    [self setupSplashScreenViews];
+    
+    self.firstPageLoaded = YES;
+    _destroyed = NO;
+}
 
+- (void)setupSplashScreenViews
+{
     NSString* topActivityIndicator = [self.commandDelegate.settings objectForKey:[@"TopActivityIndicator" lowercaseString]];
     UIActivityIndicatorViewStyle topActivityIndicatorStyle = UIActivityIndicatorViewStyleGray;
-
+    
     if ([topActivityIndicator isEqualToString:@"whiteLarge"])
     {
         topActivityIndicatorStyle = UIActivityIndicatorViewStyleWhiteLarge;
@@ -94,33 +147,61 @@ - (void)createViews
     {
         topActivityIndicatorStyle = UIActivityIndicatorViewStyleGray;
     }
-
+    
     UIView* parentView = self.viewController.view;
-    parentView.userInteractionEnabled = NO;  // disable user interaction while splashscreen is shown
-    _activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:topActivityIndicatorStyle];
+    id showMaterialSpinner = [self.commandDelegate.settings objectForKey: [@"MaterialLikeSpinner" lowercaseString]];
+    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0") && showMaterialSpinner && [showMaterialSpinner boolValue])
+    {
+        id showMaterialSpinnerTrackRadius = [self.commandDelegate.settings objectForKey: [@"MaterialLikeSpinnerTrackRadius" lowercaseString]];
+        CGFloat trackDiameter = showMaterialSpinnerTrackRadius ? ([showMaterialSpinnerTrackRadius floatValue] * 2) : 40 ;
+        
+        // Initialize the progress view
+        MMMaterialDesignSpinner *spinnerView = [[MMMaterialDesignSpinner alloc] initWithFrame:CGRectMake(0, 0, trackDiameter, trackDiameter)];
+        
+        // Set the line width of the spinner
+        id showMaterialSpinnerTrackWidth = [self.commandDelegate.settings objectForKey: [@"MaterialLikeSpinnerTrackWidth" lowercaseString]];
+        spinnerView.lineWidth = showMaterialSpinnerTrackWidth ? [showMaterialSpinnerTrackWidth floatValue] : 2.0f;
+        
+        // Set the tint color of the spinner
+        id showMaterialSpinnerTintHexColor = [self.commandDelegate.settings objectForKey: [@"MaterialLikeSpinnerTrackTintHexColor" lowercaseString]];
+        unsigned int spinnerColorConvertedFromHexString = 0;
+        if (showMaterialSpinnerTintHexColor) {
+            NSScanner *scanner = [NSScanner scannerWithString:showMaterialSpinnerTintHexColor];
+            [scanner setScanLocation:1]; // bypass '#' character
+            [scanner scanHexInt:&spinnerColorConvertedFromHexString];
+        }
+        
+        spinnerView.tintColor = showMaterialSpinnerTintHexColor ? UIColorFromRGB(spinnerColorConvertedFromHexString) : [UIColor lightGrayColor];
+        _activityView = spinnerView;
+    }
+    else
+    {
+        _activityView = (UIView<ActivityTracking> *)[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:topActivityIndicatorStyle];
+    }
+    
     _activityView.center = CGPointMake(parentView.bounds.size.width / 2, parentView.bounds.size.height / 2);
     _activityView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin
-        | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin;
+    | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin;
     [_activityView startAnimating];
-
+    
     // Set the frame & image later.
     _imageView = [[UIImageView alloc] init];
     [parentView addSubview:_imageView];
-
+    
     id showSplashScreenSpinnerValue = [self.commandDelegate.settings objectForKey:[@"ShowSplashScreenSpinner" lowercaseString]];
     // backwards compatibility - if key is missing, default to true
     if ((showSplashScreenSpinnerValue == nil) || [showSplashScreenSpinnerValue boolValue])
     {
         [parentView addSubview:_activityView];
     }
-
+    
     // Frame is required when launching in portrait mode.
     // Bounds for landscape since it captures the rotation.
     [parentView addObserver:self forKeyPath:@"frame" options:0 context:nil];
     [parentView addObserver:self forKeyPath:@"bounds" options:0 context:nil];
-
+    self.removeKVOListeners = YES;
+    
     [self updateImage];
-    _destroyed = NO;
 }
 
 - (void)hideViews
@@ -128,21 +209,26 @@ - (void)hideViews
     [_imageView setAlpha:0];
     [_activityView setAlpha:0];
 }
-
 - (void)destroyViews
 {
     _destroyed = YES;
     [(CDVViewController *)self.viewController setEnabledAutorotation:[(CDVViewController *)self.viewController shouldAutorotateDefaultValue]];
-
+    
     [_imageView removeFromSuperview];
     [_activityView removeFromSuperview];
     _imageView = nil;
     _activityView = nil;
     _curImageName = nil;
-
+    
     self.viewController.view.userInteractionEnabled = YES;  // re-enable user interaction upon completion
-    [self.viewController.view removeObserver:self forKeyPath:@"frame"];
-    [self.viewController.view removeObserver:self forKeyPath:@"bounds"];
+    
+    if (self.removeKVOListeners) {
+        self.removeKVOListeners = NO;
+        [self.viewController.view removeObserver:self forKeyPath:@"frame"];
+        [self.viewController.view removeObserver:self forKeyPath:@"bounds"];
+    }
+    
+    [CDVSplashScreen cancelPreviousPerformRequestsWithTarget:self];
 }
 
 - (CDV_iOSDevice) getCurrentDevice
@@ -165,6 +251,7 @@ - (CDV_iOSDevice) getCurrentDevice
     // this is appropriate for detecting the runtime screen environment
     device.iPhone6 = (device.iPhone && limit == 667.0);
     device.iPhone6Plus = (device.iPhone && limit == 736.0);
+    device.iPadPro = (device.iPad && limit == 1366.0);
     
     return device;
 }
@@ -190,7 +277,7 @@ - (NSString*)getImageName:(UIInterfaceOrientation)currentOrientation delegate:(i
     {
         imageName = @"Default";
     }
-
+    
     // Add Asset Catalog specific prefixes
     if ([imageName isEqualToString:@"LaunchImage"])
     {
@@ -206,7 +293,7 @@ - (NSString*)getImageName:(UIInterfaceOrientation)currentOrientation delegate:(i
             }
         }
     }
-
+    
     if (device.iPhone5)
     { // does not support landscape
         imageName = [imageName stringByAppendingString:@"-568h"];
@@ -227,14 +314,14 @@ - (NSString*)getImageName:(UIInterfaceOrientation)currentOrientation delegate:(i
             {
                 case UIInterfaceOrientationLandscapeLeft:
                 case UIInterfaceOrientationLandscapeRight:
-                        imageName = [imageName stringByAppendingString:@"-Landscape"];
+                    imageName = [imageName stringByAppendingString:@"-Landscape"];
                     break;
                 default:
                     break;
             }
         }
         imageName = [imageName stringByAppendingString:@"-736h"];
-
+        
     }
     else if (device.iPad)
     {   // supports landscape
@@ -258,6 +345,9 @@ - (NSString*)getImageName:(UIInterfaceOrientation)currentOrientation delegate:(i
                     break;
             }
         }
+        if (device.iPadPro) {
+            imageName = [imageName stringByAppendingString:@"-1336"];
+        }
     }
     
     return imageName;
@@ -267,16 +357,16 @@ - (UIInterfaceOrientation)getCurrentOrientation
 {
     UIInterfaceOrientation iOrientation = [UIApplication sharedApplication].statusBarOrientation;
     UIDeviceOrientation dOrientation = [UIDevice currentDevice].orientation;
-
+    
     bool landscape;
-
+    
     if (dOrientation == UIDeviceOrientationUnknown || dOrientation == UIDeviceOrientationFaceUp || dOrientation == UIDeviceOrientationFaceDown) {
         // If the device is laying down, use the UIInterfaceOrientation based on the status bar.
         landscape = UIInterfaceOrientationIsLandscape(iOrientation);
     } else {
         // If the device is not laying down, use UIDeviceOrientation.
         landscape = UIDeviceOrientationIsLandscape(dOrientation);
-
+        
         // There's a bug in iOS!!!! http://openradar.appspot.com/7216046
         // So values needs to be reversed for landscape!
         if (dOrientation == UIDeviceOrientationLandscapeLeft)
@@ -296,7 +386,7 @@ - (UIInterfaceOrientation)getCurrentOrientation
             iOrientation = UIInterfaceOrientationPortraitUpsideDown;
         }
     }
-
+    
     return iOrientation;
 }
 
@@ -304,14 +394,14 @@ - (UIInterfaceOrientation)getCurrentOrientation
 - (void)updateImage
 {
     NSString* imageName = [self getImageName:[self getCurrentOrientation] delegate:(id<CDVScreenOrientationDelegate>)self.viewController device:[self getCurrentDevice]];
-
+    
     if (![imageName isEqualToString:_curImageName])
     {
         UIImage* img = [UIImage imageNamed:imageName];
         _imageView.image = img;
         _curImageName = imageName;
     }
-
+    
     // Check that splash screen's image exists before updating bounds
     if (_imageView.image)
     {
@@ -327,23 +417,24 @@ - (void)updateBounds
 {
     UIImage* img = _imageView.image;
     CGRect imgBounds = (img) ? CGRectMake(0, 0, img.size.width, img.size.height) : CGRectZero;
-
+    
     CGSize screenSize = [self.viewController.view convertRect:[UIScreen mainScreen].bounds fromView:nil].size;
     UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
     CGAffineTransform imgTransform = CGAffineTransformIdentity;
-
+    
     /* If and only if an iPhone application is landscape-only as per
      * UISupportedInterfaceOrientations, the view controller's orientation is
      * landscape. In this case the image must be rotated in order to appear
      * correctly.
      */
     CDV_iOSDevice device = [self getCurrentDevice];
-    if (UIInterfaceOrientationIsLandscape(orientation) && !device.iPhone6Plus && !device.iPad)
+    
+    if ((UIInterfaceOrientationIsLandscape(orientation) && !device.iPhone6Plus && !device.iPad) && ((!device.iPhone5 && !device.iPhone4) || self.isIOS8))
     {
         imgTransform = CGAffineTransformMakeRotation(M_PI / 2);
         imgBounds.size = CGSizeMake(imgBounds.size.height, imgBounds.size.width);
     }
-
+    
     // There's a special case when the image is the size of the screen.
     if (CGSizeEqualToSize(screenSize, imgBounds.size))
     {
@@ -371,7 +462,7 @@ - (void)updateBounds
         imgBounds.size.height *= ratio;
         imgBounds.size.width *= ratio;
     }
-
+    
     _imageView.transform = imgTransform;
     _imageView.frame = imgBounds;
 }
@@ -383,90 +474,100 @@ - (void)setVisible:(BOOL)visible
 
 - (void)setVisible:(BOOL)visible andForce:(BOOL)force
 {
-    if (visible != _visible || force)
-    {
-        _visible = visible;
-
-        id fadeSplashScreenValue = [self.commandDelegate.settings objectForKey:[@"FadeSplashScreen" lowercaseString]];
-        id fadeSplashScreenDuration = [self.commandDelegate.settings objectForKey:[@"FadeSplashScreenDuration" lowercaseString]];
-
-        float fadeDuration = fadeSplashScreenDuration == nil ? kSplashScreenDurationDefault : [fadeSplashScreenDuration floatValue];
-
-        id splashDurationString = [self.commandDelegate.settings objectForKey: [@"SplashScreenDelay" lowercaseString]];
-        float splashDuration = splashDurationString == nil ? kSplashScreenDurationDefault : [splashDurationString floatValue];
-
-        id autoHideSplashScreenValue = [self.commandDelegate.settings objectForKey:[@"AutoHideSplashScreen" lowercaseString]];
-        BOOL autoHideSplashScreen = true;
-
-        if (autoHideSplashScreenValue != nil) {
-            autoHideSplashScreen = [autoHideSplashScreenValue boolValue];
-        }
-
-        if (!autoHideSplashScreen) {
-            // CB-10412 SplashScreenDelay does not make sense if the splashscreen is hidden manually
-            splashDuration = 0;
-        }
-
-
-        if (fadeSplashScreenValue == nil)
-        {
-            fadeSplashScreenValue = @"true";
-        }
-
-        if (![fadeSplashScreenValue boolValue])
-        {
-            fadeDuration = 0;
-        }
-        else if (fadeDuration < 30)
-        {
-            // [CB-9750] This value used to be in decimal seconds, so we will assume that if someone specifies 10
-            // they mean 10 seconds, and not the meaningless 10ms
-            fadeDuration *= 1000;
-        }
-
-        if (_visible)
+    @synchronized (self) {
+        if (visible != _visible || force)
         {
-            if (_imageView == nil)
+            _visible = visible;
+            
+            id fadeSplashScreenValue = [self.commandDelegate.settings objectForKey:[@"FadeSplashScreen" lowercaseString]];
+            id fadeSplashScreenDuration = [self.commandDelegate.settings objectForKey:[@"FadeSplashScreenDuration" lowercaseString]];
+            
+            float fadeDuration = fadeSplashScreenDuration == nil ? kSplashScreenDurationDefault : [fadeSplashScreenDuration floatValue];
+            
+            id splashDurationString = [self.commandDelegate.settings objectForKey: [@"SplashScreenDelay" lowercaseString]];
+            float splashDuration = splashDurationString == nil ? kSplashScreenDurationDefault : [splashDurationString floatValue];
+            
+            id autoHideSplashScreenValue = [self.commandDelegate.settings objectForKey:[@"AutoHideSplashScreen" lowercaseString]];
+            BOOL autoHideSplashScreen = true;
+            
+            if (autoHideSplashScreenValue != nil) {
+                autoHideSplashScreen = [autoHideSplashScreenValue boolValue];
+            }
+            
+            if (!autoHideSplashScreen) {
+                // CB-10412 SplashScreenDelay does not make sense if the splashscreen is hidden manually
+                splashDuration = 0;
+            }
+            
+            
+            if (fadeSplashScreenValue == nil)
             {
-                [self createViews];
+                fadeSplashScreenValue = @"true";
             }
-        }
-        else if (fadeDuration == 0 && splashDuration == 0)
-        {
-            [self destroyViews];
-        }
-        else
-        {
-            __weak __typeof(self) weakSelf = self;
-            float effectiveSplashDuration;
-
-            // [CB-10562] AutoHideSplashScreen may be "true" but we should still be able to hide the splashscreen manually.
-            if (!autoHideSplashScreen || force) {
-                effectiveSplashDuration = (fadeDuration) / 1000;
-            } else {
-                effectiveSplashDuration = (splashDuration - fadeDuration) / 1000;
+            
+            if (![fadeSplashScreenValue boolValue])
+            {
+                fadeDuration = 0;
             }
-
-            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (uint64_t) effectiveSplashDuration * NSEC_PER_SEC), dispatch_get_main_queue(), CFBridgingRelease(CFBridgingRetain(^(void) {
-                if (!_destroyed) {
-                    [UIView transitionWithView:self.viewController.view
-                                    duration:(fadeDuration / 1000)
-                                    options:UIViewAnimationOptionTransitionNone
-                                    animations:^(void) {
-                                        [weakSelf hideViews];
-                                    }
-                                    completion:^(BOOL finished) {
-                                        // Always destroy views, otherwise you could have an 
-                                        // invisible splashscreen that is overlayed over your active views
-                                        // which causes that no touch events are passed
-                                        if (!_destroyed) {
-                                            [weakSelf destroyViews];
-                                            // TODO: It might also be nice to have a js event happen here -jm
-                                        }
-                                    }
-                    ];
+            else if (fadeDuration < 30)
+            {
+                // [CB-9750] This value used to be in decimal seconds, so we will assume that if someone specifies 10
+                // they mean 10 seconds, and not the meaningless 10ms
+                fadeDuration *= 1000;
+            }
+            
+            if (_visible)
+            {
+                if (_imageView == nil)
+                {
+                    [self createViews];
                 }
-            })));
+            }
+            else if (fadeDuration == 0 && splashDuration == 0)
+            {
+                [self destroyViews];
+            }
+            else
+            {
+                __weak __typeof(self) weakSelf = self;
+                float effectiveSplashDuration;
+                
+                // [CB-10562] AutoHideSplashScreen may be "true" but we should still be able to hide the splashscreen manually.
+                if (!autoHideSplashScreen || force) {
+                    effectiveSplashDuration = (fadeDuration) / 1000;
+                } else {
+                    effectiveSplashDuration = (splashDuration - fadeDuration) / 1000;
+                }
+                NSAssert(effectiveSplashDuration < 8, @"effectiveSplashDuration should be less than 8s...");
+                
+                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (uint64_t) effectiveSplashDuration * NSEC_PER_SEC), dispatch_get_main_queue(), CFBridgingRelease(CFBridgingRetain(^(void) {
+                    if (self.isIOS8) {
+                        [(CDVViewController *)self.viewController setEnabledAutorotation:[(CDVViewController *)self.viewController shouldAutorotateDefaultValue]];
+                        [UIViewController attemptRotationToDeviceOrientation];
+                    }
+                    if (!_destroyed) {
+                        [UIView transitionWithView:self.viewController.view
+                                          duration:(fadeDuration / 1000)
+                                           options:UIViewAnimationOptionTransitionNone
+                                        animations:^(void) {
+                                            CDVSplashScreen *strongSelf = weakSelf;
+                                            [strongSelf hideViews];
+                                        }
+                                        completion:^(BOOL finished) {
+                                            CDVSplashScreen *strongSelf = weakSelf;
+                                            
+                                            // Always destroy views, otherwise you could have an
+                                            // invisible splashscreen that is overlayed over your active views
+                                            // which causes that no touch events are passed
+                                            if (!_destroyed) {
+                                                [strongSelf destroyViews];
+                                                // TODO: It might also be nice to have a js event happen here -jm
+                                            }
+                                        }
+                         ];
+                    }
+                })));
+            }
         }
     }
 }
diff --git a/src/ios/CDVSplashScreenSystemVersion.h b/src/ios/CDVSplashScreenSystemVersion.h
new file mode 100644
index 00000000..ee59b83d
--- /dev/null
+++ b/src/ios/CDVSplashScreenSystemVersion.h
@@ -0,0 +1,32 @@
+/*
+ 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.
+ */
+
+#import <UIKit/UIKit.h>
+
+#if !defined(SYSTEM_VERSION_EQUAL_TO) && !defined(SYSTEM_VERSION_GREATER_THAN) &&\
+!defined(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO) &&!defined(SYSTEM_VERSION_LESS_THAN) &&\
+!defined(SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO)
+
+#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)
+
+#endif
diff --git a/src/ios/CDVViewController+SplashScreen.h b/src/ios/CDVViewController+SplashScreen.h
index a948ea31..772bfaae 100644
--- a/src/ios/CDVViewController+SplashScreen.h
+++ b/src/ios/CDVViewController+SplashScreen.h
@@ -6,9 +6,9 @@
  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
@@ -24,5 +24,4 @@
 @property (nonatomic, assign) BOOL enabledAutorotation;
 @property (nonatomic, readonly) BOOL shouldAutorotateDefaultValue;
 
-
 @end
diff --git a/src/ios/CDVViewController+SplashScreen.m b/src/ios/CDVViewController+SplashScreen.m
index e483def6..0fad883a 100644
--- a/src/ios/CDVViewController+SplashScreen.m
+++ b/src/ios/CDVViewController+SplashScreen.m
@@ -18,6 +18,8 @@ Licensed to the Apache Software Foundation (ASF) under one
  */
 
 #import "CDVViewController+SplashScreen.h"
+
+#import "CDVSplashScreenSystemVersion.h"
 #import <objc/runtime.h>
 
 @implementation CDVViewController (SplashScreen)
@@ -35,13 +37,13 @@ - (void)setEnabledAutorotation:(BOOL)value
 - (BOOL)enabledAutorotation
 {
     NSNumber *number =  (NSNumber *)objc_getAssociatedObject(self, @selector(enabledAutorotation));
-
+    
     // Defaulting to YES to correspond parent CDVViewController behavior
     if (number == nil)
     {
         return YES;
     }
-
+    
     return [number boolValue];
 }
 
@@ -86,4 +88,15 @@ - (BOOL)shouldAutorotateDefaultValue
     return [self splash_shouldAutorotate];
 }
 
+- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
+{
+    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
+    
+    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0") && SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(@"8.3")) {
+        [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
+            [self.view setFrame:[context containerView].frame];
+        } completion:nil];
+    }
+}
+
 @end
diff --git a/src/ios/MMMaterialDesignSpinner.h b/src/ios/MMMaterialDesignSpinner.h
new file mode 100644
index 00000000..4e90742d
--- /dev/null
+++ b/src/ios/MMMaterialDesignSpinner.h
@@ -0,0 +1,43 @@
+//
+//  MMMaterialDesignSpinner.h
+//  Pods
+//
+//  Created by Michael Maxwell on 12/28/14.
+//
+//
+
+#import <UIKit/UIKit.h>
+#import "ActivityTracking.h"
+
+//! Project version number for MMMaterialDesignSpinner.
+FOUNDATION_EXPORT double MMMaterialDesignSpinnerVersionNumber;
+
+//! Project version string for MMMaterialDesignSpinner.
+FOUNDATION_EXPORT const unsigned char MMMaterialDesignSpinnerVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <Cent/PublicHeader.h>
+
+/**
+ *  A control similar to iOS' UIActivityIndicatorView modeled after Google's Material Design Activity spinner.
+ */
+@interface MMMaterialDesignSpinner : UIView <ActivityTracking>
+
+/** Sets the line width of the spinner's circle. */
+@property (nonatomic) CGFloat lineWidth;
+
+/** Specifies the timing function to use for the control's animation. Defaults to kCAMediaTimingFunctionEaseInEaseOut */
+@property (nonatomic, strong) CAMediaTimingFunction *timingFunction;
+
+/** Property indicating the duration of the animation, default is 1.5s. Should be set prior to -[startAnimating] */
+@property (nonatomic, readwrite) NSTimeInterval duration;
+
+/**
+ *  Convenience function for starting & stopping animation with a boolean variable instead of explicit
+ *  method calls.
+ *
+ *  @param animate true to start animating, false to stop animating.
+ @note This method simply calls the startAnimating or stopAnimating methods based on the value of the animate parameter.
+ */
+- (void)setAnimating:(BOOL)animate;
+
+@end
diff --git a/src/ios/MMMaterialDesignSpinner.m b/src/ios/MMMaterialDesignSpinner.m
new file mode 100644
index 00000000..87435a69
--- /dev/null
+++ b/src/ios/MMMaterialDesignSpinner.m
@@ -0,0 +1,239 @@
+//
+//  MMMaterialDesignSpinner.m
+//  Pods
+//
+//  Created by Michael Maxwell on 12/28/14.
+//
+//
+
+#import "MMMaterialDesignSpinner.h"
+
+static NSString *kMMRingStrokeAnimationKey = @"mmmaterialdesignspinner.stroke";
+static NSString *kMMRingRotationAnimationKey = @"mmmaterialdesignspinner.rotation";
+
+@interface MMMaterialDesignSpinner ()
+@property (nonatomic, readonly) CAShapeLayer *progressLayer;
+@property (nonatomic, readwrite) BOOL isAnimating;
+@end
+
+@implementation MMMaterialDesignSpinner
+
+@synthesize progressLayer=_progressLayer;
+@synthesize hidesWhenStopped=_hidesWhenStopped;
+@synthesize activityIndicatorViewStyle=_activityIndicatorViewStyle;
+@synthesize color=_color;
+
+- (instancetype)initWithFrame:(CGRect)frame {
+    if (self = [super initWithFrame:frame]) {
+        [self initialize];
+    }
+    return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)aDecoder {
+    if (self = [super initWithCoder:aDecoder]) {
+        [self initialize];
+    }
+    return self;
+}
+
+- (void)awakeFromNib
+{
+    [self initialize];
+}
+
+- (void)initialize {
+    _activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhite;
+    
+    self.duration = 1.5f;
+    _timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+    
+    [self.layer addSublayer:self.progressLayer];
+    
+    // See comment in resetAnimations on why this notification is used.
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resetAnimations) name:UIApplicationDidBecomeActiveNotification object:nil];
+}
+
+- (void)dealloc
+{
+    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
+}
+
+- (void)layoutSubviews {
+    [super layoutSubviews];
+    
+    self.progressLayer.frame = CGRectMake(0, 0, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds));
+    [self updatePath];
+}
+
+- (void)tintColorDidChange {
+    [super tintColorDidChange];
+    
+    self.color = self.tintColor;
+    self.progressLayer.strokeColor = self.tintColor.CGColor;
+}
+
+- (void)resetAnimations {
+    // If the app goes to the background, returning it to the foreground causes the animation to stop (even though it's not explicitly stopped by our code). Resetting the animation seems to kick it back into gear.
+    if (self.isAnimating) {
+        [self stopAnimating];
+        [self startAnimating];
+    }
+}
+
+- (void)setAnimating:(BOOL)animate {
+    (animate ? [self startAnimating] : [self stopAnimating]);
+}
+
+
+
+- (void)startAnimating {
+    if (self.isAnimating)
+        return;
+    
+    CABasicAnimation *animation = [CABasicAnimation animation];
+    animation.keyPath = @"transform.rotation";
+    animation.duration = self.duration / 0.375f;
+    animation.fromValue = @(0.f);
+    animation.toValue = @(2 * M_PI);
+    animation.repeatCount = INFINITY;
+    animation.removedOnCompletion = NO;
+    [self.progressLayer addAnimation:animation forKey:kMMRingRotationAnimationKey];
+    
+    CABasicAnimation *headAnimation = [CABasicAnimation animation];
+    headAnimation.keyPath = @"strokeStart";
+    headAnimation.duration = self.duration / 1.5f;
+    headAnimation.fromValue = @(0.f);
+    headAnimation.toValue = @(0.25f);
+    headAnimation.timingFunction = self.timingFunction;
+    
+    CABasicAnimation *tailAnimation = [CABasicAnimation animation];
+    tailAnimation.keyPath = @"strokeEnd";
+    tailAnimation.duration = self.duration / 1.5f;
+    tailAnimation.fromValue = @(0.f);
+    tailAnimation.toValue = @(1.f);
+    tailAnimation.timingFunction = self.timingFunction;
+    
+    
+    CABasicAnimation *endHeadAnimation = [CABasicAnimation animation];
+    endHeadAnimation.keyPath = @"strokeStart";
+    endHeadAnimation.beginTime = self.duration / 1.5f;
+    endHeadAnimation.duration = self.duration / 3.0f;
+    endHeadAnimation.fromValue = @(0.25f);
+    endHeadAnimation.toValue = @(1.f);
+    endHeadAnimation.timingFunction = self.timingFunction;
+    
+    CABasicAnimation *endTailAnimation = [CABasicAnimation animation];
+    endTailAnimation.keyPath = @"strokeEnd";
+    endTailAnimation.beginTime = self.duration / 1.5f;
+    endTailAnimation.duration = self.duration / 3.0f;
+    endTailAnimation.fromValue = @(1.f);
+    endTailAnimation.toValue = @(1.f);
+    endTailAnimation.timingFunction = self.timingFunction;
+    
+    CAAnimationGroup *animations = [CAAnimationGroup animation];
+    [animations setDuration:self.duration];
+    [animations setAnimations:@[headAnimation, tailAnimation, endHeadAnimation, endTailAnimation]];
+    animations.repeatCount = INFINITY;
+    animations.removedOnCompletion = NO;
+    [self.progressLayer addAnimation:animations forKey:kMMRingStrokeAnimationKey];
+    
+    
+    self.isAnimating = true;
+    
+    if (self.hidesWhenStopped) {
+        self.hidden = NO;
+    }
+}
+
+- (void)stopAnimating {
+    if (!self.isAnimating)
+        return;
+    
+    [self.progressLayer removeAnimationForKey:kMMRingRotationAnimationKey];
+    [self.progressLayer removeAnimationForKey:kMMRingStrokeAnimationKey];
+    self.isAnimating = false;
+    
+    if (self.hidesWhenStopped) {
+        self.hidden = YES;
+    }
+}
+
+#pragma mark - Private
+
+- (void)updatePath {
+    CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
+    CGFloat radius = MIN(CGRectGetWidth(self.bounds) / 2, CGRectGetHeight(self.bounds) / 2) - self.progressLayer.lineWidth / 2;
+    CGFloat startAngle = (CGFloat)(0);
+    CGFloat endAngle = (CGFloat)(2*M_PI);
+    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
+    self.progressLayer.path = path.CGPath;
+    
+    self.progressLayer.strokeStart = 0.f;
+    self.progressLayer.strokeEnd = 0.f;
+}
+
+#pragma mark - Properties
+
+- (CAShapeLayer *)progressLayer {
+    if (!_progressLayer) {
+        _progressLayer = [CAShapeLayer layer];
+        _progressLayer.strokeColor = self.tintColor.CGColor;
+        _progressLayer.fillColor = nil;
+        _progressLayer.lineWidth = 1.5f;
+    }
+    return _progressLayer;
+}
+
+- (BOOL)isAnimating {
+    return _isAnimating;
+}
+
+- (CGFloat)lineWidth {
+    return self.progressLayer.lineWidth;
+}
+
+- (void)setLineWidth:(CGFloat)lineWidth {
+    self.progressLayer.lineWidth = lineWidth;
+    [self updatePath];
+}
+
+- (void)setHidesWhenStopped:(BOOL)hidesWhenStopped {
+    @synchronized(self) {
+        _hidesWhenStopped = hidesWhenStopped;
+        self.hidden = !self.isAnimating && hidesWhenStopped;
+    }
+}
+
+- (BOOL)hidesWhenStopped {
+    @synchronized(self) {
+        return _hidesWhenStopped;
+    }
+}
+
+- (void)setActivityIndicatorViewStyle:(UIActivityIndicatorViewStyle)activityIndicatorViewStyle
+{
+    @synchronized(self) {
+        _activityIndicatorViewStyle = activityIndicatorViewStyle;
+    }
+}
+
+- (UIActivityIndicatorViewStyle)activityIndicatorViewStyle
+{
+    @synchronized(self) {
+        return _activityIndicatorViewStyle;
+    }
+}
+
+- (void)setColor:(UIColor *)color
+{
+}
+
+- (UIColor *)color
+{
+    @synchronized(self) {
+        return _color;
+    }
+}
+
+@end
diff --git a/www/splashscreen.js b/www/splashscreen.js
index 7cb48bdd..6b19d62f 100644
--- a/www/splashscreen.js
+++ b/www/splashscreen.js
@@ -27,6 +27,12 @@ var splashscreen = {
     },
     hide:function() {
         exec(null, null, "SplashScreen", "hide", []);
+    },
+    onPageLoading:function() {
+        exec(null, null, "SplashScreen", "onPageLoading", []);
+    },
+    onPageLoaded:function() {
+        exec(null, null, "SplashScreen", "onPageLoaded", []);
     }
 };
 


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org