As the CookieSyncManager.getInstance().sync(); is deprecated Itried to maintain cookies forever in my application using new command
flush() :
webview.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
CookieManager.getInstance().setAcceptCookie(true);
CookieManager.getInstance().acceptCookie();
CookieManager.getInstance().acceptThirdPartyCookies(webview);
CookieManager.getInstance().flush();
}
// and more settings for webview
}
But every time I open the app it seems that previous cookies were expired. Do those options help preserving cookies? And Should I put them in onPageFinished?
Besides I have to say that the cookies are working fine on the target website and are set to live for 100 days. Also minSdkVersion is 21 and targetSdkVersion is 29.
Using PersistentCookieJar — persistent and good for encapsulating the Cookies within the App itself. please checkthis
every time APP plan to launch Webview, the cookies will need to be copied from the PersistentCookieJar to the CookieManager.
Related
I have an android application that has native framework and content itself is presented in web format and in webview. The meaning of the application is to allow users to use the device using predefined
services that may require autentication.
How ever when I try to clean up the webview caches after user has completed his/her tasks the webview will remember everything and all e.g. login credentials are in place, history remains etc.
I have tried the following to do the clean up without any success, what I am missing in this ?
(wvfo if the overlay fragment in which the webview is that each service is using)
wvfo.getWebView().clearCache(true);
wvfo.getWebView().clearFormData();
wvfo.getWebView().clearHistory();
wvfo.getWebView().clearMatches();
// wvfo.getWebView().setWebViewClient(new WebViewClient());
// wvfo.loadUrl("javascript:document.open();document.close();");
CookieManager.getInstance().removeAllCookies(null);
CookieManager.getInstance().flush();
wvfo.destroyWebview();
Any ideas what is wrong with this and why the history doesn't get cleared ?
Thanks in advance
Yes, You have to delete webview default DB also. Check the below code.
static void clearWebViewAllCache(Context context, WebView webView) {
try {
AgentWebConfig.removeAllCookies(null);
webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
context.deleteDatabase("webviewCache.db");
context.deleteDatabase("webview.db");
webView.clearCache(true);
webView.clearHistory();
webView.clearFormData();
clearCacheFolder(new File(AgentWebConfig.getCachePath(context)), 0);
} catch (Exception ignore) {
//ignore.printStackTrace();
if (AgentWebConfig.DEBUG) {
ignore.printStackTrace();
}
}
}
I hope, this will help you.
Happy codding...
Is there a way to check if webview is available on the device?
Background:
If I add <uses-feature android:name="android.software.webview"/> to the Manifest the number of supported devices on Google Play drops from over 12,000 to less than 6,000 devices. So I added android:required="false" to this feature. In case webview is available websites should be displayed inside the app otherwise launched in the default browser:
String mUrl = "http://www.example.com";
if (*** WHAT TO PUT HERE? ***) {
WebView webview = (WebView) findViewById(R.id.webView);
if (Build.VERSION.SDK_INT >= 24) {
webview.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
view.loadUrl(request.toString());
return false;
}
});
} else {
webview.setWebViewClient(new WebViewClient() {
#SuppressWarnings("deprecation")
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return false;
}
});
}
webview.loadUrl(mUrl);
} else {
Uri uri = Uri.parse(mUrl);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
Edit (to make things clear): <uses-permission android:name="android.permission.INTERNET" /> is (and always had been) part of the manifest. It’s just the addition of <uses-feature android:name="android.software.webview" /> which causes the drop of supported devices.
There is someone having the same issue here: https://issuetracker.google.com/issues/37035282
(unfortunately not answered)
Although #vsatkh pointed out that it is not necessarily needed to declare this feature as required, you can check the device’s feature compatibility as follows:
getPackageManager().hasSystemFeature("android.software.webview")
This method returns true or false.
Some additional information about Google Play’s filtering:
Google Play only filters supported devices based on <uses-feature> elements declared in the manifest. <uses-permission> elements don’t affect Google Play’s filtering unless they imply a feature. android.permission.INTERNET does not imply any feature. Permissions that imply features are listed here:
https://developer.android.com/guide/topics/manifest/uses-feature-element.html#permissions
Alternative solution:
val hasWebView: Boolean = kotlin.runCatching { CookieManager.getInstance() }.isSuccess
It works because if WebView is not available then CookieManager.getInstance() will throw AndroidRuntimeException.
getPackageManager().hasSystemFeature("android.software.webview") is not reliable. It will return true if the device is supposed to have a webview, but has disabled it in system settings.
I tested it on a Huawei P20 by going to Settings -> Apps -> Android System WebView -> Disable
There is a way around: CookieManager
AFAIK: There is no dedicated API that gives you that information. But if I told you there is a way around.
As Torkel mentioned, getPackageManager().hasSystemFeature(..) does not guarantee to tell the Android System WebView is enabled or disabled.
getPackageManager().hasSystemFeature(..) will still return true even a user disables the WebView in Settings.
CookieManager
So an alternative would be to use CookieManager.
An application WebView cookie is managed by CookieManager. It throws an exception if you try to create CookieManager instance when the WebView is disabled.
In Kotlin, you can get result from block statements but it is not possible in Java. So you can use Exception Handling logic in a function to decide.
Here is an example
public boolean webViewEnabled(){
try{
CookieManager.getInstance();
return true;
}catch(Exception e){
return false;
}
}
Note:
The current implementation might change in the future, or they might come up with a dedicated API for this. I have tried finding a perfect solution in many forums but this is the closest I have reached.
This solution will prevent your app from crashing but you will still see the error in your logs because CookieManager.getInstance throws an Exception object.
You can read more detail here https://source.android.com/compatibility/android-cdd.pdf.
According to the section WebView Compatibility, android.software.webview feature is indicate that device or oem must provides a complete implementation of the android.webkit.WebView API. So the number of device on play store was drop because actually, most device did not fully implemented all api required by webkit.
So if your web content required certain html5 feature please check here http://mobilehtml5.org/ if your app target kitkat or even lollipop+ then you should be safe.
So there is no need to declare android.software.webview feature unless your web content really need all api of webkit.
As of API 26, you can use WebView.getCurrentWebViewPackage() to check if there is a valid WebView installed and active. The CookieManager check can be really slow (upwards of 500ms) and can impact startup performance if the check is happening in the critical path.
From the documentation:
If WebView has already been loaded into the current process this method will return the package that was used to load it. Otherwise, the package that would be used if the WebView was loaded right now will be returned; this does not cause WebView to be loaded, so this information may become outdated at any time.
The WebView package changes either when the current WebView package is updated, disabled, or uninstalled. It can also be changed through a Developer Setting. If the WebView package changes, any app process that has loaded WebView will be killed. The next time the app starts and loads WebView it will use the new WebView package instead.
#return the current WebView package, or {#code null} if there is none.
You can verify that this is working as expected by swapping the WebView implementation and then calling this method to list the PackageName (e.g if you're running the dev/beta Chrome WebViews) by calling:
WebView.getCurrentWebViewPackage()?.packageName
You can test disabled state by running the following ADB command (although you might need an emulator running as root via adb root): adb shell pm disable <webview-package-id>.
WebView has been had in Android API 1 so it's always available what's not it's the internet connection
https://developer.android.com/reference/android/webkit/WebView.html
In you manifest you need internet permission :
<uses-permission android:name="android.permission.INTERNET" />
I'm new to Android, and I'm working on a simple WebView app.
I'm using shouldOverrideUrlLoading to handle some commands from my remote HTML to Android.
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if(!url.startsWith("https://www.mywebsite.com)) {
switch (url) {
case "mycmd://app_logoff":
Toast.makeText(context, getString(R.string.logoff_ok), Toast.LENGTH_SHORT).show();
appLogoff();
break;
default:
view.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(urlWeb)));
}
}
return true;
I tested on my Android device (Motorola X Play) and it worked good! Is it safe to say that this will work on all android devices that matches the app minimum API level? Is this a good practice?
Since you are using shouldOverrideUrlLoading means you are implementing the custom WebViewClient and shouldOverrideUrlLoading should give your app a chance to take over the control when a new url is about to be loaded in the current WebView.
As per the developer documentation this API is added in API Level 1 so that basically means it is supported since long and should not be a concern as far as minimum API level.
I guess no one can surely say that it will work or not work on ALL devices unless actually tested on them. You may want to test them using Android emulators with different configurations such as different SDK/Platforms, API Level etc. to be double sure.
As a side note and caution, This method is not called for requests using the POST "method".
from webview android exapmle, shouldOverrideUrlLoading is used.
However, it is deprecated since api 24.
boolean shouldOverrideUrlLoading (WebView view, String url)
New replacement API with new parameter is here.
boolean shouldOverrideUrlLoading (WebView view, WebResourceRequest request)
I noticed that with the last update of Google System WebView, all the links in my WebViews are opened in the view itself. But according to the documentation from google:
public boolean shouldOverrideUrlLoading (WebView view, String url)
Added in API level 1
Give the host application a chance to take over the control when a new url is about to be loaded in the current WebView. If WebViewClient is not provided, by default WebView will ask Activity Manager to choose the proper handler for the url. If WebViewClient is provided, return true means the host application handles the url, while return false means the current WebView handles the url. This method is not called for requests using the POST "method".
I did not provide custom WebViewClient.
NOTE: The device that I noticed the problem was HTC One with the latest Google System WebView from June 8, 2015
I can reproduce the findings. Android System WebView 43.0.2357.121 exhibits the behavior that you describe, while the version I had on before upgrading to it did not.
I have filed a bug report on this and now need to do more testing and warn the world.
Thanks for pointing this out!
UPDATE
Here is a blog post that I wrote on this subject. Quoting myself from it:
My recommendation at the moment is:
Always attach a WebViewClient to your WebView
Always implement shouldOverrideUrlLoading() on the WebViewClient
Always return true to indicate that you are handling the event
Always do what your app needs to have done, whether that is loading
the URL into the WebView or launching a browser on the URL (rather
than returning false and relying on stock behavior)
Something like this static inner class appears to do the trick —
create an instance and pass it to setWebViewClient() on your
WebView:
private static class URLHandler extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (shouldKeepInWebView(url)) {
view.loadUrl(url);
}
else {
Intent i=new Intent(Intent.ACTION_VIEW, Uri.parse(url))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
view.getContext().startActivity(i);
}
return(true);
}
private boolean shouldKeepInWebView(String url) {
return(true); // or false, or use regex, or whatever
}
}
(where you would put your business logic in shouldKeepInWebView() to
determine whether or not a given URL should stay in the WebView
or launch a browser)
Seems to me this issue was resolved in 44.0.240.54.
My Sign Up Process produces cookies in a WebView, not in native code. All my tests depend on the cookies retrieved from the Webview so I need a way to extract data from a webview inside a Robotium test. How can this be done? Here is my WebView fragment:
public class MyWebViewFragment extends Fragment {
private CookieManager cookieManager;
#ViewById
WebView myWebView;
#AfterViews
void theAfterViews() {
myWebView.getSettings().setJavaScriptEnabled(true);
myWebView.getSettings().setDomStorageEnabled(true);
CookieSyncManager.createInstance(getActivity());
cookieManager = CookieManager.getInstance();
myWebView.loadUrl(theURL);
myWebView.setWebViewClient(new WebViewClient()
{
public void onPageStarted(WebView view, String url, Bitmap favicon) {
if ((url != null) && (url.equals(theURL)))
{
String theCookies = cookieManager.getCookie(url);
// ######## I need to pull these Cookies out here in the Robotium test. How do I use Solo etc to do this?
}
}
}
}
I need to know how to write a Robotium test that will at the right point pull out the values of the Cookies and save it for the rest of the tests to use. I need to get thiw working or none of my other tests will run. Thanks
The simple answer as i think you may know having seen your other questions is to get hold of the fragment and then ask the fragment for the value. Potentially you might consider mocking this functionality out for your tests or allow your tests a method to be able to set the cookies for itself etc (not sure if this is feasible for your case or not.)