Instantly share code, notes, and snippets.
Last active
August 29, 2015 14:07
-
Star
1
(1)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save saiaspire/d02e1c9dc332db7b5305 to your computer and use it in GitHub Desktop.
A persistent Cookie Store for Android which implements the java.net.CookieStore interface and can be used with frameworks like Retrofit & Volley. Cookies are stored and will persist on the user's device between application sessions since they are serialized and stored in SharedPreferences.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.content.Context; | |
import android.content.SharedPreferences; | |
import android.text.TextUtils; | |
import android.util.Log; | |
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.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; | |
/** | |
* A persistent cookie store for Android which implements the {@link CookieStore} interface. | |
* Cookies are stored and will persist on the user's device between application sessions since | |
* they are serialized and stored in {@link SharedPreferences}. | |
*/ | |
public class PersistentHttpCookieStore implements CookieStore { | |
private static final String LOG_TAG = "PersistentHttpCookieStore"; | |
private static final String COOKIE_PREFS = "CookiePrefsFile"; | |
private static final String COOKIE_NAME_PREFIX = "cookie_"; | |
private static final String COOKIE_DOMAIN_PREFIX = "domain_"; | |
private static final String COOKIE_DOMAIN_STORE = "domains"; | |
/** | |
* this map may have null keys! | |
*/ | |
private final Map<URI, List<HttpCookie>> cookiesCache; | |
private final SharedPreferences cookiePrefs; | |
/** | |
* Construct a persistent cookie store. | |
* | |
* @param context Context to attach cookie store to | |
*/ | |
public PersistentHttpCookieStore(Context context) { | |
cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0); | |
cookiesCache = new HashMap<URI, List<HttpCookie>>(); | |
// Load any previously stored domains into the cache | |
String storedCookieDomains = cookiePrefs.getString(COOKIE_DOMAIN_STORE, null); | |
if (storedCookieDomains != null) { | |
// Get all the domains | |
String[] storedCookieDomainsArray = TextUtils.split(storedCookieDomains, ","); | |
for (String domain : storedCookieDomainsArray) { | |
// Get the cookie names under the domain | |
String storedCookiesNames = cookiePrefs.getString(COOKIE_DOMAIN_PREFIX + domain, | |
null); | |
if (storedCookiesNames != null) { | |
String[] storedCookieNamesArray = TextUtils.split(storedCookiesNames, ","); | |
if (storedCookieNamesArray != null) { | |
List<HttpCookie> cookies = new ArrayList<HttpCookie>(); | |
for (String cookieName : storedCookieNamesArray) { | |
// Get the serialized cookies, deserialize it and add it to our cache | |
String encodedCookie = cookiePrefs.getString(COOKIE_NAME_PREFIX + domain | |
+ cookieName, null); | |
if (encodedCookie != null) | |
cookies.add(decodeCookie(encodedCookie)); | |
} | |
cookiesCache.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 = cookiesCache.get(uri); | |
if (cookies == null) { | |
cookies = new ArrayList<HttpCookie>(); | |
cookiesCache.put(uri, cookies); | |
} else { | |
cookies.remove(cookie); | |
} | |
cookies.add(cookie); | |
// Save cookie into persistent store | |
SharedPreferences.Editor prefsWriter = cookiePrefs.edit(); | |
prefsWriter.putString(COOKIE_DOMAIN_STORE, TextUtils.join(",", cookiesCache.keySet())); | |
Set<String> cookieNames = new HashSet<String>(); | |
for (HttpCookie httpCookie : cookies) { | |
cookieNames.add(httpCookie.getName()); | |
prefsWriter.putString(COOKIE_NAME_PREFIX + uri + httpCookie.getName(), | |
encodeCookie(new SerializableHttpCookie(httpCookie))); | |
} | |
prefsWriter.putString(COOKIE_DOMAIN_PREFIX + uri, TextUtils.join(",", cookieNames)); | |
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 = cookiesCache.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 : cookiesCache.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 : cookiesCache.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>(cookiesCache.keySet()); | |
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 (cookiesCache.containsKey(uri) && cookiesCache.get(uri).remove(cookie)) { | |
SharedPreferences.Editor prefsWriter = cookiePrefs.edit(); | |
List<HttpCookie> cookies = cookiesCache.get(uri); | |
Set<String> cookieNames = new HashSet<String>(); | |
for (HttpCookie httpCookie : cookies) { | |
cookieNames.add(httpCookie.getName()); | |
} | |
prefsWriter.putString(COOKIE_DOMAIN_PREFIX + uri, | |
TextUtils.join(",", cookieNames)); | |
prefsWriter.remove(COOKIE_NAME_PREFIX + uri + cookie.getName()); | |
prefsWriter.commit(); | |
return true; | |
} | |
return false; | |
} | |
public synchronized boolean removeAll() { | |
// Clear cookies from persistent store | |
cookiePrefs.edit().clear().commit(); | |
// Clear cookies from cache | |
boolean result = !cookiesCache.isEmpty(); | |
cookiesCache.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(SerializableHttpCookie cookie) { | |
if (cookie == null) | |
return null; | |
ByteArrayOutputStream os = new ByteArrayOutputStream(); | |
try { | |
ObjectOutputStream outputStream = new ObjectOutputStream(os); | |
outputStream.writeObject(cookie); | |
} catch (IOException e) { | |
Log.d(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 = ((SerializableHttpCookie) objectInputStream.readObject()) | |
.getCookie(); | |
} catch (IOException e) { | |
Log.d(LOG_TAG, "IOException in decodeCookie", e); | |
} catch (ClassNotFoundException e) { | |
Log.d(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; | |
} | |
private URI cookiesUri(URI uri) { | |
if (uri == null) { | |
return null; | |
} | |
try { | |
return new URI("http", uri.getHost(), null, null); | |
} catch (URISyntaxException e) { | |
return uri; // probably a URI with no host | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MainActivity extends Activity { | |
PersistentHttpCookieStore cookieStore; | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_main); | |
// Set system wide cookie handler to use the persistent store | |
cookieStore = new PersistentHttpCookieStore(getApplicationContext()); | |
CookieManager cookieManager = new CookieManager(cookieStore, CookiePolicy.ACCEPT_ALL); | |
CookieHandler.setDefault(cookieManager); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.IOException; | |
import java.io.ObjectInputStream; | |
import java.io.ObjectOutputStream; | |
import java.io.Serializable; | |
import java.net.HttpCookie; | |
public class SerializableHttpCookie implements Serializable { | |
private static final long serialVersionUID = -6051428667568260064L; | |
private transient HttpCookie cookie; | |
public SerializableHttpCookie(HttpCookie cookie) { | |
this.cookie = cookie; | |
} | |
public HttpCookie getCookie() { | |
return cookie; | |
} | |
private void writeObject(ObjectOutputStream out) throws IOException { | |
out.writeObject(cookie.getName()); | |
out.writeObject(cookie.getValue()); | |
out.writeObject(cookie.getComment()); | |
out.writeObject(cookie.getCommentURL()); | |
out.writeBoolean(cookie.getDiscard()); | |
out.writeObject(cookie.getDomain()); | |
out.writeLong(cookie.getMaxAge()); | |
out.writeObject(cookie.getPath()); | |
out.writeObject(cookie.getPortlist()); | |
out.writeBoolean(cookie.getSecure()); | |
out.writeInt(cookie.getVersion()); | |
} | |
private void readObject(ObjectInputStream in) throws IOException, | |
ClassNotFoundException { | |
String name = (String) in.readObject(); | |
String value = (String) in.readObject(); | |
cookie = new HttpCookie(name, value); | |
cookie.setComment((String) in.readObject()); | |
cookie.setCommentURL((String) in.readObject()); | |
cookie.setDiscard(in.readBoolean()); | |
cookie.setDomain((String) in.readObject()); | |
cookie.setMaxAge(in.readLong()); | |
cookie.setPath((String) in.readObject()); | |
cookie.setPortlist((String) in.readObject()); | |
cookie.setSecure(in.readBoolean()); | |
cookie.setVersion(in.readInt()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment