package com.geel9.facepunch;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.StringBufferInputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.select.Elements;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;
import com.crittercism.app.Crittercism;
import com.geel9.facepunch.R;
import com.geel9.facepunch.activities.FPActivity;
import com.geel9.facepunch.misc.FPPostSection;
public class APISession
{
// State
public String username;
protected String hashedPassword;
public int bb_userid = -1;
public boolean loggedIn = false;
public String sessionID = "0", lastPost, lastThread, postHash, postStartTime;
public boolean loggedIn() { return loggedIn; }
public int userId() { return bb_userid; }
public String username() { return username; }
public String password(){ return hashedPassword; }
public enum ErrorResult{
NOERROR, NOAUTH, WRONGACTION
}
/**
* The possible return code for logging in
*
* @author Geel9
*/
public enum LoginResult{
SUCCESS, FAILED_WRONGPASSWORD, FAILED_FAILLIMIT, FAILED_NOSOURCE, FAILED_BADMD5
}
/**
* The user ranks on Facepunch.
*
* @author Overv
*/
public enum Rank {
BANNED, REGULAR, GOLD, MODERATOR
}
/**
* The available post ratings.
*
* @author Overv
*/
public enum Rating
{
AGREE( "Agree", 1, 1 ), DISAGREE( "Disagree", 2, 2 ), FUNNY( "Funny", 3, 3 ), WINNER( "Winner", 18, 4 ), ZING( "Zing", 13, 5 ), INFORMATIVE( "Informative", 4, 6 ), FRIENDLY( "Friendly", 5, 7 ),
USEFUL( "Useful", 6, 8 ), OPTIMISTIC( "Optimistic", 7, 9 ), ARTISTIC( "Artistic", 8, 10 ), LATE( "Late", 9, 11 ), DUMB( "Dumb", 12, 12 ), PROGRAMMINGKING( "Programming King", 14, 8 ),
MAPPINGKING( "Mapping King", 17, 8 ), LUAKING( "Lua King", 16, 8 ), LUAHELPER( "Lua Helper", 19, 8 ), SMARKED( "Smarked", 15, 8 ), MOUSTACHE( "Moustache", 21, 8 );
String ratingName;
int ratingId;
int ratingOrder;
Rating( String name, int id, int order)
{
ratingName = name;
ratingId = id;
ratingOrder = order;
}
@Override
public String toString()
{
return ratingName;
}
public int ID(){
return ratingId;
}
public int Order(){
return ratingOrder;
}
public static Rating fromString( String htmlName )
{
for( Rating r : values() )
if( r.name().equalsIgnoreCase( htmlName ) )
return r;
return null;
}
public static LinkedHashMap<Rating, String> SortRatings(LinkedHashMap<Rating, String> ratings){
LinkedHashMap<Rating, String> ret = new LinkedHashMap<Rating, String>();
List<Rating> keys = new ArrayList(ratings.keySet());
Collections.sort(keys, new Comparator<Rating>() {
public int compare(Rating r1, Rating r2) {
return r1.Order() - r2.Order();
}
});
for(Rating r : keys){
ret.put(r, ratings.get(r));
}
return ret;
}
}
public class RatingPair implements Comparable{
public Rating rating;
public int numRatings;
public RatingPair() {}
public int compareTo(Object arg0) {
RatingPair rp = (RatingPair)arg0;
return rp.numRatings - numRatings;
}
}
/**
* Class that holds information about a specific user on Facepunch.
* The actual amount of available information depends on the function that returned the structure.
*
* @author Overv
*/
public class FPUser
{
private FPUser() {}
private int id;
private String name;
private String name_html;
public String userTitle = "";
public Rank rank = Rank.REGULAR;
private boolean online;
private int joinYear;
private String joinMonth;
private int postCount;
public int getId() { return id; }
public String getName() { return name; }
public Rank getRank() { return rank; }
public String getStyledName() {
return name_html;
}
public boolean isOnline() { return online; }
public int getJoinYear() { return joinYear; }
public String getJoinMonth() { return joinMonth; }
public int getPostCount() { return postCount; }
}
/**
* Class that holds information about a specific thread on Facepunch.
*
* @author Overv
*/
public class FPThread
{
private FPThread() {}
private int id;
private String icon, title, lastAuthor, lastAuthorTime;
private FPUser author;
private boolean read, locked, sticky;
private int readers;
private int pages;
private int posts, views, newPosts;
public int getId() { return id; }
public String getIcon() { return icon; }
public String getTitle() { return title; }
public String getLastAuthor(){ return lastAuthor; }
public String getLastAuthorTime(){ return lastAuthorTime; }
public FPUser getAuthor() { return author; }
public boolean isRead() { return read; }
public boolean isLocked() { return locked; }
public boolean isSticky() { return sticky; }
public int readerCount() { return readers; }
public int pageCount() { return pages; }
public int postCount() { return posts; }
public int viewCount() { return views; }
public int unreadPostCount() { return newPosts; }
}
/**
* Class used to store PMs.
*
* @author Geel9
*/
public class FPPM{
private FPPM(){}
private int id;
private String title, sender, senderTime, senderDate, icon;
private boolean read;
public int getId(){ return id; }
public String getTitle(){ return title; }
public String getAuthor(){ return sender; }
public String getDate(){ return senderDate; }
public String getTime(){ return senderTime; }
public String getIcon(){ return icon; }
public boolean getRead(){ return read; }
}
/**
* Class that holds information about a specific post on Facepunch.
*
* @author Overv
*/
public class FPPost
{
private FPPost() {}
private int id;
private String date;
private String title;
private FPUser author;
private boolean isNew = false;
private String message;
public ArrayList<FPPostSection> sections = new ArrayList<FPPostSection>();
private String os, browser, flagdog, BBCode;
public HashMap<Rating, Integer> ratings = new HashMap<Rating, Integer>();
public LinkedHashMap<Rating, String> ratingCodes = new LinkedHashMap<Rating, String>();
public int getId() { return id; }
public String getDate() { return date; }
public FPUser getAuthor() { return author; }
public String getMessageHTML() { return message; }
public String getTitle(){ return title; }
public String getBBCode() { return BBCode; }
public String getOperatingSystem() { return os; }
public String getBrowser() { return browser; }
public boolean getNew() { return isNew; }
public int getRatingCount( Rating rating ) { return ratings.containsKey( rating ) ? ratings.get( rating ) : 0; }
public void loadRatingKeys(JSONObject ratingKeys){
for(int i = 0; i < Rating.values().length; i++){
Rating rating = Rating.values()[i];
String ratingName = rating.toString().toLowerCase().replace(" ","_");
String key = ratingKeys.optString(ratingName);
if(!key.equals("")){
ratingCodes.put(rating, key);
}
ratingCodes = Rating.SortRatings(ratingCodes);
}
}
public void loadRatingNums(JSONObject ratings){
for(int i = 0; i < Rating.values().length; i++){
Rating rating = Rating.values()[i];
String ratingName = rating.toString().toLowerCase().replace(" ","_");
int ratingNum = ratings.optInt(ratingName);
if(ratingNum != 0){
this.ratings.put(rating, ratingNum);
}
}
}
}
/**
* Classes for managing frontpage forums
*
* @author Overv
*/
public class Forum
{
public Forum() {}
private int id;
private String name;
public int getId() { return id; }
public String getName() { return name; }
private ArrayList<Forum> subforums = new ArrayList<Forum>();
public Forum[] getSubForums(){
Forum[] arr = new Forum[subforums.size()];
subforums.toArray( arr );
return arr;
}
public String[] getSubForumsString(){
ArrayList<String> ret = new ArrayList<String>();
for(Forum f : subforums){
ret.add(String.valueOf(f.id) + "\n" + f.name);
}
String[] ret2 = new String[ret.size()];
ret.toArray(ret2);
return ret2;
}
public ArrayList<Forum> loadSubForums(String[] subs){
ArrayList<Forum> subforums2 = new ArrayList<Forum>();
for(String forum : subs){
String[] contents = forum.split("\n");
Forum f = new Forum();
f.id = Integer.parseInt(contents[0]);
f.name = contents[1];
subforums2.add(f);
}
return subforums2;
}
}
public class Category
{
private Category() {}
private int id;
private String name;
private ArrayList<Forum> forums = new ArrayList<Forum>();
public int getId() { return id; }
public String getName() { return name; }
public Forum[] getForums()
{
Forum[] arr = new Forum[forums.size()];
forums.toArray( arr );
return arr;
}
}
public ErrorResult checkErrors(JSONObject error){
if(error == null) return ErrorResult.WRONGACTION;
String errorCode = error.optString("error");
if(errorCode == null) return ErrorResult.NOERROR;
else if(errorCode.contains("No session for that")) return ErrorResult.NOAUTH;
else if(errorCode.contains("Invalid rating type")) return ErrorResult.WRONGACTION;
return ErrorResult.NOERROR;
}
// Authentication
public interface LoginCallback
{
public void onResult( LoginResult returnCode );
}
/**
* Sign in as the specified user within this session.
*
* @author Geel9
*
* @param username Username to sign in with.
* @param password Associated passsword.
* @param callback Function to pass the result to.
*/
public void login( String username, String password, final LoginCallback callback )
{
final String pwdHash = MD5( password );
if(pwdHash == null){
callback.onResult(LoginResult.FAILED_BADMD5);
return;
}
this.username = username;
asyncAPIRequest( "action=authenticate&username=" + username + "&password=" + pwdHash, "", true, new WebRequestCallback()
{
public void onResult( String ret, JSONObject json )
{
if(json == null)
{
callback.onResult(LoginResult.FAILED_NOSOURCE);
return;
}
//Will equal nologin if there is no login field, which only occurs when there is an error.
String login = json.optString("login", "nologin");
if(login.equals("nologin")){
//Generic catch-all for right now.
//Replace with all possible responses later.
String error = json.optString("error");
callback.onResult(LoginResult.FAILED_WRONGPASSWORD);
return;
}
bb_userid = json.optInt("userid");
hashedPassword = pwdHash;
sessionID = json.optString("session_id");
loggedIn = true;
callback.onResult(LoginResult.SUCCESS);
}
} );
}
// Logging out
public interface LogoutCallback
{
public void onResult( boolean success );
}
/**
* Sign out as the currently logged in user.
*
* @author Overv
*
* @param callback Function to pass the result to.
*/
public void logout( final LogoutCallback callback )
{
loggedIn = false;
username = "";
hashedPassword = "";
sessionID = "0";
callback.onResult(true);
}
public interface GetPMCallback
{
public void onResult(boolean success, FPPM[] pms);
}
/**
* Retrieve the PMs of a user
*
* @author Geel9
*/
public void listPMs(final FPActivity caller, int page, final GetPMCallback callback){
}
public interface ReplyPMCallback{
public void onResult(boolean success);
}
public void ReplyPM(int id, String title, String recipients, String message, final ReplyPMCallback callback){
}
public interface GetSinglePMCallback{
public void onResult(boolean success, FPPost PM);
}
public void GetPM(final FPActivity caller, int id, final GetSinglePMCallback callback){
}
// Forum listing
public interface ForumCallback
{
public void onResult( boolean success, Category[] categories );
}
/**
* Retrieve the categories and forums visible on the frontpage.
*
* @author Overv
*
* @param callback Function to pass the thread list to.
*/
public void listForums(final FPActivity caller, final ForumCallback callback )
{
asyncAPIRequest("action=getforums&session_id=" + sessionID, "", caller.loadNitro(), new WebRequestCallback(){
public void onResult(String ret, JSONObject json) {
ErrorResult error = checkErrors(json);
if(error != ErrorResult.NOERROR){
callback.onResult(false, null);
return;
}
JSONArray jCategories = json.optJSONArray("categories");
Category[] categories = new Category[jCategories.length()];
for(int i = 0; i < jCategories.length(); i++){
JSONObject jCategory;
try{
jCategory = (JSONObject) jCategories.getJSONObject(i);
}
catch(JSONException e){
callback.onResult(false, null);
return;
}
Category category = new Category();
category.name = jCategory.optString("name");
category.id = 0;
category.forums = new ArrayList<Forum>();
JSONArray forums = jCategory.optJSONArray("forums");
for(int k = 0; k < forums.length(); k++){
JSONObject jForum;
try{
jForum = (JSONObject) forums.getJSONObject(k);
}
catch(JSONException e){
continue;
}
Forum forum = new Forum();
forum.id = jForum.optInt("id");
forum.name = jForum.optString("name");
forum.subforums = new ArrayList<Forum>();
JSONArray jSubForums = jForum.optJSONArray("forums");
if(jSubForums != null){
for(int j = 0; j < jSubForums.length(); j++){
JSONObject jSubForum;
try{
jSubForum = (JSONObject) jSubForums.getJSONObject(j);
}
catch(JSONException e){
continue;
}
Forum subForum = new Forum();
subForum.id = jSubForum.optInt("id");
subForum.name = jSubForum.optString("name");
forum.subforums.add(subForum);
}
}
category.forums.add(forum);
}
categories[i] = category;
}
callback.onResult(true, categories);
}
});
}
// Thread listing
public interface ThreadCallback
{
public void onResult( boolean success, FPThread[] threads );
}
private FPThread[] ExtractThreadsFromJSON(JSONObject json){
JSONArray jThreads = json.optJSONArray("threads");
if(jThreads == null) return new FPThread[0];
FPThread[] threads = new FPThread[jThreads.length()];
for(int i = 0; i < jThreads.length(); i++){
JSONObject jThread;
try{
jThread = jThreads.getJSONObject(i);
}catch(JSONException e){
return null;
}
String status = jThread.optString("status");
FPThread thread = new FPThread();
thread.id = jThread.optInt("id");
thread.title = jThread.optString("title");
thread.icon = jThread.optString("icon");
thread.sticky = status.contains("sticky");
thread.locked = jThread.optBoolean("locked");
thread.pages = jThread.optInt("pages");
thread.readers = jThread.optInt("reading");
thread.read = status.contains("new");
thread.views = jThread.optInt("views");
thread.posts = jThread.optInt("replies");
FPUser author = new FPUser();
author.name = jThread.optString("author");
author.id = jThread.optInt("authorid");
thread.author = author;
thread.lastAuthor = jThread.optString("lastpostauthorname");
thread.lastAuthorTime = jThread.optString("lastposttime");
//Hexxeh does some weird shit with newposts
if(loggedIn)
thread.newPosts = jThread.optInt("newposts", 0);
threads[i] = thread;
}
return threads;
}
/**
* Retrieve read threads with new posts
*
* @author Overv
*
* @param forumId Forum identifier.
* @param page Page of thread list.
* @param callback Function to pass the thread list to.
*/
public void listThreads(final FPActivity caller, int forumId, int page, final ThreadCallback callback )
{
asyncAPIRequest("action=getthreads&forum_id=" + forumId + "&page=" + page + "&session_id=" + sessionID, "", caller.loadNitro(), new WebRequestCallback(){
public void onResult(String ret, JSONObject json) {
ErrorResult error = checkErrors(json);
if(error != ErrorResult.NOERROR){
callback.onResult(false, null);
return;
}
FPThread[] threads = ExtractThreadsFromJSON(json);
callback.onResult(threads != null, threads);
}
});
}
/**
* Retrieve read threads with new posts.
*
* @author Geel9
*
* @param callback Function to pass the thread list to.
*/
public void listReadThreads(final FPActivity caller, final ThreadCallback callback )
{
if(!loggedIn()){
callback.onResult(false, null);
return;
}
asyncAPIRequest("action=getreadthreads&session_id=" + sessionID, "", caller.loadNitro(), new WebRequestCallback(){
public void onResult(String ret, JSONObject json) {
ErrorResult error = checkErrors(json);
if(error != ErrorResult.NOERROR || json == null){
callback.onResult(false, null);
return;
}
FPThread[] threads = ExtractThreadsFromJSON(json);
callback.onResult(threads != null, threads);
}
});
}
/* Retrieve popular threads.
*
* @author Geel9
*
* @param callback Function to pass the thread list to.
*/
public void listPopularThreads(final FPActivity caller, final ThreadCallback callback )
{
asyncAPIRequest("action=getpopularthreads&session_id=" + sessionID, "", caller.loadNitro(), new WebRequestCallback(){
public void onResult(String ret, JSONObject json) {
ErrorResult error = checkErrors(json);
if(error != ErrorResult.NOERROR || json == null){
callback.onResult(false, null);
return;
}
FPThread[] threads = ExtractThreadsFromJSON(json);
callback.onResult(threads != null, threads);
}
});
}
// Post listing
public interface PostCallback
{
public void onResult( boolean success, FPPost[] posts, String threadTitle, int numPages, int curPage );
}
/**
* Retrieve the posts in a thread on the specified page.
*
* @author Geel9
*
* @param threadId Thread identifier.
* @param page Thread page.
* @param callback The function to pass the post list to.
*/
public void listPosts(final FPActivity caller, final int threadId, final int page, final PostCallback callback )
{
String action = "&action=getposts&thread_id=" + threadId + "&page=" + page;
//Simple way of going to "new posts": pass in a page number less than 0.
if(page <= 0){
action = "&action=getnewposts&thread_id=" + threadId;
}
asyncAPIRequest("session_id=" + sessionID + action, "", caller.loadNitro(), new WebRequestCallback(){
public void onResult(String ret, JSONObject json) {
ErrorResult error = checkErrors(json);
if(error != ErrorResult.NOERROR || json == null){
callback.onResult(false, null, "", 0, 0);
return;
}
JSONArray jPosts = json.optJSONArray("posts");
if(jPosts == null){
callback.onResult(false, null, "", 0, 0);
return;
}
FPPost[] posts = new FPPost[jPosts.length()];
String title = json.optString("title");
int pages = json.optInt("numpages");
int curPage = json.optInt("currpage", page);
for(int i = 0; i < jPosts.length(); i++){
JSONObject jPost;
try {
jPost = jPosts.getJSONObject(i);
} catch (JSONException e) {
callback.onResult(false, null, "", 0, 0);
return;
}
int id = jPost.optInt("id");
int userid = jPost.optInt("userid");
String username = jPost.optString("username");
String postdate = jPost.optString("time");
String usertitle = jPost.optString("usertitle");
String status = jPost.optString("status", "old");
String message = jPost.optString("message");
String usergroup = jPost.optString("usergroup");
int postCount = jPost.optInt("postcount");
String username_html = jPost.optString("username_html");
JSONObject ratingKeys = jPost.optJSONObject("ratingkeys");
JSONObject ratings = jPost.optJSONObject("ratings");
if(usergroup.equals("moderator")){
username_html = "<strong><font color=\"#00aa00\">" + username + "</font></strong>";
}
FPPost post = new FPPost();
FPUser user = new FPUser();
user.id = userid;
user.name = username;
user.postCount = postCount;
user.name_html = username_html;
user.userTitle = usertitle;
post.author = user;
post.id = id;
post.date = postdate;
post.message = message;
post.isNew = status.contains("new");
if(ratingKeys != null)
post.loadRatingKeys(ratingKeys);
if(ratings != null)
post.loadRatingNums(ratings);
posts[i] = post;
}
callback.onResult(true, posts, title, pages, curPage);
}
});
}
// Rating
public interface RateCallback
{
public void onResult( boolean success );
}
/**
* Rate a post on the last retrieved thread page.
*
* @author Overv
*
* @param post Post to rate.
* @param rating Rating to rate.
* @param callback The function to pass the result to.
*/
public void rate( FPPost post, Rating rating, final RateCallback callback )
{
if ( !post.ratingCodes.containsKey( rating ) || !loggedIn() )
{
callback.onResult( false );
return;
}
String ratingKey = post.ratingCodes.get(rating);
String ratingName = rating.ratingName.toLowerCase().replace(" ", "_");
asyncAPIRequest( "action=rate&session_id=" + sessionID +"&post_id=" + post.id + "&key=" + ratingKey + "&rating=" + ratingName, "", true, new WebRequestCallback()
{
public void onResult(String ret, JSONObject obj )
{
callback.onResult( checkErrors(obj) == ErrorResult.NOERROR );
}
} );
}
public interface QuoteCallback{
public void onResult(boolean success, String quote);
}
public void quote(FPPost post, final QuoteCallback callback){
if(!loggedIn()){
callback.onResult(false, "");
return;
}
asyncAPIRequest( "action=getquote&session_id=" + sessionID + "&post_id=" + post.id, "", true, new WebRequestCallback()
{
public void onResult(String ret, JSONObject json )
{
try{
ErrorResult error = checkErrors(json);
if(error == ErrorResult.NOAUTH){
callback.onResult(false, "");
return;
}
else if(error == ErrorResult.NOERROR){
String quote = json.optString("quote");
callback.onResult(true, quote);
}
}catch(Exception e){
callback.onResult(false, "");
}
}
} );
}
public interface EditCallback{
public void onResult(boolean success);
}
public void edit(FPPost post, String newMessage, final EditCallback callback){
if(!loggedIn()){
callback.onResult(false);
return;
}
long unixTime = System.currentTimeMillis() / 1000L;
asyncAPIRequest( "action=doedit&session_id=" + sessionID, "post_id=" + post.id + "&message=" + URLEncoder.encode(newMessage), true, new WebRequestCallback(){
public void onResult(String ret, JSONObject json) {
try{
ErrorResult error = checkErrors(json);
if(error == ErrorResult.NOAUTH){
callback.onResult(false);
return;
}
else if(error == ErrorResult.NOERROR){
callback.onResult(true);
}
}catch(Exception e){
callback.onResult(false);
}
}
});
}
public void getEdit(FPPost post, final QuoteCallback callback){
if(!loggedIn()){
callback.onResult(false, "");
return;
}
asyncAPIRequest( "action=getedit&session_id=" + sessionID + "&post_id=" + post.id,"", true, new WebRequestCallback(){
public void onResult(String ret, JSONObject obj) {
try{
ErrorResult error = checkErrors(obj);
if(error == ErrorResult.NOAUTH){
callback.onResult(false, "");
return;
}
else if(error == ErrorResult.NOERROR){
String edit = obj.optString("edit");
callback.onResult(true, edit);
}
}catch(Exception e){
callback.onResult(false, "");
}
}
});
}
private class RateData
{
public int id;
public String code;
}
// Reply to latest retrieved thread
public interface ReplyCallback
{
public void onResult( boolean success );
}
/**
* Reply to the last retrieved thread.
*
* @author Overv
*
* @param message Message to reply with.
* @param callback The function to pass the result to.
*/
public void reply( String message, int threadID, final ReplyCallback callback )
{
if(!loggedIn()){
callback.onResult(false);
return;
}
asyncAPIRequest("action=newreply&session_id=" + sessionID, "thread_id=" + threadID + "&message=" + URLEncoder.encode(message), true, new WebRequestCallback()
{
public void onResult(String ret, JSONObject json)
{
callback.onResult(true);
}
} );
}
// Retrieve avatar
public interface AvatarCallback
{
public void onResult( boolean success, Bitmap avatar );
}
public void getImage( String url, Context context, final AvatarCallback callback )
{
asyncImageRequest( url, new ImageRequestCallback()
{
public void onResult( Bitmap image )
{
if ( image == null )
callback.onResult( false, null );
else
callback.onResult( true, image );
}
} );
//}
}
/**
* Retrieve the avatar image of the specified user.
*
* @author Overv
*
* @param userId User identifier.
* @param callback The function to pass the avatar image to.
*/
public void getAvatar( int userId, Context context, boolean downloadAvatar, final AvatarCallback callback )
{
try{
File cacheDir = context.getCacheDir();
cacheDir.mkdir();
File avatarFile = new File(cacheDir, userId + ".png");
if(avatarFile.exists()){
BitmapFactory bf = new BitmapFactory();
Bitmap b = bf.decodeFile(avatarFile.getAbsolutePath());
if(b != null){
callback.onResult(true, b);
}
}
else if(downloadAvatar){
asyncImageRequest( "http://facepunch.com/image.php?u=" + userId, new ImageRequestCallback()
{
public void onResult( Bitmap image )
{
if ( image == null )
callback.onResult( false, null );
else
callback.onResult( true, image );
}
} );
}
else
callback.onResult(false, null);
}catch(OutOfMemoryError e){
callback.onResult(false, null);
}
}
public void trimCache(Context context, int megabytes){
File cacheDir = context.getCacheDir();
cacheDir.mkdir();
long bytesTotal = 0;
for(File f : cacheDir.listFiles()){
bytesTotal += f.length();
}
float MB = (float)((float)bytesTotal / 1024f) / 1024f;
if(MB >= megabytes){
int numFiles = cacheDir.listFiles().length / 2;
List<File> toDelete = new ArrayList<File>();
for(int i = 0; i < numFiles; i++){
File f = cacheDir.listFiles()[i];
toDelete.add(f);
}
for(File del : toDelete){
del.delete();
}
}
}
public void cacheAvatar(Bitmap b, int userID, Context context){
File cacheDir = context.getCacheDir();
cacheDir.mkdir();
File avatarFile = new File(cacheDir, userID + ".png");
if(!avatarFile.exists()){
try {
FileOutputStream out = new FileOutputStream(avatarFile);
b.compress(Bitmap.CompressFormat.PNG, 90, out);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// Retrieve flag icon
public interface FlagCallback
{
public void onResult( boolean success, Bitmap flag );
}
/**
* Retrieve the flag associated with the specified flagdog code.
*
* @author Overv
*
* @param post The post to retrieve the flag from.
* @param callback The function to pass the flag image to.
*/
public void getFlag( FPPost post, final FlagCallback callback )
{
asyncImageRequest( "http://flagdog.facepunchstudios.com/?ipe=" + post.flagdog, new ImageRequestCallback()
{
public void onResult( Bitmap image )
{
if ( image == null )
callback.onResult( false, null );
else
callback.onResult( true, image );
}
} );
}
// Calculate MD5 hash
static String MD5( String str )
{
//DAMMIT OVERV WHY IS EVERYTHING YOU WROTE BROKEN
MessageDigest md;
try {
md = MessageDigest.getInstance("MD5");
md.update(str.getBytes("UTF-8"));
byte byteData[] = md.digest();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < byteData.length; i++) {
sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
}
String ret = sb.toString();
return ret;
} catch ( NoSuchAlgorithmException e ) { return null; }
catch ( UnsupportedEncodingException e ) { return null; }
}
// Quick single regex matcher
public static String quickMatch( String regex, String target )
{
Matcher m = Pattern.compile( regex, Pattern.DOTALL ).matcher( target );
if ( m.find() )
return m.group( 1 );
else
return "";
}
public static String quickMatch( String regex, String target, int index )
{
Matcher m = Pattern.compile( regex, Pattern.DOTALL ).matcher( target );
if ( m.find() )
return m.group( index );
else
return null;
}
// Retrieve image from URL
private interface ImageRequestCallback
{
public void onResult( Bitmap image );
}
private void asyncImageRequest( String url, ImageRequestCallback callback )
{
new ImageRequest().execute( url, callback );
}
private class ImageRequest extends AsyncTask<Object, Void, Object[]>
{
protected Object[] doInBackground( Object... params )
{
Bitmap img = null;
try
{
URL url = new URL( (String)params[0] );
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setInstanceFollowRedirects( true );
conn.setDoInput( true );
conn.connect();
img = BitmapFactory.decodeStream( conn.getInputStream() );
} catch ( IOException e ){}
catch(OutOfMemoryError e){}
return new Object[]{ img, params[1] };
}
protected void onPostExecute( Object[] params )
{
ImageRequestCallback callback = (ImageRequestCallback)params[1];
if ( params[0] == null )
{
callback.onResult( null );
return;
}
callback.onResult( (Bitmap)params[0] );
}
}
// Perform Facepunch web request
private interface WebRequestCallback
{
public void onResult( String ret, JSONObject json );
}
private void asyncWebRequest( String action, String postBody, WebRequestCallback callback )
{
new WebRequest().execute( action, postBody, callback, false );
}
private void asyncAPIRequest( String action, String postBody, boolean nitro, WebRequestCallback callback)
{
new WebRequest().execute( "http://api.facepun.ch/?nitro=" + nitro + "&" + action, postBody, callback, true );
}
class WebRequest extends AsyncTask<Object, Void, Object[]>
{
protected Object[] doInBackground( Object... params )
{
String request = (String)params[0];
String postBody = (String)params[1];
Boolean api = (Boolean)params[3];
WebRequestCallback callback = (WebRequestCallback)params[2];
Crittercism.leaveBreadcrumb("Loading page: " + request);
StringBuilder response = new StringBuilder();
try
{
URL url = new URL( request );
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setInstanceFollowRedirects( true );
// Send POST body
if ( postBody != null && postBody != "")
{
conn.setDoOutput( true );
OutputStreamWriter out = new OutputStreamWriter( conn.getOutputStream() );
out.write( postBody );
out.close();
}
// Get response text
InputStreamReader reader = new InputStreamReader( conn.getInputStream() );
char[] buffer = new char[10*1024];
int n = 0;
while ( n >= 0 )
{
n = reader.read( buffer, 0, buffer.length );
if ( n > 0 ) response.append( buffer, 0, n );
}
reader.close();
return new Object[] { callback, response.toString(), api};
} catch ( Exception e )
{
return new Object[] { callback };
}
}
protected void onPostExecute( Object[] params )
{
if ( params.length == 1 )
{
( (WebRequestCallback)params[0] ).onResult( null, null );
return;
}
WebRequestCallback callback = (WebRequestCallback)params[0];
String response = (String)params[1];
Boolean isAPI = (Boolean)params[2];
JSONObject json = null;
if(isAPI)
try {
json = new JSONObject(response);
} catch (JSONException e) {
e.printStackTrace();
}
callback.onResult( response, json );
}
}
public interface unReadCallback{
public void onResult(boolean success);
}
public void unReadThread(int id, final unReadCallback callback) {
}
}