Instantly share code, notes, and snippets.
manishk3008/Android Persistent Cookies Store
Last active Dec 29, 2020
CookieStore for dealing with persistent cookies in Android
//HOW TO USE | |
SiCookieStore2 siCookieStore = new SiCookieStore2(context); | |
CookieManager cookieManager = new CookieManager((CookieStore) siCookieStore, CookiePolicy.ACCEPT_ALL); | |
CookieHandler.setDefault(cookieManager); |
/** | |
* Taken from https://github.com/loopj/android-async-http | |
*/ | |
package com.orb.net; | |
import java.io.IOException; | |
import java.io.ObjectInputStream; | |
import java.io.ObjectOutputStream; | |
import java.io.Serializable; | |
import java.net.HttpCookie; | |
/** | |
* A simple wrapper for {@link HttpCookie} to work with {@link SiCookieStore2} | |
* Gives power of serialization-deserialization to {@link HttpCookie} | |
* @author Manish | |
* | |
*/ | |
public class SICookie2 implements Serializable { | |
private HttpCookie mHttpCookie; | |
/** | |
* | |
*/ | |
private static final long serialVersionUID = 2532101328282342578L; | |
/** | |
* | |
*/ | |
public SICookie2(HttpCookie cookie) { | |
this.mHttpCookie = cookie; | |
} | |
public HttpCookie getCookie() { | |
return mHttpCookie; | |
} | |
private void writeObject(ObjectOutputStream out) throws IOException { | |
out.writeObject(mHttpCookie.getName()); | |
out.writeObject(mHttpCookie.getValue()); | |
out.writeObject(mHttpCookie.getComment()); | |
out.writeObject(mHttpCookie.getCommentURL()); | |
out.writeBoolean(mHttpCookie.getDiscard()); | |
out.writeObject(mHttpCookie.getDomain()); | |
out.writeLong(mHttpCookie.getMaxAge()); | |
out.writeObject(mHttpCookie.getPath()); | |
out.writeObject(mHttpCookie.getPortlist()); | |
out.writeBoolean(mHttpCookie.getSecure()); | |
out.writeInt(mHttpCookie.getVersion()); | |
} | |
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { | |
String name = (String) in.readObject(); | |
String value = (String) in.readObject(); | |
mHttpCookie = new HttpCookie(name, value); | |
mHttpCookie.setComment((String) in.readObject()); | |
mHttpCookie.setCommentURL((String) in.readObject()); | |
mHttpCookie.setDiscard(in.readBoolean()); | |
mHttpCookie.setDomain((String) in.readObject()); | |
mHttpCookie.setMaxAge(in.readLong()); | |
mHttpCookie.setPath((String) in.readObject()); | |
mHttpCookie.setPortlist((String) in.readObject()); | |
mHttpCookie.setSecure(in.readBoolean()); | |
mHttpCookie.setVersion(in.readInt()); | |
} | |
} |
/** | |
* Inspired from https://github.com/loopj/android-async-http | |
*/ | |
package com.orb.net; | |
import java.io.ByteArrayInputStream; | |
import java.io.ByteArrayOutputStream; | |
import java.io.IOException; | |
import java.io.ObjectInputStream; | |
import java.io.ObjectOutputStream; | |
import java.net.CookieStore; | |
import java.net.HttpCookie; | |
import java.net.URI; | |
import java.net.URISyntaxException; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.Collections; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Locale; | |
import java.util.Map; | |
import java.util.Set; | |
import android.annotation.SuppressLint; | |
import android.content.Context; | |
import android.content.SharedPreferences; | |
import android.text.TextUtils; | |
import com.orb.common.Log; | |
/** | |
* Implementation of {@link CookieStore} for persistence cookies, uses shared | |
* preference for storing cookies. | |
* | |
* @author Manish | |
* | |
*/ | |
public class SiCookieStore2 implements CookieStore { | |
private static final String LOG_TAG = "SICookieStore2"; | |
private static final String COOKIE_PREFS = "com.orb.net.cookieprefs"; | |
private static final String COOKIE_DOMAINS_STORE = "com.orb.net.CookieStore.domain"; | |
private static final String COOKIE_DOMAIN_PREFIX = "com.orb.net.CookieStore.domain_"; | |
private static final String COOKIE_NAME_PREFIX = "com.orb.net.CookieStore.cookie_"; | |
/*This map here will store all domain to cookies bindings*/ | |
private final CookieMap map; | |
private final SharedPreferences cookiePrefs; | |
/** | |
* Construct a persistent cookie store. | |
* | |
* @param context | |
* Context to attach cookie store to | |
*/ | |
public SiCookieStore2(Context context) { | |
cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0); | |
map = new CookieMap(); | |
// Load any previously stored domains into the store | |
String storedCookieDomains = cookiePrefs.getString(COOKIE_DOMAINS_STORE, null); | |
if (storedCookieDomains != null) { | |
String[] storedCookieDomainsArray = TextUtils.split(storedCookieDomains, ","); | |
//split this domains and get cookie names stored for each domain | |
for (String domain : storedCookieDomainsArray) { | |
String storedCookiesNames = cookiePrefs.getString(COOKIE_DOMAIN_PREFIX + domain, | |
null); | |
//so now we have these cookie names | |
if (storedCookiesNames != null) { | |
//split these cookie names and get serialized cookie stored | |
String[] storedCookieNamesArray = TextUtils.split(storedCookiesNames, ","); | |
if (storedCookieNamesArray != null) { | |
//in this list we store all cookies under one URI | |
List<HttpCookie> cookies = new ArrayList<HttpCookie>(); | |
for (String cookieName : storedCookieNamesArray) { | |
String encCookie = cookiePrefs.getString(COOKIE_NAME_PREFIX + domain | |
+ cookieName, null); | |
//now we deserialize or unserialize (whatever you call it) this cookie | |
//and get HttpCookie out of it and pass it to List | |
if (encCookie != null) | |
cookies.add(decodeCookie(encCookie)); | |
} | |
map.put(URI.create(domain), cookies); | |
} | |
} | |
} | |
} | |
} | |
public synchronized void add(URI uri, HttpCookie cookie) { | |
if (cookie == null) { | |
throw new NullPointerException("cookie == null"); | |
} | |
uri = cookiesUri(uri); | |
List<HttpCookie> cookies = map.get(uri); | |
if (cookies == null) { | |
cookies = new ArrayList<HttpCookie>(); | |
map.put(uri, cookies); | |
} else { | |
cookies.remove(cookie); | |
} | |
cookies.add(cookie); | |
// Save cookie into persistent store | |
SharedPreferences.Editor prefsWriter = cookiePrefs.edit(); | |
prefsWriter.putString(COOKIE_DOMAINS_STORE, TextUtils.join(",", map.keySet())); | |
Set<String> names = new HashSet<String>(); | |
for (HttpCookie cookie2 : cookies) { | |
names.add(cookie2.getName()); | |
prefsWriter.putString(COOKIE_NAME_PREFIX + uri + cookie2.getName(), | |
encodeCookie(new SICookie2(cookie2))); | |
} | |
prefsWriter.putString(COOKIE_DOMAIN_PREFIX + uri, TextUtils.join(",", names)); | |
prefsWriter.commit(); | |
} | |
public synchronized List<HttpCookie> get(URI uri) { | |
if (uri == null) { | |
throw new NullPointerException("uri == null"); | |
} | |
List<HttpCookie> result = new ArrayList<HttpCookie>(); | |
// get cookies associated with given URI. If none, returns an empty list | |
List<HttpCookie> cookiesForUri = map.get(uri); | |
if (cookiesForUri != null) { | |
for (Iterator<HttpCookie> i = cookiesForUri.iterator(); i.hasNext();) { | |
HttpCookie cookie = i.next(); | |
if (cookie.hasExpired()) { | |
i.remove(); // remove expired cookies | |
} else { | |
result.add(cookie); | |
} | |
} | |
} | |
// get all cookies that domain matches the URI | |
for (Map.Entry<URI, List<HttpCookie>> entry : map.entrySet()) { | |
if (uri.equals(entry.getKey())) { | |
continue; // skip the given URI; we've already handled it | |
} | |
List<HttpCookie> entryCookies = entry.getValue(); | |
for (Iterator<HttpCookie> i = entryCookies.iterator(); i.hasNext();) { | |
HttpCookie cookie = i.next(); | |
if (!HttpCookie.domainMatches(cookie.getDomain(), uri.getHost())) { | |
continue; | |
} | |
if (cookie.hasExpired()) { | |
i.remove(); // remove expired cookies | |
} else if (!result.contains(cookie)) { | |
result.add(cookie); | |
} | |
} | |
} | |
return Collections.unmodifiableList(result); | |
} | |
public synchronized List<HttpCookie> getCookies() { | |
List<HttpCookie> result = new ArrayList<HttpCookie>(); | |
for (List<HttpCookie> list : map.values()) { | |
for (Iterator<HttpCookie> i = list.iterator(); i.hasNext();) { | |
HttpCookie cookie = i.next(); | |
if (cookie.hasExpired()) { | |
i.remove(); // remove expired cookies | |
} else if (!result.contains(cookie)) { | |
result.add(cookie); | |
} | |
} | |
} | |
return Collections.unmodifiableList(result); | |
} | |
public synchronized List<URI> getURIs() { | |
List<URI> result = new ArrayList<URI>(map.getAllURIs()); | |
result.remove(null); // sigh | |
return Collections.unmodifiableList(result); | |
} | |
public synchronized boolean remove(URI uri, HttpCookie cookie) { | |
if (cookie == null) { | |
throw new NullPointerException("cookie == null"); | |
} | |
if (map.removeCookie(uri, cookie)) { | |
SharedPreferences.Editor prefsWriter = cookiePrefs.edit(); | |
prefsWriter.putString(COOKIE_DOMAIN_PREFIX + uri, | |
TextUtils.join(",", map.getAllCookieNames(uri))); | |
prefsWriter.remove(COOKIE_NAME_PREFIX + uri + cookie.getName()); | |
prefsWriter.apply(); | |
return true; | |
} | |
return false; | |
} | |
public synchronized boolean removeAll() { | |
// Clear cookies from persistent store | |
SharedPreferences.Editor prefsWriter = cookiePrefs.edit(); | |
prefsWriter.clear(); | |
prefsWriter.commit(); | |
// Clear cookies from local store | |
boolean result = !map.isEmpty(); | |
map.clear(); | |
return result; | |
} | |
/** | |
* Serializes HttpCookie object into String | |
* | |
* @param cookie | |
* cookie to be encoded, can be null | |
* @return cookie encoded as String | |
*/ | |
protected String encodeCookie(SICookie2 cookie) { | |
if (cookie == null) | |
return null; | |
ByteArrayOutputStream os = new ByteArrayOutputStream(); | |
try { | |
ObjectOutputStream outputStream = new ObjectOutputStream(os); | |
outputStream.writeObject(cookie); | |
} catch (IOException e) { | |
Log.e(LOG_TAG, "IOException in encodeCookie", e); | |
return null; | |
} | |
return byteArrayToHexString(os.toByteArray()); | |
} | |
/** | |
* Returns HttpCookie decoded from cookie string | |
* | |
* @param cookieString | |
* string of cookie as returned from http request | |
* @return decoded cookie or null if exception occured | |
*/ | |
protected HttpCookie decodeCookie(String cookieString) { | |
byte[] bytes = hexStringToByteArray(cookieString); | |
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); | |
HttpCookie cookie = null; | |
try { | |
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); | |
cookie = ((SICookie2) objectInputStream.readObject()).getCookie(); | |
} catch (IOException e) { | |
Log.e(LOG_TAG, "IOException in decodeCookie", e); | |
} catch (ClassNotFoundException e) { | |
Log.e(LOG_TAG, "ClassNotFoundException in decodeCookie", e); | |
} | |
return cookie; | |
} | |
/** | |
* Using some super basic byte array <-> hex conversions so we don't | |
* have to rely on any large Base64 libraries. Can be overridden if you | |
* like! | |
* | |
* @param bytes | |
* byte array to be converted | |
* @return string containing hex values | |
*/ | |
protected String byteArrayToHexString(byte[] bytes) { | |
StringBuilder sb = new StringBuilder(bytes.length * 2); | |
for (byte element : bytes) { | |
int v = element & 0xff; | |
if (v < 16) { | |
sb.append('0'); | |
} | |
sb.append(Integer.toHexString(v)); | |
} | |
return sb.toString().toUpperCase(Locale.US); | |
} | |
/** | |
* Converts hex values from strings to byte arra | |
* | |
* @param hexString | |
* string of hex-encoded values | |
* @return decoded byte array | |
*/ | |
protected byte[] hexStringToByteArray(String hexString) { | |
int len = hexString.length(); | |
byte[] data = new byte[len / 2]; | |
for (int i = 0; i < len; i += 2) { | |
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character | |
.digit(hexString.charAt(i + 1), 16)); | |
} | |
return data; | |
} | |
/** | |
* Utility function to male sure that every time you get consistent URI | |
* @param uri | |
* @return | |
*/ | |
private URI cookiesUri(URI uri) { | |
if (uri == null) { | |
return null; | |
} | |
try { | |
return new URI(uri.getScheme(), uri.getHost(), null, null); | |
} catch (URISyntaxException e) { | |
return uri; | |
} | |
} | |
/** | |
* A implementation of {@link Map} for utility class for storing URL cookie map | |
* @author Manish | |
* | |
*/ | |
private class CookieMap implements Map<URI, List<HttpCookie>> { | |
private final Map<URI, List<HttpCookie>> map; | |
/** | |
* | |
*/ | |
public CookieMap() { | |
map = new HashMap<URI, List<HttpCookie>>(); | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see java.util.Map#clear() | |
*/ | |
@Override | |
public void clear() { | |
map.clear(); | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see java.util.Map#containsKey(java.lang.Object) | |
*/ | |
@Override | |
public boolean containsKey(Object key) { | |
return map.containsKey(key); | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see java.util.Map#containsValue(java.lang.Object) | |
*/ | |
@Override | |
public boolean containsValue(Object value) { | |
return map.containsValue(value); | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see java.util.Map#entrySet() | |
*/ | |
@Override | |
public Set<java.util.Map.Entry<URI, List<HttpCookie>>> entrySet() { | |
return map.entrySet(); | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see java.util.Map#get(java.lang.Object) | |
*/ | |
@Override | |
public List<HttpCookie> get(Object key) { | |
return map.get(key); | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see java.util.Map#isEmpty() | |
*/ | |
@Override | |
public boolean isEmpty() { | |
return map.isEmpty(); | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see java.util.Map#keySet() | |
*/ | |
@Override | |
public Set<URI> keySet() { | |
return map.keySet(); | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see java.util.Map#put(java.lang.Object, java.lang.Object) | |
*/ | |
@Override | |
public List<HttpCookie> put(URI key, List<HttpCookie> value) { | |
return map.put(key, value); | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see java.util.Map#putAll(java.util.Map) | |
*/ | |
@Override | |
public void putAll(Map<? extends URI, ? extends List<HttpCookie>> map) { | |
this.map.putAll(map); | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see java.util.Map#remove(java.lang.Object) | |
*/ | |
@Override | |
public List<HttpCookie> remove(Object key) { | |
return map.remove(key); | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see java.util.Map#size() | |
*/ | |
@Override | |
public int size() { | |
return map.size(); | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see java.util.Map#values() | |
*/ | |
@Override | |
public Collection<List<HttpCookie>> values() { | |
return map.values(); | |
} | |
/** | |
* List all URIs for which cookies are stored in map | |
* @return | |
*/ | |
public Collection<URI> getAllURIs() { | |
return map.keySet(); | |
} | |
/** | |
* Get all cookies names stored for given URI | |
* @param uri | |
* @return | |
*/ | |
public Collection<String> getAllCookieNames(URI uri) { | |
List<HttpCookie> cookies = map.get(uri); | |
Set<String> cookieNames = new HashSet<String>(); | |
for (HttpCookie cookie : cookies) { | |
cookieNames.add(cookie.getName()); | |
} | |
return cookieNames; | |
} | |
/** | |
* Removes requested {@link HttpCookie} {@code httpCookie} from given {@code uri} value | |
* @param uri | |
* @param httpCookie | |
* @return | |
*/ | |
public boolean removeCookie(URI uri, HttpCookie httpCookie) { | |
if (map.containsKey(uri)) { | |
return map.get(uri).remove(httpCookie); | |
} else { | |
return false; | |
} | |
} | |
} | |
} |
This comment has been minimized.
This comment has been minimized.
Helped me very much. Thanks |
This comment has been minimized.
This comment has been minimized.
Thanks |
This comment has been minimized.
This comment has been minimized.
thanks for this |
This comment has been minimized.
This comment has been minimized.
tested and worked. thanks! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
better fix the typo in the title :)