diff --git a/src/android/InAppChromeClient.java b/src/android/InAppChromeClient.java index 74456a442..fc3fefef6 100644 --- a/src/android/InAppChromeClient.java +++ b/src/android/InAppChromeClient.java @@ -24,21 +24,24 @@ Licensed to the Apache Software Foundation (ASF) under one import org.json.JSONArray; import org.json.JSONException; +import android.view.View; import android.webkit.JsPromptResult; import android.webkit.WebChromeClient; import android.webkit.WebStorage; import android.webkit.WebView; import android.webkit.GeolocationPermissions.Callback; +import android.widget.ProgressBar; public class InAppChromeClient extends WebChromeClient { private CordovaWebView webView; private String LOG_TAG = "InAppChromeClient"; private long MAX_QUOTA = 100 * 1024 * 1024; - - public InAppChromeClient(CordovaWebView webView) { + private ProgressBar progressbar; + public InAppChromeClient(CordovaWebView webView,ProgressBar progressbar) { super(); this.webView = webView; + this.progressbar=progressbar; } /** * Handle database quota exceeded notification. @@ -128,5 +131,15 @@ public boolean onJsPrompt(WebView view, String url, String message, String defau } return false; } - -} + @Override + public void onProgressChanged(WebView view, int newProgress) { + if (newProgress == 100) { + progressbar.setVisibility(View.GONE); + } else { + if (progressbar.getVisibility() == View.GONE) + progressbar.setVisibility(View.VISIBLE); + progressbar.setProgress(newProgress); + } + super.onProgressChanged(view, newProgress); + } +} \ No newline at end of file diff --git a/src/android/ThemeableBrowser.java b/src/android/ThemeableBrowser.java index ea6cd1088..aaa0bc1c1 100644 --- a/src/android/ThemeableBrowser.java +++ b/src/android/ThemeableBrowser.java @@ -25,8 +25,11 @@ Licensed to the Apache Software Foundation (ASF) under one import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Paint; import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ClipDrawable; +import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.StateListDrawable; import android.net.Uri; @@ -58,6 +61,7 @@ Licensed to the Apache Software Foundation (ASF) under one import android.widget.EditText; import android.widget.FrameLayout; import android.widget.LinearLayout; +import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.Spinner; import android.widget.TextView; @@ -242,6 +246,17 @@ public void run() { pluginResult.setKeepCallback(true); this.callbackContext.sendPluginResult(pluginResult); } + else if (action.equals("hide")) { + this.cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + dialog.hide(); + } + }); + PluginResult pluginResult = new PluginResult(PluginResult.Status.OK); + pluginResult.setKeepCallback(true); + this.callbackContext.sendPluginResult(pluginResult); + } else if (action.equals("reload")) { if (inAppWebView != null) { this.cordova.getActivity().runOnUiThread(new Runnable() { @@ -750,7 +765,25 @@ public void onNothingSelected( title.setTextSize(features.title.textSize); } } - + final ProgressBar progressbar = new ProgressBar(cordova.getActivity(), null, android.R.attr.progressBarStyleHorizontal); + FrameLayout.LayoutParams progressbarLayout = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, 6); + //progressbarLayout. + progressbar.setLayoutParams(progressbarLayout); + if (features.browserProgress != null){ + Integer progressColor=Color.BLUE; + if ( features.browserProgress.progressColor != null + && features.browserProgress.progressColor.length() > 0) { + progressColor = Color.parseColor(features.browserProgress.progressColor); + } + ClipDrawable progressDrawable = new ClipDrawable(new ColorDrawable(progressColor), Gravity.LEFT, ClipDrawable.HORIZONTAL); + progressbar.setProgressDrawable(progressDrawable); + Integer progressBgColor = Color.GRAY; + if ( features.browserProgress.progressBgColor != null + && features.browserProgress.progressBgColor.length() > 0) { + progressBgColor = Color.parseColor(features.browserProgress.progressBgColor); + } + progressbar.setBackgroundColor(progressBgColor); + } // WebView inAppWebView = new WebView(cordova.getActivity()); final ViewGroup.LayoutParams inAppWebViewParams = features.fullscreen @@ -760,7 +793,7 @@ public void onNothingSelected( ((LinearLayout.LayoutParams) inAppWebViewParams).weight = 1; } inAppWebView.setLayoutParams(inAppWebViewParams); - inAppWebView.setWebChromeClient(new InAppChromeClient(thatWebView)); + inAppWebView.setWebChromeClient(new InAppChromeClient(thatWebView, progressbar)); WebViewClient client = new ThemeableBrowserClient(thatWebView, new PageLoadListener() { @Override public void onPageFinished(String url, boolean canGoBack, boolean canGoForward) { @@ -927,6 +960,9 @@ public void onClick(View view) { if (features.location) { // Add our toolbar to our main view/layout main.addView(toolbar); + if (features.browserProgress!=null&&features.browserProgress.showProgress){ + main.addView(progressbar); + } } if (!features.fullscreen) { @@ -1390,6 +1426,7 @@ private static class Options { public boolean backButtonCanClose; public boolean disableAnimation; public boolean fullscreen; + public BrowserProgress browserProgress; } private static class Event { @@ -1417,6 +1454,12 @@ private static class BrowserMenu extends BrowserButton { public EventLabel[] items; } + private static class BrowserProgress { + public boolean showProgress; + public String progressBgColor; + public String progressColor; + } + private static class Toolbar { public int height = TOOLBAR_DEF_HEIGHT; public String color; diff --git a/src/ios/CDVThemeableBrowser.h b/src/ios/CDVThemeableBrowser.h index ed59e22f1..0b8d0c9b9 100644 --- a/src/ios/CDVThemeableBrowser.h +++ b/src/ios/CDVThemeableBrowser.h @@ -49,6 +49,7 @@ @property (nonatomic) NSDictionary* statusbar; @property (nonatomic) NSDictionary* toolbar; @property (nonatomic) NSDictionary* title; +@property (nonatomic) NSDictionary* browserProgress; @property (nonatomic) NSDictionary* backButton; @property (nonatomic) NSDictionary* forwardButton; @property (nonatomic) NSDictionary* closeButton; @@ -76,6 +77,7 @@ - (void)injectScriptCode:(CDVInvokedUrlCommand*)command; - (void)show:(CDVInvokedUrlCommand*)command; - (void)show:(CDVInvokedUrlCommand*)command withAnimation:(BOOL)animated; +- (void)hide:(CDVInvokedUrlCommand*)command; - (void)reload:(CDVInvokedUrlCommand*)command; @end @@ -105,6 +107,7 @@ @property (nonatomic, strong) IBOutlet UIButton* menuButton; @property (nonatomic, strong) IBOutlet UIActivityIndicatorView* spinner; @property (nonatomic, strong) IBOutlet UIView* toolbar; +@property (nonatomic, strong) IBOutlet UIProgressView* progressView; @property (nonatomic, strong) NSArray* leftButtons; @property (nonatomic, strong) NSArray* rightButtons; @@ -114,6 +117,8 @@ @property (nonatomic) NSURL* currentURL; @property (nonatomic) CGFloat titleOffset; +@property (nonatomic , readonly , getter=loadProgress) CGFloat currentProgress; + - (void)close; - (void)reload; - (void)navigateTo:(NSURL*)url; diff --git a/src/ios/CDVThemeableBrowser.m b/src/ios/CDVThemeableBrowser.m index 166173084..a9ba89f28 100644 --- a/src/ios/CDVThemeableBrowser.m +++ b/src/ios/CDVThemeableBrowser.m @@ -41,8 +41,12 @@ Licensed to the Apache Software Foundation (ASF) under one #define kThemeableBrowserPropWwwImagePressed @"wwwImagePressed" #define kThemeableBrowserPropWwwImageDensity @"wwwImageDensity" #define kThemeableBrowserPropStaticText @"staticText" +//#define kThemeableBrowserPropShowPageTitle @"showPageTitle" +#define kThemeableBrowserPropShowProgress @"showProgress" #define kThemeableBrowserPropShowPageTitle @"showPageTitle" #define kThemeableBrowserPropTitleTextSize @"textSize" +#define kThemeableBrowserPropProgressBgColor @"progressBgColor" +#define kThemeableBrowserPropProgressColor @"progressColor" #define kThemeableBrowserPropAlign @"align" #define kThemeableBrowserPropTitle @"title" #define kThemeableBrowserPropCancel @"cancel" @@ -59,6 +63,12 @@ Licensed to the Apache Software Foundation (ASF) under one #define LOCATIONBAR_HEIGHT 21.0 #define FOOTER_HEIGHT ((TOOLBAR_HEIGHT) + (LOCATIONBAR_HEIGHT)) +NSString *completeRPCURLPath = @"/webviewprogressproxy/complete"; + +const float MyInitialProgressValue = 0.1f; +const float MyInteractiveProgressValue = 0.5f; +const float MyFinalProgressValue = 0.9f; + #pragma mark CDVThemeableBrowser @interface CDVThemeableBrowser () { @@ -315,7 +325,25 @@ - (void)show:(CDVInvokedUrlCommand*)command withAnimation:(BOOL)animated } }); } +- (void)hide:(CDVInvokedUrlCommand*)command +{ + /* + if (self.themeableBrowserViewController == nil) { + NSLog(@"Tried to hide IAB after it was closed."); + return; + } + if (_previousStatusBarStyle == -1) { + NSLog(@"Tried to hide IAB while already hidden"); + return; + } + */ + if (self.themeableBrowserViewController != nil) { + [[self.themeableBrowserViewController presentingViewController] dismissViewControllerAnimated:YES completion:nil]; + _isShown = NO; + /*_previousStatusBarStyle = -1;*/ + } +} - (void)openInCordovaWebView:(NSURL*)url withOptions:(NSString*)options { if ([self.commandDelegate URLIsWhitelisted:url]) { @@ -575,6 +603,19 @@ - (void)emitWarning:(NSString*)code withMessage:(NSString*)message #pragma mark CDVThemeableBrowserViewController +@interface CDVThemeableBrowserViewController () + { + NSUInteger loadingCount; + NSUInteger maxLoadCount; + + NSURL *currentURL; + + CGFloat currentLoadProgress; + + BOOL interactive; + } +@end + @implementation CDVThemeableBrowserViewController @synthesize currentURL; @@ -593,6 +634,10 @@ - (id)initWithUserAgent:(NSString*)userAgent prevUserAgent:(NSString*)prevUserAg #endif _navigationDelegate = navigationDelegate; _statusBarStyle = statusBarStyle; + maxLoadCount = loadingCount = 0; + //默认值currentLoadProgress = 99; + currentLoadProgress = 99; + interactive = NO; [self createViews]; } @@ -826,6 +871,14 @@ - (void)createViews self.view.backgroundColor = [CDVThemeableBrowserViewController colorFromRGBA:[self getStringFromDict:_browserOptions.statusbar withKey:kThemeableBrowserPropColor withDefault:@"#ffffffff"]]; [self.view addSubview:self.toolbar]; + self.progressView=[[UIProgressView alloc] initWithFrame:CGRectMake(0.0, toolbarY+toolbarHeight+[self getStatusBarOffset], self.view.bounds.size.width, 20.0)]; + self.progressView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; + self.progressView.progressViewStyle=UIProgressViewStyleDefault; + self.progressView.progressTintColor=[CDVThemeableBrowserViewController colorFromRGBA:[self getStringFromDict:_browserOptions.browserProgress withKey: kThemeableBrowserPropProgressColor withDefault:@"#0000FF"]]; + self.progressView.trackTintColor=[CDVThemeableBrowserViewController colorFromRGBA:[self getStringFromDict:_browserOptions.browserProgress withKey:kThemeableBrowserPropProgressBgColor withDefault:@"#808080"]]; + if ([self getBoolFromDict:_browserOptions.browserProgress withKey:kThemeableBrowserPropShowProgress]) { + [self.view addSubview:self.progressView]; + } // [self.view addSubview:self.addressLabel]; // [self.view addSubview:self.spinner]; } @@ -1354,23 +1407,45 @@ - (void)webViewDidStartLoad:(UIWebView*)theWebView // loading url, start spinner self.addressLabel.text = NSLocalizedString(@"Loading...", nil); + loadingCount++; + maxLoadCount = fmax(maxLoadCount, loadingCount); [self.spinner startAnimating]; - return [self.navigationDelegate webViewDidStartLoad:theWebView]; + [self.navigationDelegate webViewDidStartLoad:theWebView]; + [self startProgress:theWebView]; } - (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType { - BOOL isTopLevelNavigation = [request.URL isEqual:[request mainDocumentURL]]; + if ([request.URL.path isEqualToString:completeRPCURLPath]) { + [self completeProgress:theWebView]; + return NO; + } + + BOOL ret = [self.navigationDelegate webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType]; + BOOL isFragmentJump = NO; + if (request.URL.fragment) { + NSString *nonFragmentURL = [request.URL.absoluteString stringByReplacingOccurrencesOfString:[@"#" stringByAppendingString:request.URL.fragment] withString:@""]; + isFragmentJump = [nonFragmentURL isEqualToString:theWebView.request.URL.absoluteString]; + } + + BOOL isTopLevelNavigation = [request.mainDocumentURL isEqual:request.URL]; + + BOOL isHTTP = [request.URL.scheme isEqualToString:@"http"] || [request.URL.scheme isEqualToString:@"https"]; + if (ret && !isFragmentJump && isHTTP && isTopLevelNavigation) { + currentURL = request.URL; + [self reset:theWebView]; + } if (isTopLevelNavigation) { self.currentURL = request.URL; } [self updateButtonDelayed:theWebView]; - - return [self.navigationDelegate webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType]; + + //return [self.navigationDelegate webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType]; + return ret; } - (void)webViewDidFinishLoad:(UIWebView*)theWebView @@ -1407,6 +1482,26 @@ - (void)webViewDidFinishLoad:(UIWebView*)theWebView } [self.navigationDelegate webViewDidFinishLoad:theWebView]; + loadingCount--; + [self incrementProgress:theWebView]; + + NSString *readyState = [theWebView stringByEvaluatingJavaScriptFromString:@"document.readyState"]; + + BOOL tpInteractive = [readyState isEqualToString:@"interactive"]; + if (tpInteractive) + { + interactive = YES; + NSString *waitForCompleteJS = [NSString stringWithFormat:@"window.addEventListener('load',function() { var iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = '%@://%@%@'; document.body.appendChild(iframe); }, false);", theWebView.request.mainDocumentURL.scheme, theWebView.request.mainDocumentURL.host, completeRPCURLPath]; + [theWebView stringByEvaluatingJavaScriptFromString:waitForCompleteJS]; + } + + BOOL isNotRedirect = currentURL && [currentURL isEqual:theWebView.request.mainDocumentURL]; + BOOL complete = [readyState isEqualToString:@"complete"]; + if (complete && isNotRedirect) + { + [self completeProgress:theWebView]; + } + } - (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error @@ -1418,8 +1513,105 @@ - (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error self.addressLabel.text = NSLocalizedString(@"Load Error", nil); [self.navigationDelegate webView:theWebView didFailLoadWithError:error]; + loadingCount--; + [self incrementProgress:theWebView]; + + NSString *readyState = [theWebView stringByEvaluatingJavaScriptFromString:@"document.readyState"]; + + BOOL tpInteractive = [readyState isEqualToString:@"interactive"]; + if (tpInteractive) + { + interactive = YES; + NSString *waitForCompleteJS = [NSString stringWithFormat:@"window.addEventListener('load',function() { var iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = '%@://%@%@'; document.body.appendChild(iframe); }, false);", theWebView.request.mainDocumentURL.scheme, theWebView.request.mainDocumentURL.host, completeRPCURLPath]; + [theWebView stringByEvaluatingJavaScriptFromString:waitForCompleteJS]; + } + + BOOL isNotRedirect = currentURL && [currentURL isEqual:theWebView.request.mainDocumentURL]; + BOOL complete = [readyState isEqualToString:@"complete"]; + if ((complete && isNotRedirect) || error) + { + [self completeProgress:theWebView]; + } } +-(void)setprogress:(CGFloat)progress webView:(UIWebView *)webView +{ + + if (progress == 0 && (currentLoadProgress == 1 || currentLoadProgress == 99)) + { + //新的开始标记 + currentLoadProgress = progress; + [self.progressView setProgress:progress animated:YES]; + + } + else + { + if (progress > currentLoadProgress) + { + currentLoadProgress = progress; + [self.progressView setProgress:progress animated:YES]; + } + } +} + +- (void) hideAndResetProgress +{ + [self.progressView setHidden:YES]; + [self.progressView setProgress:0 animated:NO]; +} + +- (void)reset:(UIWebView *)webView +{ + maxLoadCount = loadingCount = 0; + interactive = NO; + [self setprogress:0.0 webView:webView]; +} + +- (void)startProgress:(UIWebView *)webView +{ + if (currentLoadProgress < MyInitialProgressValue) + { + [self.progressView setHidden:NO]; + [self setprogress:MyInitialProgressValue webView:webView]; + } +} + +/** + * + * @param webView webView + */ +- (void)completeProgress:(UIWebView *)webView +{ + [self setprogress:1.0 webView:webView]; + [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(hideAndResetProgress) userInfo:nil repeats:NO]; +} + +- (void)incrementProgress:(UIWebView *)webView +{ + float progress = currentLoadProgress; + float maxProgress = interactive ? MyFinalProgressValue : MyInteractiveProgressValue; + float remainPercent = (float)loadingCount / (float)maxLoadCount; + float increment = (maxProgress - progress) * remainPercent; + progress += increment; + progress = fmin(progress, maxProgress); + [self setprogress:progress webView:webView]; +} + +/** + * + * @return currentProgress + */ +-(CGFloat)loadProgress +{ + if (currentLoadProgress == 99) + { + return 0; + } + else + { + return currentLoadProgress; + } +} - (void)updateButton:(UIWebView*)theWebView { if (self.backButton) { diff --git a/www/themeablebrowser.js b/www/themeablebrowser.js index a6931bf4d..e94249bac 100644 --- a/www/themeablebrowser.js +++ b/www/themeablebrowser.js @@ -42,6 +42,9 @@ ThemeableBrowser.prototype = { exec(null, null, 'ThemeableBrowser', 'show', []); return this; }, + hide: function (eventname) { + exec(null, null, "ThemeableBrowser", "hide", []); + }, reload: function (eventname) { exec(null, null, 'ThemeableBrowser', 'reload', []); return this;