So I'm building an app with a lot of web content I plan to release it using Phone Gap build but will host all the content online and will link to it. I was wondering if there is a way that the web pages can be downloaded when there is an active internet connection for offline use and when there is a connection again for the data to be refreshed preferably when the user is using a wifi connection. The site will mostly be in html, js, and php. I will be hosting with bluehost
Is there any way of doing this? Thanks in advance! Littleswany!
PhoneGap apps ARE downloaded to the device, when they are downloaded from the store. They are basically a wrapper around an index.html file, but the app is actually programmed in JavaScript, which is responsible for creating and displaying views etc. The only time you need to check for an internet connection is when you are communicating with your back end (PHP)... If the ajax request fails, the best solution is to provide the user with a button/link to try again when they have regained their internet connection, or set a timer which fires intermittently to keep trying again... NEVER use a while(true) loop in your Phone Gap app - it will just hang.
I am not familiar with java, but i think i can provide the logic to get the job done.
You want to do an infinite loop that checks if the user is on wifi. Then if true, use wget, rsync, or scp to download the website. Something like this.:
while (true){
// do an if statement that checks if user is on wifi. Then do a then statement that uses rsync or wget.
}
Info on how to nest if statements in while loops in java: java loop, if else
I do not know if wget, rsync, or scp can be ran from java. You'll need to look more into it or write your own alternative function to do it. Something like:
function download_file() {
var url = "http://www.example.com/file.doc"
window.location = url;
}
You should be able to do it from your java like this:
String whatToRun = "/usr/local/bin/wget http://insitu.fruitfly.org/insitu_image_storage/img_dir_38/insitu38795.jpe";
Sources:
1. What is the equivalent of wget in javascript to download a file from a given url?
2. Call a command in terminal using Java (OSX)
First Create an Connection filter class
public class Connection_Status{
private static ConnectivityManager connectivityManager;
static boolean connected = false;
public static Boolean isOnline(Context ctx) {
try {
connectivityManager = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
connected = networkInfo != null && networkInfo.isAvailable()&& networkInfo.isConnected();
return connected;
} catch (Exception e) {
System.out.println("CheckConnectivity Exception: " + e.getMessage());
}
return connected;
}
}
And in your Main class
public class Main extends Activity{
private WebView mWebView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (WebView) findViewById(R.id.webview);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setBuiltInZoomControls(true);
if(Connection_Status.isOnline(Main.this)){
HttpClient httpclient = new DefaultHttpClient(); // Create HTTP Client
HttpGet httpget = new HttpGet("http://yoururl.com"); // Set the action you want to do
HttpResponse response = httpclient.execute(httpget); // Executeit
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent(); // Create an InputStream with the response
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "iso-8859-1"), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) // Read line by line
sb.append(line + "\n");
String resString = sb.toString(); //
is.close(); // Close the stream
}
}
}
Or you can use cache on it e.g
mWebView.getSettings().setAppCacheMaxSize(1024*1024*8);
mWebView.getSettings().setAppCachePath(""+this.getCacheDir());
mWebView.getSettings().setAppCacheEnabled(true);
mWebView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
Don't forget to add the following permissions
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- for the connection status-->
Sources:
https://stackoverflow.com/a/6503817/1309629
Related
I am loading a local web application using a webview in Android:
view.loadUrl("https://appassets.androidplatform.net/assets/dist/index.html")
However when I check the current route in my JavaScript it says https://appassets.androidplatform.net/assets/dist/index.html
This makes stuff like routing and file loading kind of a pain - is there a way to set the root of the loaded application, so I can avoid the whole /assets/ ?
Have a look at WebViewAssetLoader the code snippet there does exactly what you need. So you basically just need to forward all the requests which can be handled by the assetsLoader to it, and use the response that it returns.
final WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
.addPathHandler("/assets/", new AssetsPathHandler(this))
.build();
webView.setWebViewClient(new WebViewClient() {
#Override
#RequiresApi(21)
public WebResourceResponse shouldInterceptRequest(WebView view,
WebResourceRequest request) {
return assetLoader.shouldInterceptRequest(request.getUrl());
}
#Override
#SuppressWarnings("deprecation") // for API < 21
public WebResourceResponse shouldInterceptRequest(WebView view,
WebResourceRequest request) {
return assetLoader.shouldInterceptRequest(Uri.parse(request));
}
});
WebSettings webViewSettings = webView.getSettings();
// Setting this off for security. Off by default for SDK versions >= 16.
webViewSettings.setAllowFileAccessFromFileURLs(false);
// Off by default, deprecated for SDK versions >= 30.
webViewSettings.setAllowUniversalAccessFromFileURLs(false);
// Keeping these off is less critical but still a good idea, especially if your app is not
// using file:// or content:// URLs.
webViewSettings.setAllowFileAccess(false);
webViewSettings.setAllowContentAccess(false);
// Assets are hosted under http(s)://appassets.androidplatform.net/assets/... .
// If the application's assets are in the "main/assets" folder this will read the file
// from "main/assets/www/index.html" and load it as if it were hosted on:
// https://appassets.androidplatform.net/assets/www/index.html
webview.loadUrl("https://appassets.androidplatform.net/assets/www/index.html");
I'm developing an angular website which is loading in an app from a WebView, and there is only one of the links in it that has to be opened outside of the app (external browser)
I need a way to handle this from JavaScript not putting extra work to the android side.
and i have already tried some ways including:
window.open("url","_system")
(navigator as any).app.loadUrl("http://google.com", {openExternal : true});
Well, there is no such thing
instead it must be handled from android application code. you can add a parameter to the url when u need it to open in external browser, ( here it is external=true ) and then check for that parameter in your webview url loading as below:
webView.setWebViewClient(new WebViewClient(){
#RequiresApi(api = Build.VERSION_CODES.N)
#Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
if((String.valueOf(request.getUrl())).contains("external=true")) {
Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl());
view.getContext().startActivity(intent);
return true;
} else {
view.loadUrl(String.valueOf(request.getUrl()));
}
return true;
}
});
Windows won't let my WebView_ScriptNotify Event receive a call if the html is loaded from ms-appdata.
I'm aware that I can use the ms-appx-web protocol to load such a file from my app bundle, but this is no option because the data to show are downloaded after install of the app.
I also can't just use webView.navigateToString because this won't include the referenced libraries in the html file.
Currently I'm trying something like this in my Class.xaml.cs
WebView webView = new WebView();
webView.ScriptNotify += WebView_ScriptNotify;
Uri navigationUri = new Uri(#"ms-appdata:///local/index.html");
webView.Navigate(navigationUri);
and
private void WebView_ScriptNotify(object sender, NotifyEventArgs e)
{
System.Diagnostics.Debug.WriteLine("ScriptNotifyValue: " + e.Value);
//I want to do the magic here, but this will never be called
}
in the html file is
<div id="content">
<div class="btn" onClick="window.external.notify('hello world');"</div>
</div>
Furthermore, it's no option to use InvokeScript(), because I don't know when the event must be fired and the values for it.
Yet it's mandatory to use files from ms-appdata.
Do you know a solution for this?
Even an alternative workaroung would amaze me.
Ref Script notify changes in XAML:
For content to be able to send notifications the following conditions apply:
The source of the page should be from the local system via NavigateToString(), NavigateToStream() or ms-appx-web:///
Or
The source of the page is delivered via https:// and the site domain name is listed in the app content URI’s section of the package manifest.
So to solve this issue, we can use WebView.NavigateToLocalStreamUri method with the protocol ms-local-stream://, rather than ms-appdata://. For example:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// The 'Host' part of the URI for the ms-local-stream protocol needs to be a combination of the package name
// and an application-defined key, which identifies the specific resolver, in this case 'MyTag'.
Uri url = webView.BuildLocalStreamUri("MyTag", "index.html");
StreamUriWinRTResolver myResolver = new StreamUriWinRTResolver();
// Pass the resolver object to the navigate call.
webView.NavigateToLocalStreamUri(url, myResolver);
}
private void webView_ScriptNotify(object sender, NotifyEventArgs e)
{
System.Diagnostics.Debug.WriteLine("ScriptNotifyValue: " + e.Value);
}
}
public sealed class StreamUriWinRTResolver : IUriToStreamResolver
{
public IAsyncOperation<IInputStream> UriToStreamAsync(Uri uri)
{
if (uri == null)
{
throw new Exception();
}
string path = uri.AbsolutePath;
// Because of the signature of the this method, it can't use await, so we
// call into a seperate helper method that can use the C# await pattern.
return GetContent(path).AsAsyncOperation();
}
private async Task<IInputStream> GetContent(string path)
{
// We use app's local folder as the source
try
{
Uri localUri = new Uri("ms-appdata:///local" + path);
StorageFile f = await StorageFile.GetFileFromApplicationUriAsync(localUri);
IRandomAccessStream stream = await f.OpenAsync(FileAccessMode.Read);
return stream;
}
catch (Exception) { throw new Exception("Invalid path"); }
}
}
For more info, please see Remarks and Examples in WebView.NavigateToLocalStreamUri method and also Custom URI resolving in What’s new in WebView in Windows 8.1. Besides, there is also a WebView control (XAML) sample on GitHub.
I want to load contents of below web page in console application using c#.
http://justicecourts.maricopa.gov/findacase/casehistory.aspx
Using below code I am getting empty on the screen but it works perfectly if I load google.com web page.
By using WebClient and WebRequest I was getting error "Please enable javascript" and content was not loading so I used below code and javascipt error is not displaying now but web page content is not loading. I am struggling with this issue quite from long time, have seen lot of post regarding this and couldn't get this work.
Could anyone please help?
Thanks in Advance..
class Program
{
private static bool completed = false;
private static WebBrowser wb;
[STAThread]
static void Main(string[] args)
{
wb = new WebBrowser();
wb.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(wb_DocumentCompleted);
wb.Navigate("http://justicecourts.maricopa.gov/findacase/casehistory.aspx");
while (!completed)
{
Application.DoEvents();
Thread.Sleep(100);
}
Console.Write("\n\nDone with it!\n\n");
Console.ReadLine();
}
static void wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
Console.WriteLine(wb.Document.Body.InnerHtml);
completed = true;
}
}
If you literally just want to dump the contents of that URL out to the console, try this:
using(WebClient client = new WebClient()) {
Console.WriteLine(client.DownloadString(url));
}
try adding more wait.
static void Main(string[] args)
{
wb = new WebBrowser();
wb.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(wb_DocumentCompleted);
wb.Navigate("http://justicecourts.maricopa.gov/findacase/casehistory.aspx");
while (!completed)
{
Application.DoEvents();
Thread.Sleep(100);
}
//wait even more
for (int i = 0; i < 6; i++)
{
Application.DoEvents();
Thread.Sleep(1000);
}
Console.Write("\n\nDone with it!\n\n");
Console.ReadLine();
}
otherwise you can use EO Browser it is paid. but in your case trail will work cause it is not GUI application.as it shows trail message in GUI.
in EO you can say..
EOContorol.WebView.LoadUrlAndWait(URL);
Try using PhantomJs
basicaly like running a webbrowser without a window. (headless)
I have a webservice that I am trying to authenticate with in the background using a webview. When I initially send the request it will work appropriately (failure/success based on credentials), but after it seems like I am getting a cached response.
Here is my webview setup code:
WebView browser = new WebView(this);
WebSettings settings = browser.getSettings();
settings.setJavaScriptEnabled(true);
settings.setSavePassword(false);
settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
settings.setAppCacheEnabled(false);
browser.setWebChromeClient(new WebChromeClient() {
public void onProgressChanged(WebView view, int progress) {
Log.d("BROWSERPROGRESS", Integer.toString(progress));
}
});
jsInterface = new AddAccountJSInterface();
browser.addJavascriptInterface(jsInterface, "ADDACCOUNTJSINTERFACE");
browser.setWebViewClient(new AddAccountClient(this));
So as you may see I have two additional classes controlling my webView:
An object that provides an interface for javascript (AddAccountJSInterface)
A WebViewClient
Additionally I do have a WebChromeClient, but it's only there for debugging and I'm pretty sure that it won't interfere with anything.
The JS interface simply provides an easy way of getting the body HTML for performing analysis, so I'm confident that isn't the issue either.
The WebViewClient has the following code in it which does most of the "custom" work for routing based on various responses from the webservice.
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if(url.contains(INSTALL_PREFIX)) {
HashMap<String, String> params = extractParameters(url);
verificationComplete(params);
return true;
}
return false;
}
#Override
public void onPageFinished(WebView view, String url){
if(invalidShop(view)) {
Toast.makeText(context, context.getString(R.string.no_find_shop), Toast.LENGTH_SHORT).show();
shopAddressField.requestFocus();
replaceUiElements(loadingBar, addAccountButton);
} else if(url.contains(ADMIN_AUTH_LOGIN)) {
if(invalidLogin(view)) {
Toast.makeText(context, context.getString(R.string.invalid_login),Toast.LENGTH_SHORT).show();
emailField.requestFocus();
replaceUiElements(loadingBar, addAccountButton);
} else {
String email = emailField.getText().toString();
String password = passwordField.getText().toString();
String submitJS = String.format(FORM_SUBMISSION_JS, email, password);
jsInterface.setInnerHTML("");
browser.loadUrl(submitJS);
}
}
}
In my activity I have 3 text fields that I need to fill followed by clicking a button to submit it. The activity then takes in the data from 3 text fields (shopAddressField, usernameField, passwordField) and then executes some javascript that populates some form data (which was loaded in the invisible webView) then clicks the submit button.
It is the last part that is messing up, which appears to be caching the response from the server (perhaps using cookies?) and return that instead of asking the server if the data is correct or not.
A bit of clarification:
JSInterface is simply a Java object that allows me to execute javascript on my webview which is tied to a function within that object. In my case my JSInterface has one function which is setInnerHtml(String html).
This is the javascript that is executed on the webview:
javascript:window.ADDACOUNTJSINTERFACE.setInnerHTML(document.body.innerHTML)
And this is the setInnerHtml function:
public void setInnerHtml(String innerHtml) {
this.innerHtml = innerHtml;
}
So when I actually execute jsInterface.setInnerHtml("") I'm just over-writing the HTML that was pulled in (to be sure I'm not getting my old data from there for some reason).
As for my submitJS it is once again some Javascript that is executed on my webView as follows:
// submitJS will be something like this once all the credentials have been set
// Note: I know that the server will make jQuery available
// Note: Much of the Java string formatting has been removed to help clarify
// the code.
String submitJS =
"javascript:(function() {
$('login-input').value='username';
$('password').value='password';
$('sign-in-form').up().submit();
})()"
// I then simply get the webview to execute the javascript above
webView.loadData(submitJS);
So it turns out the problem wasn't based around the Caching, and possibly not cookies.
When executing javascript on your webView it does this in a separate thread and can be quite slow. This lead to a race condition which caused code to be executed in the wrong order.
I've solved this problem by using a Semaphore as a Mutex. This allows me to prevent my getter from returning before the Javascript on the webView is able to execute.
The interface I created now looks like this:
private class AddAccountJSInterface {
private final String TAG = getClass().getName().toUpperCase();
private Semaphore mutex = new Semaphore(1, false);
private String innerHTML;
public void aquireSemaphore() {
Log.d(TAG, "Attempting to lock semaphore");
try {
mutex.acquire();
} catch(InterruptedException e) {
Log.d(TAG, "Oh snap, we got interrupted. Just going to abort.");
return;
}
Log.d(TAG, "Semaphore has been aquired");
}
#SuppressWarnings("unused")
public void setInnerHTML(String html) {
this.innerHTML = html;
Log.d(TAG, "setInnerHTML is now releasing semaphore.");
mutex.release();
Log.d(TAG, "setInnerHTML has successfully released the semaphore.");
}
public synchronized String getInnerHTML() {
Log.d(TAG, "getInnerHTML attempting to aquire semaphore, may block...");
String innerHTML = "";
try {
mutex.acquire();
Log.d(TAG, "getInnerHTML has aquired the semaphore, grabbing data.");
innerHTML = this.innerHTML;
Log.d(TAG, "getInnerHTML no longer needs semaphore, releasing");
mutex.release();
} catch (InterruptedException e) {
Log.d(TAG, "Something has gone wrong while attempting to aquire semaphore, aborting");
}
return innerHTML;
}
}
Now the way I use this in my code is as follows:
// I have access to the jsInterface object which is an instance of the class above as well as a webView which I will be executing the javascript on.
String getInnerHtmlJS = "javascript:window.MYJSINTERFACE.setInnerHTML(document.body.innerHTML);"
jsInterface.aquireSemaphore()
// Execute my JS on the webview
jsInterface.loadUrl(getInnerHtmlJS)
// Now we get our inner HTML
// Note: getInnerHTML will block since it must wait for the setInnerHTML (executed via the JS) function to release the semaphore
String theInnerHTML = jsInterface.getInnerHTML();