I have a list of web urls. I need to open them all at once in an external browser where each link's page results in a new tab. So for example, if the list has a link to google.com and yahoo.com they should both be opened in the browser in their own separate tabs.
To open a single link within an external browser is pretty straight forward - create an ACTION_VIEW intent, set the url and call startActivity. But how can I do this with multiple urls? Doing multiple startActivity calls won't work. Any ideas?
Thank you for your time!
My answer is based on a little research so I am not sure about the exact code but I will suggest an approach here
First you can have a Fragment or Activity with the WebView view type in the layout file respectively and depending on your use case, get the reference for the WebView. We will pass your list of urls to the activity containing the Webview
Say, we do it like so
val arrayList = getIntent().getArrayListExtra("urls")
val webView = findViewById<WebView>(R.id.myWebview)
Now we can listen to scroll changes of the WebView and wait for the user to reach the end of the WebView page as given in the following link
How to check the scrollend of WebView
And then when the scroll end is reached, we can load the next url from the array
webView.setOnScrollChangeListener((view, i, i1, i2, i3) -> {
int r1 = webView.computeVerticalScrollRange();
int r2 = webView.computeVerticalScrollExtent();
if (i1 == (r1 - r2)) {
webView.loadUrl(arrayList.get(1) // Or Whatever your next
index is
// Maybe maintain a global variable for which url is currently
loaded
}
});
To make the experience nice for the user you can consider setting a WebViewClient for your WebView and override onProgressChanged to show the progress to the user, just in case he doesn't feel lost
Related
I'm trying to implement a simple web browser app using WebView with android studio. I want to save my WebView's state (e.g. WebBackForwardList) to internal storage and restore it when i relaunch the app. I don't want to just load last visited url, instead i also want that when i call goBack() or goForward() methods of my WebView i want it to restore previous and next visited links from internal storage.
I've currently implemented my own WebView, WebViewClient and WebChromeClient and saving visited links as strings to internal storage so i can load them by calling loadUrl() in override methods of goBack() and goForward() while restoring. But this makes pages to load slower than WebView's own goBack() and goForward() methods and also it doesn't restore scroll position, zoom etc.
So i have two questions;
there is a way to get WebBackForwardList of WebView by calling copyBackForwardList() method, but is there a way to set/edit WebBackForwardList?
If not, how can i save and restore it after restart the app?
This is not the best solution, but i implemented a workaround for this situation. In simple words, I'm saving WebBackForwardList's items' URL's from zero to current index to a Stack named backStack, from current index plus one to last index to forwardStack. Then I save these stacks and current item's URL to internal storage.
When the app is restarted, WebView loads the current item URL. When the user wants to go back or forward, I first check wether WebView can go back or forward. If it can, I call goBack() method of WebView. If it can't, then I pop from the stacks I saved to internal storage and load the URL.
But when loading URL from backStack, I had to push current WebBackForwardList's items' URL's to forwardStack and clear WebView history. This is necessary because WebView saves last loaded URL to last index of WebBackForwardList. And this distorts WebBackForwardList because I am loading a previous item.
It's currently working fine, but as I said this is not the best solution.
You shoud use restoreState and saveState to implement saving & restoring.
class AppWebTab extents WebView{
long unique_tab_id;
public void boolean load(){
File stackpath = new File(getContext().getExternalCacheDir(), "webstack"+unique_tab_id);
if(stackpath.exists()) {
Parcel parcel = Parcel.obtain();
byte[] data = ...//read file as Byte Array
parcel.unmarshall(data, 0, data.length);
parcel.setDataPosition(0);
Bundle bundle = new Bundle();
bundle.readFromParcel(parcel);
parcel.recycle();
WebBackForwardList stacks = restoreState(bundle);
if(stacks!=null && stacks.getSize()>0) {
return true;
}
}
return false;
}
public void save(){
File stackpath = new File(getContext().getExternalCacheDir(), "webstack"+unique_tab_id);
Bundle bundle = new Bundle();
WebBackForwardList stacks = saveState(bundle);
if(stacks!=null&&stacks.getSize()>0) {
Parcel parcel = Parcel.obtain();
parcel.setDataPosition(0);
bundle.writeToParcel(parcel, 0);
byte[] bytes = parcel.marshall();
parcel.recycle();
...//save Byte Array Data to file
} else {
stackpath.delete();
}
}
}
Thanks to the effort put into chromium, the saved BackForwardList is even navigable when the device is offline but the local cache is available.
But you cannot modified WebBackForwardList directly. So if you have multiple webviews representing one single browser tab (luxury but sometimes useful), It's hard to merge and save multiple BackForwardList as one, which is a pity.
Like #j__m said, system upgrade and format change may cause this method to fail. After testing, the data of WebView (WEBVIEW_CHROMIUM_STATE) has a certain downward compatibility, while the Parcelable does not.
In the test, the data stored using Parcel cannot cross the Android version. For this reason, I must use ObjectOutputStream to implement my own serialization method, which is almost the same. As a result, the data saved with ObjectOutputStream survived the update from Android 4.4 to Android 9, and can be parsed to restore the pages successfully.
code
Data format compatibility must be considered, especially when syncing tabs across various versions of android devices.
Now using this technology + webdav server and with the same version of webview.apk installed, I enable the user to share browsing states from android 9 -> android 6, and vice versa !
If the versions of webview.apk differ, or if it's not installed at all, then you will have forward-compatibility issues and the web page will apear blank (no crashing observed). but the backward-compatibility is still ok.
2021 Update : observed crash after downgrading webview.apk from version 92.* dev to version 91.* . It says render process crashed, so you need to implement onRenderProcessGone and return true in your custom WebViewClient.
There is a rather specific webpage loaded into WebView which URL is like http://www.site.com/mob/ (basically a mobile-optimized web page). This webpage display 25 articles only and on the bottom is a button "More articles".
When a user presses it, I catch URL http://www.site.com/Web/MobHomeItems.aspx?page=N (where N is 2, 3, 4...) and after that another 25 items have been loaded on the same screen.
Now, when I click on some article and go to article details, and later return to the page via the Back key, the WebView forgets how many articles have been loaded and simply loads the default page with 25 displayed articles. Imagine how frustrating this would be to a user if he came to 100th article.
I tried overriding many methods in WebClient and in WebChromeClient, but so far I have been unable to load N number of pages loaded via "More Articles" button. For example, I first thought this would help, but it did not.
#Override
public void onLoadResource(WebView view, String url) {
//http://www.site.com/Web/MobHomeItems.aspx?page=2
if (url.contains("?page=")) {
//save this URL for later and on return from
// article details, pass it to LoadResource()
super.onLoadResource(view, url);
}
Then I tried similar approach with other method - basically remembering how many pages have been loaded on the main page, and then on return from article details, simply tell webview to load this URL.
Can anyone help me? How to append loaded pages to the main page? Should I use JavaScript here maybe?
PS. Loading mentioned URL http://www.site.com/Web/MobHomeItems.aspx?page=N does not help as it loads this concrete page into the WebView only, and it does not append this Nth page to the main page.
EDIT
As #Raghunandan asked, I do not have problems loading back to 1st page (?page=1). This is default when user presses Back button on article details. I want to load to the page where a user was before pressing article details. If he was on ?page=100, I want to load back to that page e.g. I want to have 25x100 articles open. Again, default is always "open 25 articles or ?page=1 or http://www.site.com".
Override the method shouldOverrideUrlLoading of WebViewClient.
like this:
public boolean shouldOverrideUrlLoading (WebView view, String url) {
if (url is kind of article detail) {
WebView newOne = new WebView(); // create a new Webview for displaying the details.
view.setVisibility(View.INVISIBLE); // hiding current page (article list)
return true; // To tell the WebView we have process this url.
}
return false;
}
The user click one link of article's detail.
shouldOverriderUrlLoading would be triggered.
We created one new WebView to open the url.
Hiding current page
The user reading artical
The user click back key, close the newOne WebView then make the
previous WebView visible.The article list will show up immediately and remained the old statement
.
There is a another way to do this.
The WebSettings has a private method "setPageCacheCapacity" , the default value is 0 , you could enlarge it (may be 5).
You can access this method by using reflection of java.
The method can enable WebView to cache more than one document. In the other word. when user press the back key, the WebView will go back to the older document.
I have an app with a previously-existing, web-based registration process that I am trying to use inside a WebView. I need to add some style tags to the html in order to hide some elements for better displaying the content inside my app. I can get it to work on initial load, but I cannot figure out how to do it from one page to the next inside the WebView. Here is what I have working:
On initial load of the site, I am getting the raw html and appending "<style>MY STYLES HERE</style>" to the string before calling
wv.loadDataWithBaseURL(url, rawHtml, null, "UTF-8", url);
This works perfectly, but if a user clicks a link on the page and it loads another page into the WebView, then this code does not get called and the style tag is lost.
I assume I need to override "shouldOverrideUrlLoading" in the WebViewClient, but I don't know how to intercept the html from here. I thought I would try something like:
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
String rawHtml = getRawHtml(url) + "<style>...</style>";
wv.loadDataWithBaseURL(url, rawHtml, null, "UTF-8", url);
}
But this obviously sends it into an endless loop of intercepting the load to start a new load.
I have also tried overriding onPageFinished and doing:
wv.loadUrl("javascript:(function() { ... })()");
which works, except that it waits until the entire page is loaded before executing. This causes the page to appear loaded with all of the UI elements in tact, and then all of the ones I am trying to hide suddenly disappear. My ultimate goal is to enhance the look and feel of the site on a mobile device, so this is not an option.
Is there something else I can do in "shouldOverrideUrlLoading" to inject style tags? Or if not, what else can I try?
I've run into this problem, and depending on the number of redirects, etc, we have not been able to make the injected JavaScript available all the time.
At minimum, you should use the wv.loadUrl("javascript:(function() { ... })()"); approach, but call it in both onPageStarted() and onPageFinished().
Depending on the complexity of your pages, you might need to inject the JavaScript in onLoadResource() as well.
I am working on an android project right now and have a question about how to do callbacks in different webviews. I used JSInterface for my project too. Here I have 2 webviews. One has an index page, anther is a overlay(still a html page though.) What I want to do is if any user clicks on some links on the overlay, it should fire a callback function which is written in the java file where the index page was connected to through JSInterface. It might sound confusing, but I have draw something to help make it clear!
Thanks!
You can use a custom URL scheme like myurl://function for your functionality links. Then write an event handler for the WebView's shouldOverrideUrlLoading event in which you decide how to process the URL: either instruct the webview to load it, or do some custom action.
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url)
{
if (url.startsWith("myurl://"))
{
// Parse further to extract function and do custom action
}
else
{
// Load the page via the webview
view.loadUrl(url);
}
return true;
}
I used startsWith to check the URL for this quick and dirty example, but you should consider using android.net.Uri.parse for parsing URLs.
This should allow you to call the Java function foo() without having to go through the first WebView.
If you want to go through the first webview, then you can call a function on the JSInterface like this (where webView1 is the first WebView retrieved through findViewById):
webView1.loadUrl("javascript:myjsinterface.myjsfunc();")
I am trying to launch a websearch using data input from a user. The data is input through TextEdit boxes. Upon submission of the data, i would like my program to: 1) search for a specific webpage based on the user input 2)Find specific elements at the webpage 3) Display the webpage.
Here is an example:
User Input (in a non browser/webview page)
1) Store Name: Macey's 2)Zip Code: 77471
In the background my program will:
1) Find the Macey's website
2) Find the store nearest zip code 77471
3) Load the Web page for the store nearest zip code 77471
Obviously there is a lot of error handeling, exceptions, ect that would go along with this. For the sake of making this example "easy" lets pretend that 1) A the Macey's main page exists 2)A sperate page for the 77471 store exists. 3)There is a link to the 77471 store on the Macey's main page.
I have the code for getting the user input variables and i know how to launch the webview. What i dont know how to do is to search for the Macy's home page, then find the link i am looking for on the homepage and navigate to it. Loading the webview is not the problem. Find the data is.
Below is my current code. Right now i am setup so that the user will navigate to the webpage they are looking for but i would rather handle the searching for them, if it is possible.
public void InitializeWebView(){
portal = (WebView)findViewById(R.id.web_Portal);
WebSettings Settings = portal.getSettings();
Settings.setSavePassword(false);
Settings.setSaveFormData(false);
Settings.setJavaScriptEnabled(true);
Settings.setSupportZoom(true);
Settings.supportZoom();
portal.setWebViewClient(new WebViewClient(){
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
}
public void searchAndShow(String Store, String zip){
portal.loadUrl("http://www.google.com");
}
You can get search result in JSON format from google using their API. Here is a nice example in JAVA. Just don't use key parameter until you do not have a vlid key.