kirgy
kirgy

Reputation: 1607

Android webview cookie returns null

I'm trying to simply set and retrieve a cookie inside a webview in android. I have tried numerous cookie manager scripts to try and get this to work. I have JavaScript enabled.

When running the application on a Samsung S3 and a Samsung Galaxy Tab 10.1 cookies don't appear to set at all (android 4.1). However, when running the software on a Samsung Galaxy ace, HTC Desire Z and in the android emulators, the cookies are set and read perfectly fine.

When working, the webview returns the string as expected, when not working, the output is simply "null"; the cookie has not value/is not set.

My specific case also uses sliding Navigation class, which is an extension of Actionbar Sherlock.

I'd really appreciate any help, I've been struggling with this for several weeks now. 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:

public class MainActivity extends SherlockActivity implements 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();
}

Upvotes: 6

Views: 8985

Answers (4)

rhinoeli
rhinoeli

Reputation: 151

I encountered the same limitations. I am loading local html files from the assets fodler. On a 2.2 emulator, cookies are stored and can be retrieved via JavaScript. However, testing on my 4.1 device, cookies set by a local page won't persist.

Based on @kirgy's and @user3220983's suggestions, I made it using the Javascript interface.

webview.addJavascriptInterface(new JavaScriptInterface(this), "Android");

public class JavaScriptInterface {         
    ...

    @JavascriptInterface
    public void setCookie(String name, String value) {
        Editor editor = activity.settings.edit();       
        editor.putString(name, value);
        editor.commit();
    }

    @JavascriptInterface
    public String getCookie(String name) {
        return activity.settings.getString(name, "");
    }   

    @JavascriptInterface
    public void deleteCookie(String name) {
        Editor editor = activity.settings.edit();       
        editor.remove(name);
        editor.commit();
    }

Note: these functions does not provide full cookie functionality (expiration, path), I am posting this as an idea of a good starting point.

Upvotes: 3

BuvinJ
BuvinJ

Reputation: 11046

I encountered the same issue as kirgy. I was trying to use cookies (with javascript) in a Droid webview that displayed local content. It would not store the data. The same code worked perfectly if run from a remote url in the same webview though. Enabling cookies in java (with the Android CookieManger) had no effect.

Fortunately, I used custom javascript functions to encapsulate all my cookie needs in the local web app. Using the webview javascript interface, when running on the droid I just managed my cookies in a virtual manner by handling it up in the java.

For my needs, I only required session storage, so just put the cookies data in a java HashMap. I didn't worry about expiration dates or long term storage. If you need your "virtual cookies" to implement those details, it shouldn't be too difficult to figure out once the data is going to and from java via the js interface.

Upvotes: 1

kirgy
kirgy

Reputation: 1607

After over a month of research I've concluded that setting a cookie in android versions greater than 2.3 is not possible in a webview where the file is on the localhost (read directly from the assets folder).

I went through many alternatives to to using cookie storage, including using HTML's localstorage. The whole issue here, I am led to believe, is security. If I were to create a cookie or localstorage on the localhost, then other apps could access that storage, thus is a major security threat.

My ultimate problem was trying to bridge the webview with two-way communication with java and also having a place to store data during the process. My solution was to take advantage of the JavascriptInterface

The idea is as follows:

  • Parse data to function in javascript
  • Javascript calls a special javascript function e.g. "setData(myDataName, myData)"
  • Java has a function which listens to this, and will take arguments set by javscript, and can also return values to javascript.
  • Once data is parsed to Java, java stores this in a series of files in assets.
  • When javascript function "getData(myDataName)" is called, the data is returned by the same kind of method by Java.

I found this very useful: Android JavascriptInterface Documentation, Handling JavascriptInterface in Android 2.3

The only major 'hang-ups' come with a what I found was a fairly serious bug in Android 2.3 (fixed in 3.0) which effectively broke the JavascriptInterface. The second link above describes the best workaround I have found.

Here is an example of how to get the two-way communication working (it's very messy, but it works):

Custom webinterface class:

public class webViewInterface { 
    //@JavascriptInterface //for SDK 12+
    public String showToast(String myText) {
        Toast.makeText(context, myText, Toast.LENGTH_LONG).show();
        return myText;
    }
    }

Main class: 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>

Upvotes: 4

user2847819
user2847819

Reputation:

You need to set the cookie's expiration date or else the cookie won't be be accepted.

Something like this:

document.setCookie = function(sName,sValue)
{
    var oDate = new Date();
    oDate.setYear(oDate.getFullYear()+1);
    var sCookie = sName.escape() + '=' + escape(sValue) + ';expires=' + oDate.toGMTString() + ';path=/';
    document.cookie= sCookie;
}

Good luck, hope it works!

Upvotes: 1

Related Questions