I am trying to implement an app, that uses WebView to show a website which has a form inside. The problem is, when user fills the form and just before the Send button is pressed, the internet connection is lost. In this case, I receive onErrorReceived callback, and here is my implementation:
#Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
mDisconnected = true;
mDisconnectUrl = failingUrl;
mWebView = view;
mWebView.saveState(mWebViewState);
}
So I try to store the content of the form here and the failing url, so I can continue sending it when the connection established again. So I have another thread trying to make a new Socket connection with the server, and when it succeeds, I call a function in my WebViewClient class reconnect():
void reconnect(Context appContext) {
if (mDisconnected) {
try {
((Activity) appContext).runOnUiThread(new Runnable() {
public void run() {
mWebView.reload();
mWebView.restoreState(mWebViewState);
}
});
} catch (Exception e) {
mWebView.goBack();
}
mDisconnected = false;
}
}
So I try to make a reload and restore the form data so the WebView can continue with its send operation. The problem is when the page is again loaded, all the data entered in the form are lost and it does not continue with the Send operation, which was already clicked before the connection is lost. Has any one any idea, how to solve this problem.
The data is very sensitive, so it is important to continue with sending. It is also possible to let the user fill the form again and send it again, but it is not wanted in this scenario.
EDIT:
Maybe the question can be simplified. Is there any function or way to refresh a website in WebViewClient, which continues where it was left of and retrieves the form data into the state before the problem was occured.
Use database to store the contents of form and then send the data through service.
Delete the contents of database upon successfull submission.
Related
I have an android app with Azure Mobile Services and implemented Offline Sync. The app works well but when syncing data it seems not to complete so there is always a few rows on tables which have not synced?
Anyone have any ideas what the problem might be. I believe that on the next try it would finish where it left off or am I wrong?
Thanks in advance
The app works well but when syncing data it seems not to complete so there is always a few rows on tables which have not synced?
I would recommend you use fiddler to capture the network traces when handling the sync operations.
For Incremental Sync, the request would be as follows:
Get https://{your-app-name}.azurewebsites.net/tables/TodoItem?$filter=(updatedAt%20ge%20datetimeoffset'2017-11-03T06%3A56%3A44.4590000%2B00%3A00')&$orderby=updatedAt&$skip=0&$top=50&__includeDeleted=true
For opting out of incremental sync, you would retrieve all records without the filter updatedAt.
Get https://{your-app-name}.azurewebsites.net/tables/TodoItem?$skip=0&$top=50&__includeDeleted=true
Note: If there are too many items, the SDK would send multiple requests to pull all items that match your given query from the associated remote table. Also, you need to make sure you specify the includeDeleted() in your query.
In summary, you need to make sure that all items could be retrieved via the above requests. Additionally, if the pull operation has pending local updates, then the pull operation would first execute a push operation. So, I assume that you could catch the exception when calling pull operation for handling the conflict resolution.
Bruce's answer is fine but I used a slightly different method without the need to use fiddler.
I change my connection from this
mClient = new MobileServiceClient("[AZUREWEBSITE]", cntxall);
mClient.setAndroidHttpClientFactory(new MyOkHttpClientFactory());
To this
mClient = new MobileServiceClient("[AZUREWEBSITE]", cntxall).withFilter(
new ServiceFilter() {
#Override
public ListenableFuture<ServiceFilterResponse> handleRequest(ServiceFilterRequest request, NextServiceFilterCallback nextServiceFilter) {
// Get the request contents
String url = request.getUrl();
String content = request.getContent();
if (url != null) {
Log.d("Request URL:", url);
}
if (content != null) {
Log.d("Request Content:", content);
}
// Execute the next service filter in the chain
ListenableFuture<ServiceFilterResponse> responseFuture = nextServiceFilter.onNext(request);
Futures.addCallback(responseFuture, new FutureCallback<ServiceFilterResponse>() {
#Override
public void onFailure(Throwable e) {
Log.d("Exception:", e.getMessage());
}
#Override
public void onSuccess(ServiceFilterResponse response) {
if (response != null && response.getContent() != null) {
Log.d("Response Content:", response.getContent());
}
}
});
return responseFuture;
}
}
);
This is the logging method for Azure connections and shows the request in the log.
I need to parse JSON content out of every page in my WebView, before it is shown to the user.
In order to do so, I need to parse the JSON element and only then I’m able to use the shouldOverrideUrlLoading method. I can’t use it before I’ve parsed the JSON object because I rely on that element as part of my implementation of that method.
I have created a variable called jsonParsed to indicate whether I have all needed information out of the JSON object.
At first, it is initialized to be false.
When I get a response from the server I know the object was parsed so I change the variable to be true in the onLoaded method, and then the code is ready to get the information and check it.
This is the wanted process:
Opening new page by the user
Entering automatically to shouldOverrideUrlLoading and then to the if statement(JSON wasn't parsed yet)
Getting the JSON response in OnResponse
Going back to shouldOverrideUrlLoading and entering the else statement (JSON is parsed)
As you can see, each process should go through both if and else statements, the if before the JSON was parsed, and the else after it was parsed.
The problem:
shouldOverrideUrlLoading is reached every step 1, but each time it goes into a different statement. The first time goes into the if (steps 2+3), and the next one goes into the else (step 4) and this process repeats itself...
I think the this line webView.loadUrl(loadingURL); doesn't call shouldOverrideUrlLoading for some reason.
Here is my code snippet:
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon)
{
super.onPageStarted(view, url, favicon);
mProgressDialog.show();
}
#Override
public boolean shouldOverrideUrlLoading(WebView wView, String url){
loadingURL=url;
if (!jsonParsed){
/** JSON object is not parsed
* Some code for adding request
to requestQueue and than loading new url in onResponse method... */
}
else {
/** JSON object is parsed, starting checking process
* I need to check this URL and decide whether or not to override it*/
jsonParsed=false;
Root root = new Gson().fromJson(jsonResponse, Root.class);
for (Page page : root.query.pages.values()) {
firstParagraph=page.extract; //Parsing process, works fine
}
if (firstParagraph!=null) {
//If the webView isn't Wikipedia's article, it will be null
if (firstParagraph.contains(allowedContent)) {
//Passing URL to view
return true;
}
}
return false; //The URL leads to unallowed content
}
}
#Override
public void onPageFinished(WebView view, String url) {
CloseBlocksAndDisableEdit(view);
mProgressDialog.dismiss();
}
private final Response.Listener<String> onLoaded = new Response.Listener<String>() {
#Override
public void onResponse(String response) {
jsonResponse=response; //Getting response
jsonParsed=true; //Object was examined
webView.loadUrl(loadingURL);
}
};
What can be the cause of this problem?
If it's not possible in this way, I'd like to know which way is better...
I have found this answer:
https://stackoverflow.com/a/6739042/7483311
It is written in this answer:
After some research I conclude that despite what most of the tutorials
out there say, shouldOverrideUrlLoading() does not get called when:
You load a URL like:
loadUrl("http://www.google.com");
The browser redirects the user automatically via an HTTP Redirect.
This probably makes my code impposible.
In this line: webView.loadUrl(loadingURL);, I'm making an https request that was not made by the user in the WebView, and that's why it doesn't call shouldoverrideurlloading.
I have a bunch of products in my app and when the user purchases the products, the product count gets updated but if I shut down the app and restart it again, the product count gets lost. Currently, I'm saving the data in a document but I came across sharedpreferences which I feel like is a better way to save the data for the app. I would like to know how can I save the data and read it again. Would this be a better approach to save the data and not lose it.
private void saveInterimStocks() {
InterimStockDao dao = new InterimStockDao(CouchManager.OPERATIONAL_DB, AirFiUtils.getOperationalDatabaseIndexMap());
// saving the data in StockCarryForward Document - JSON doc
StockCarryForward stock = new StockCarryForward();
if (getFlightLegSubscription().isPresent()) {
stock.setFlightKey(getFlightLegSubscription().get().getFlightMetadata().getFlightKey());
}
if (getFlightLegIdentifier().isPresent()) {
stock.setFli(getFlightLegIdentifier().get());
}
stock.setStockMap(AirFiApplication.getStockMap());
dao.createOrUpdate(stock);
}
If I logout of the app and then shutdown and restart it, the data is not lost. Only if I shut down without logging out, the data gets lost.
#Override
public void onStop() {
super.onStop();
if (getAirlineProfile().isAutomaticLogoutEnabled()) {
stopLogoutTimer();
}
if (getAirlineProfile().isStocksEnabled()) {
saveInterimStocks();
}
}
Here is the onStop() of the activity where the method gets called.
I am using this library for connecting to a websocket server from android.
Specifically this part :
AsyncHttpClient.getDefaultInstance().websocket("ws://192.168.2.10:8000/temp" , "my-protocol", new WebSocketConnectCallback() {
#Override
public void onCompleted(Exception ex, WebSocket webSocket) {
if (ex != null) {
ex.printStackTrace();
return;
}
webSocket.send("a string");
webSocket.setStringCallback(new StringCallback() {
#Override
public void onStringAvailable(String s) {
Debug.Log( LOGTAG ,"I got a string: " + s);
}
});
webSocket.close(); // issue here
}
});
I would like to close the socket when I click a button. Now everytime I want to send a message to the socket I open it and close it.
I would like to open it once and keep it alive and close it when I click a close button. My idea was to pass a variable to the WebSocketConnectCallback and make a static variable and based on this variable close the socket.
I would like to know what is the best practice in a situation like this.
Use the Application class (http://developer.android.com/reference/android/app/Application.html):
Inherid your own class for Application and here you can track the socket, open it, close it as you need.
See a tutorial (first google match, maybe there is a better one): http://www.intertech.com/Blog/androids-application-class/
So basically extend Application and add your class in the manifest file as application class.
Your may add a timer that might close the socket after several time while not used.
We suppose that a url is already loaded (Let's call it the original url).
webView.setWebViewClient(new WebViewClient() {
#Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
webView.loadUrl("file:///android_asset/missing.html");
}
});
I have created my own error page to prevent the "web page not available message" to appear.
The app must reload the webview every time it is resumed. So i have the following lines of code:
#Override
protected void onResume() {
super.onResume();
webView.reload();
}
The problem here is that when the error page is loaded (for example when the user is not connected to the internet), and then a connection is available again and the user resumes the app, the original url is not loaded (which seems logic, the current now is missing.html). But is this a nice approach? Any suggestions for the problem?
There is also a refresh button if the user wants to reload the content. Same issue here.
You should load the wanted url instead of using webView.reload(), like this:
webView.loadUrl("http://yoururl");
or go back to previous page with:
webView.loadUrl("javascript:history.go(-1)");
I'd examine internet connectivity in onResume() like suggested on http://developer.android.com/training/basics/network-ops/managing.html
public static boolean isOnline() {
ConnectivityManager connMgr = (ConnectivityManager) App.instans.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
return (networkInfo != null && networkInfo.isConnected());
}
public void onResume() {
It's not just the mobile connection that can drop but also the page can become unavailable and as you rightly say using webview.loadUrl("someerrorpage.html") will create fake history and cause problems if the user tries to refresh or press back.
My solution was to replace the contents of the default error page with my own custom one e.g.
webView.evaluateJavascript("javascript:document.open();document.write('your custom error html');document.close();", null);