Java – Android WebView cookie returns null
I want to set and retrieve a cookie in WebView in Android I've tried many cookie manager scripts to try to make it work I have JavaScript enabled
When running applications on Samsung S3 and Samsung Galaxy Tab 10.1, it seems that cookies are not set at all (Android 4.1) However, when the software is run on Samsung Galaxy ace, HTC Desire Z and Android simulator, the cookie is set and read completely normally
When working, WebView returns a string as expected. When not working, the output is just "null"; Cookie has no value / is not set
My specific situation also uses the sliding navigation class, which is an extension of actionbar Sherlock
I really appreciate any help. I've been trying to solve this problem for weeks thank you.
HTML:
<html> <head> <title> </title> <script> function createCookie(name,value) { var day = (1 * 24 * 60 * 60 * 1000); var date = new Date(); date.setTime(date.getTime() + (20 * 365 * day)); var expires = "; expires=" + date.toGMTString(); document.cookie = name + '=; expires=Thu,01 Jan 1970 00:00:01 GMT;'; document.cookie = name + "=" + value + expires + "; path=/"; } function readCookie(name) { var nameEQ = name + "="; var ca = document.cookie.split(';'); for(var i=0;i < ca.length;i++) { var c = ca[i]; while (c.charAt(0)==' ') c = c.substring(1,c.length); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); } return null; } </script> </head> <body> <h1 class=""> <script type="text/javascript"> createCookie("test","If this is working,it returns this string. If this is not working,it returns null."); document.write("test: " + readCookie("test")); </script> </body> </html>
Java code:
The public class mainactivity extends sherlockactivity to implement isidenavigationcallback{
public static final String EXTRA_TITLE = "com.devspark.sidenavigation.sample.extra.MTGOBJECT"; public static final String EXTRA_RESOURCE_ID = "com.devspark.sidenavigation.sample.extra.RESOURCE_ID"; public static final String EXTRA_MODE = "com.devspark.sidenavigation.sample.extra.MODE"; public static String WebLoaded = "0"; public static String page = "signup.PHP"; private ImageView icon; private SideNavigationView sideNavigationView; private WebView engine; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); icon = (ImageView) findViewById(android.R.id.icon); sideNavigationView = (SideNavigationView) findViewById(R.id.side_navigation_view); sideNavigationView.setMenuItems(R.menu.side_navigation_menu); sideNavigationView.setMenuClickCallback(this); if (getIntent().hasExtra(EXTRA_TITLE)) { String title = getIntent().getStringExtra(EXTRA_TITLE); int resId = getIntent().getIntExtra(EXTRA_RESOURCE_ID,0); setTitle(title); icon.setImageResource(resId); sideNavigationView.setMode(getIntent().getIntExtra(EXTRA_MODE,0) == 0 ? Mode.LEFT : Mode.RIGHT); } //test getSupportActionBar().setDisplayHomeAsUpEnabled(true); String domain = "localhost"; CookieManager cookieManager = CookieManager.getInstance(); cookieManager.setAcceptCookie(true); cookieManager.setCookie(domain,"name=value"); cookieManager.setCookie(domain,"path=/"); cookieManager.setCookie(domain,"HttpOnly"); //enable cookies CookieManager.getInstance().setAcceptCookie(true); //navigates web engine,including on nav click engine = (WebView) findViewById(R.id.web_engine); engine.loadUrl("file:///android_asset/" + page); //enable JavaScript support - disabled by default for some weird reason engine.getSettings().setJavaScriptEnabled(true); engine.setWebViewClient(new WebViewClient()); //disables text selection engine.setOnLongClickListener(new View.OnLongClickListener() { public boolean onLongClick(View v) { return true; } }); } @Override public void onPause() { super.onPause(); engine.getSettings().setJavaScriptEnabled(false); } @Override public void onResume() { super.onResume(); engine.getSettings().setJavaScriptEnabled(true); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: sideNavigationView.toggleMenu(); break; case R.id.mode_left: item.setChecked(true); sideNavigationView.setMode(Mode.LEFT); break; case R.id.mode_right: item.setChecked(true); sideNavigationView.setMode(Mode.RIGHT); break; default: return super.onOptionsItemSelected(item); } return true; } @Override public void onSideNavigationItemClick(int itemId) { switch (itemId) { case R.id.side_navigation_menu_item1: invokeActivity(getString(R.string.title1),R.drawable.ic_android1); page = "index.html"; break; case R.id.side_navigation_menu_item2: invokeActivity(getString(R.string.title2),R.drawable.ic_android2); page = "test.html"; break; case R.id.side_navigation_menu_item3: invokeActivity(getString(R.string.title3),R.drawable.ic_android3); break; case R.id.side_navigation_menu_item4: invokeActivity(getString(R.string.title4),R.drawable.ic_android4); break; case R.id.side_navigation_menu_item5: invokeActivity(getString(R.string.title5),R.drawable.ic_android5); break; default: return; } finish(); }
Solution
After more than a month of research, I concluded that it is impossible to set an Android cookie larger than 2.3 in WebView, because the WebView file is on localhost (read directly from the assets folder)
I have experienced many alternatives to using cookie storage, including local storage using HTML I was led to believe that the whole problem here is security If I want to create cookies or localstorage on localhost, other applications can access the storage, so it is a major security threat
My final problem is to try to bridge WebView through two-way communication with Java, and there is a place to store data in the process My solution is to use JavaScript interface
The idea is as follows:
>Parse data to run in JavaScript > JavaScript calls special JavaScript functions, such as "SetData (mydataname, mydata)" > java has a function that listens to it, which will accept the parameters set by javscript and return the value to JavaScript. > After parsing the data into Java, Java will store it in a series of files in the asset. > When calling the JavaScript function "GetData (mydataname)", Java will use the same type of method to return data
I find this very useful: Android JavaScript interface documentation, handling JavaScript interface in Android 2.3
The only major "hang up" I found was the fair serious bug in Android 2.3 (fixed at 3.0), which effectively broke the JavaScript interface The second link above describes the best solution I found
This is an example of how to make two-way communication work (it's very confusing, but it works):
Custom web interface class:
public class webViewInterface { //@JavascriptInterface //for SDK 12+ public String showToast(String myText) { Toast.makeText(context,myText,Toast.LENGTH_LONG).show(); return myText; } }
Main courses: protected void oncreate (bundle savedinstancestate) {super.oncreate (savedinstancestate);
setContentView(R.layout.activity_main); engine = (WebView) findViewById(R.id.web_engine); context = this; WebSettings webSettings = engine.getSettings(); webSettings.setJavaScriptEnabled(true); engine.setWebViewClient(new WebViewClient()); engine.loadUrl("file:///android_asset/" + page); boolean javascriptInterfaceBroken = false; try { if (Build.VERSION.RELEASE.startsWith("2.3")) { javascriptInterfaceBroken = true; } } catch (Exception e) { // Ignore,and assume user javascript interface is working correctly. } // Add javascript interface only if it's not broken // @TODO: write the workaround for < 2.3 devices if (!javascriptInterfaceBroken) { engine.addJavascriptInterface(new webViewInterface(),"MainActivityInterface"); } //disables text selection engine.setOnLongClickListener(new View.OnLongClickListener() { public boolean onLongClick(View v) { return true; } }); }
HTML:
<html> <head> </head> <body> <input type="button" value="Say hello" onClick="showAndroidToast();" /> <script type="text/javascript"> function showAndroidToast() { document.write(MainActivityInterface.showToast("eureka!")); } </script> </body> </html>