Java tutorial
/* * PsnUsParser.java * Copyright (C) 2010-2014 Akop Karapetyan * * This file is part of Spark 360, the online gaming service client. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA. * */ package com.akop.bach.parser; import java.io.IOException; import java.net.URLEncoder; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.CookieStore; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.cookie.BasicClientCookie; import org.apache.http.message.BasicNameValuePair; import org.apache.http.params.HttpParams; import org.json.JSONArray; import org.json.JSONObject; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import com.akop.bach.App; import com.akop.bach.BasicAccount; import com.akop.bach.PSN; import com.akop.bach.PSN.ComparedGameCursor; import com.akop.bach.PSN.ComparedGameInfo; import com.akop.bach.PSN.ComparedTrophyCursor; import com.akop.bach.PSN.ComparedTrophyInfo; import com.akop.bach.PSN.Friends; import com.akop.bach.PSN.GameCatalogItem; import com.akop.bach.PSN.GameCatalogItemDetails; import com.akop.bach.PSN.GameCatalogList; import com.akop.bach.PSN.GamerProfileInfo; import com.akop.bach.PSN.Games; import com.akop.bach.PSN.Profiles; import com.akop.bach.PSN.Trophies; import com.akop.bach.Preferences; import com.akop.bach.PsnAccount; import com.akop.bach.R; import com.akop.bach.util.IgnorantHttpClient; import com.akop.bach.util.rss.RssChannel; import com.akop.bach.util.rss.RssHandler; // NOTE: Vast majority of the US parser is broken, since the EU parser is // sufficient (and equivalent) for all but blog and game catalog information public class PsnUsParser extends PsnParser { protected static final String URL_BLOG = "http://feeds.feedburner.com/PSBlog?format=xml"; private static final DateFormat TROPHY_DATE_FORMAT = new SimpleDateFormat("EEE MMM d HH:mm:ss zzz yyyy"); private static final DateFormat COMPARE_TROPHY_DATE_FORMAT = new SimpleDateFormat("MMM d, yyyy hh:mm:ss a zzz"); protected static final String URL_LOGIN = "https://store.playstation.com/j_acegi_external_security_check?target=/external/login.action"; private static final String URL_RETURN_LOGIN = "http://us.playstation.com/uwps/PSNTicketRetrievalGenericServlet"; private static final String URL_GAMES = "http://us.playstation.com/playstation/psn/profile/%1$s/get_ordered_trophies_data"; private static final String URL_TROPHIES = "http://us.playstation.com/playstation/psn/profile/%1$s/get_ordered_title_details_data"; private static final String URL_COMPARE_TROPHIES = "http://us.playstation.com/playstation/psn/profile/trophies/%1$s/compare/detail?ids=%2$s&ids=%3$s"; private static final String URL_COMPARE_TROPHIES_DETAIL = "http://us.playstation.com/playstation/psn/profile/get_user_trophies_with_profile?title=%1$s&target=%2$s"; private static final String URL_PROFILE_SUMMARY = "http://us.playstation.com/playstation/psn/profile/trophies?id=%1.16f"; private static final String URL_FRIENDS = "http://us.playstation.com/playstation/psn/profile/friends?id=%1.16f"; private static final String URL_GAMER_DETAIL = "http://us.playstation.com/playstation/psn/profiles/%1$s"; private static final String URL_COMPARE_TROPHIES_REFERER = "http://us.playstation.com/playstation/psn/profile/compare?ids=%1$s"; private static final String URL_GAME_CATALOG = "http://us.playstation.com/ps-products/BrowseGames"; private static final Pattern PATTERN_URL = Pattern.compile("(http:[^'\"]+)"); private static final Pattern PATTERN_LOGIN_REDIR_URL = Pattern .compile("parent\\.location\\s*=\\s*'(http:[^']+)'"); private static final Pattern PATTERN_ONLINE_ID = Pattern.compile("<div id=\"id-handle\">\\s*(\\S+)\\s*</div>"); private static final Pattern PATTERN_LEVEL = Pattern.compile("<div id=\"leveltext\">\\s*(\\d+)\\s*</div>"); private static final Pattern PATTERN_PROGRESS = Pattern .compile("<div class=\"progresstext\">\\s*(\\d+)%\\s*</div>"); private static final Pattern PATTERN_AVATAR_CONTAINER = Pattern .compile("<div id=\"id-avatar\">\\s(.*?)\\s*</div>"); private static final Pattern PATTERN_GAMES = Pattern .compile("<div class=\"slot\"[^>]*>(.*?)</div>\\s*</div>\\s*</div>\\s*</div>", Pattern.DOTALL); private static final Pattern PATTERN_GAME_TITLE = Pattern .compile("<span class=\"gameTitleSortField\">([^<]*)</span>"); private static final Pattern PATTERN_GAME_ICON = Pattern.compile("src=\"([^\"]*)\""); private static final Pattern PATTERN_GAME_PROGRESS = Pattern .compile("<span class=\"gameProgressSortField\">\\s*(\\d+)\\s*</span>"); private static final Pattern PATTERN_GAME_TROPHIES = Pattern .compile("<div class=\"trophycontent\">\\s*(\\d+)\\s*</div>"); private static final Pattern PATTERN_GAME_UID = Pattern.compile("<a href=\"(/(?:[^/]+/)+([^\"]+))\">"); private static final Pattern PATTERN_TROPHIES = Pattern.compile( "<div class=\"slot\\s*([^\"]*)\"[^>]*>(.*?)</div>\\s*</div>\\s*</div>\\s*</div>", Pattern.DOTALL); private static final Pattern PATTERN_TROPHY_TITLE = Pattern .compile("<span class=\"trophyTitleSortField\">([^<]*)</span>"); private static final Pattern PATTERN_TROPHY_DESCRIPTION = Pattern .compile("<span class=\"subtext\">([^<]*)</span>"); private static final Pattern PATTERN_TROPHY_ICON = Pattern.compile("src=\"([^\"]*)\""); private static final Pattern PATTERN_TROPHY_EARNED = Pattern .compile("class=\"dateEarnedSortField\"[^>]*>([^<]*)</span>"); private static final Pattern PATTERN_TROPHY_TYPE = Pattern .compile("class=\"trophyTypeSortField\"[^>]*>\\s*([^<\\s]*)\\s*</span>"); private static final Pattern PATTERN_FRIENDS = Pattern.compile("name=\"ids\" value=\"([^\"]+)\""); private static final Pattern PATTERN_TROPHY_COUNT = Pattern .compile("<div class=\"text ([^\"]*)\">(\\d+)[^<]*</div>"); private static final Pattern PATTERN_URL_TROPHIES = Pattern.compile("/TrophyDetailImage\\?([^\"]*)\""); private static final Pattern PATTERN_COMPARE_TROPHIES = Pattern.compile( "<div id=\"T_([^\"]+)\" class=\"slot\\s*([^\"]*)\"[^>]*>(.*?)</div>\\s*</div>\\s*</div>\\s*</div>", Pattern.DOTALL); private static final Pattern PATTERN_COMPARE_TROPHIES_TITLE = Pattern .compile("<span class=\"gameTitleSortField\">([^<]*)</span>"); private static final Pattern PATTERN_COMPARE_TROPHIES_TITLE_ID = Pattern .compile("/get_user_trophies_with_profile\\?title=([^&]+)&"); private static final Pattern PATTERN_GAME_CATALOG_ITEMS = Pattern.compile( "<div class=\"bgame_list clearfix\">(.*?)</table>\\s*</div>", Pattern.DOTALL | Pattern.MULTILINE); private static final Pattern PATTERN_GAME_CATALOG_ESSENTIALS = Pattern .compile("<div class=\"imagebox\">\\s*<a class=\"first\" href=" + "\"[^\"]*\"><img src=\"([^\"]*)\" alt=\"[^\"]*\" title=" + "\"[^\"]*\".*?<h6>\\s*<a href=\"([^\"]*)\" onclick=\"[^\"]*\">" + "([^<]*)</a>\\s*</h6>\\s*<p>(.*?)</p>", Pattern.DOTALL); private static final Pattern PATTERN_GAME_CATALOG_STATS = Pattern.compile("<p>([^:]*):\\s*([^<]*)</p>"); private static final Pattern PATTERN_GAME_CATALOG_DETAILS = Pattern .compile("<meta name=\"([^\"]+)\" content=\"([^\"]+)\"\\s*/>"); private static final Pattern PATTERN_GAME_CATALOG_GAME_XML = Pattern .compile("swf\"></a>\\s*<a href=\"([^\"]+\\.xml)\"></a>"); private static final Pattern PATTERN_GAME_CATALOG_PSV_THUMBS = Pattern .compile("<img onclick=\"screenshotview\\(this\\)\" .*? src=\"([^\"]+)\""); private static final Pattern PATTERN_GAME_CATALOG_PSV_LARGE = Pattern .compile("id=\"ssimagelarge[^\"]+\"><img .*? src=\"([^\"]+)\""); private static final String SCREENSHOT_BASE_URL = "http://webassets.scea.com/pscomauth/groups/public/documents/webasset/"; protected static final int COLUMN_GAME_ID = 0; protected static final int COLUMN_GAME_PROGRESS = 1; protected static final int COLUMN_GAME_BRONZE = 2; protected static final int COLUMN_GAME_SILVER = 3; protected static final int COLUMN_GAME_GOLD = 4; protected static final int COLUMN_GAME_PLATINUM = 5; protected static final String[] GAMES_PROJECTION = new String[] { Games._ID, Games.PROGRESS, Games.UNLOCKED_BRONZE, Games.UNLOCKED_SILVER, Games.UNLOCKED_GOLD, Games.UNLOCKED_PLATINUM, }; protected static final String[] FRIEND_ID_PROJECTION = { Friends._ID }; public PsnUsParser(Context context) { super(context); HttpParams params = mHttpClient.getParams(); //params.setParameter("http.useragent", USER_AGENT); params.setParameter("http.protocol.max-redirects", 0); } @Override protected DefaultHttpClient createHttpClient(Context context) { return new IgnorantHttpClient(); } @Override protected boolean onAuthenticate(BasicAccount account) throws IOException, ParserException { PsnAccount psnAccount = (PsnAccount) account; String password = psnAccount.getPassword(); if (password == null) throw new ParserException(mContext.getString(R.string.decryption_error)); HttpParams params = mHttpClient.getParams(); // Prepare POSTDATA List<NameValuePair> inputs = new ArrayList<NameValuePair>(3); addValue(inputs, "j_username", psnAccount.getEmailAddress()); addValue(inputs, "j_password", password); addValue(inputs, "returnURL", URL_RETURN_LOGIN); // Enable redirection (max 1) params.setParameter("http.protocol.max-redirects", 2); CookieStore store = mHttpClient.getCookieStore(); BasicClientCookie cookie = new BasicClientCookie("APPLICATION_SITE_URL", "http://us.playstation.com/"); cookie.setDomain(".playstation.com"); cookie.setPath("/"); store.addCookie(cookie); // Post authentication data String page = getResponse(URL_LOGIN, inputs); // Disable redirection params.setParameter("http.protocol.max-redirects", 1); // Get redirection URL Matcher m = PATTERN_LOGIN_REDIR_URL.matcher(page); if (!m.find()) { if (App.getConfig().logToConsole()) App.logv("onAuthUS: Redir URL not found"); String outageMessage; if ((outageMessage = getOutageMessage(page)) != null) throw new ParserException(outageMessage); return false; } // 2. Post to redirection URL try { getResponse(m.group(1)); } catch (ClientProtocolException e) { // Ignore redirection error } return true; } @Override protected String getSessionFile(BasicAccount account) { return account.getUuid() + ".us.session"; } @Override protected String preparseResponse(String response) throws IOException { // Sony's stupid method of requesting re-authentication // This needs to be improved; too un-dependable if (response.startsWith("<script") && response.endsWith("</script>")) throw new ClientProtocolException(); return super.preparseResponse(response); } protected ContentValues parseSummaryData(PsnAccount account) throws IOException, ParserException { long started = System.currentTimeMillis(); String url = String.format(URL_PROFILE_SUMMARY, Math.random()); HttpUriRequest request = new HttpGet(url); request.addHeader("Referer", "http://us.playstation.com/mytrophies/index.htm"); request.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); String page = getResponse(request, null); if (App.getConfig().logToConsole()) started = displayTimeTaken("parseSummaryData page fetch", started); ContentValues cv = new ContentValues(10); Matcher m; if (!(m = PATTERN_ONLINE_ID.matcher(page)).find()) throw new ParserException(mContext, R.string.error_online_id_not_detected); cv.put(Profiles.ONLINE_ID, m.group(1)); int level = 0; if ((m = PATTERN_LEVEL.matcher(page)).find()) level = Integer.parseInt(m.group(1)); int progress = 0; if ((m = PATTERN_PROGRESS.matcher(page)).find()) progress = Integer.parseInt(m.group(1)); int trophiesPlat = 0; int trophiesGold = 0; int trophiesSilver = 0; int trophiesBronze = 0; if ((m = PATTERN_URL_TROPHIES.matcher(page)).find()) { String queryString = m.group(1); String[] pairs = queryString.split("&"); for (String pair : pairs) { String[] param = pair.split("="); if (param.length > 1) { if (param[0].equalsIgnoreCase("bronze")) trophiesBronze = Integer.parseInt(param[1]); else if (param[0].equalsIgnoreCase("silver")) trophiesSilver = Integer.parseInt(param[1]); else if (param[0].equalsIgnoreCase("gold")) trophiesGold = Integer.parseInt(param[1]); else if (param[0].equalsIgnoreCase("platinum")) trophiesPlat = Integer.parseInt(param[1]); } } } cv.put(Profiles.LEVEL, level); cv.put(Profiles.PROGRESS, progress); cv.put(Profiles.TROPHIES_PLATINUM, trophiesPlat); cv.put(Profiles.TROPHIES_GOLD, trophiesGold); cv.put(Profiles.TROPHIES_SILVER, trophiesSilver); cv.put(Profiles.TROPHIES_BRONZE, trophiesBronze); String iconUrl = null; if ((m = PATTERN_AVATAR_CONTAINER.matcher(page)).find()) { if ((m = PATTERN_URL.matcher(m.group(1))).find()) iconUrl = m.group(1); } cv.put(Profiles.ICON_URL, iconUrl); if (App.getConfig().logToConsole()) started = displayTimeTaken("parseSummaryData processing", started); return cv; } private void parseAccountSummary(PsnAccount account) throws ParserException, IOException { ContentValues cv = parseSummaryData(account); ContentResolver cr = mContext.getContentResolver(); long accountId = account.getId(); boolean newRecord = true; long started = System.currentTimeMillis(); Cursor c = cr.query(Profiles.CONTENT_URI, new String[] { Profiles._ID }, Profiles.ACCOUNT_ID + "=" + accountId, null, null); if (c != null) { if (c.moveToFirst()) newRecord = false; c.close(); } if (newRecord) { cv.put(Profiles.ACCOUNT_ID, account.getId()); cv.put(Profiles.UUID, account.getUuid()); cr.insert(Profiles.CONTENT_URI, cv); } else { cr.update(Profiles.CONTENT_URI, cv, Profiles.ACCOUNT_ID + "=" + accountId, null); } cr.notifyChange(Profiles.CONTENT_URI, null); if (App.getConfig().logToConsole()) displayTimeTaken("Summary update", started); account.refresh(Preferences.get(mContext)); account.setOnlineId(cv.getAsString(Profiles.ONLINE_ID)); account.setIconUrl(cv.getAsString(Profiles.ICON_URL)); account.setLastSummaryUpdate(System.currentTimeMillis()); account.save(Preferences.get(mContext)); } protected void parseGames(PsnAccount account) throws ParserException, IOException { String page = getResponse(String.format(URL_GAMES, URLEncoder.encode(account.getScreenName(), "UTF-8")), true); ContentResolver cr = mContext.getContentResolver(); boolean changed = false; long updated = System.currentTimeMillis(); Cursor c; String title; String iconUrl; String uid; int progress; int bronze; int silver; int gold; int platinum; String[] queryParams = new String[1]; final long accountId = account.getId(); ContentValues cv; List<ContentValues> newCvs = new ArrayList<ContentValues>(100); long started = System.currentTimeMillis(); Matcher m; Matcher gameMatcher = PATTERN_GAMES.matcher(page); for (int rowNo = 1; gameMatcher.find(); rowNo++) { String group = gameMatcher.group(1); if (!(m = PATTERN_GAME_UID.matcher(group)).find()) continue; uid = m.group(2); progress = 0; if ((m = PATTERN_GAME_PROGRESS.matcher(group)).find()) progress = Integer.parseInt(m.group(1)); bronze = silver = gold = platinum = 0; if ((m = PATTERN_GAME_TROPHIES.matcher(group)).find()) { bronze = Integer.parseInt(m.group(1)); if (m.find()) { silver = Integer.parseInt(m.group(1)); if (m.find()) { gold = Integer.parseInt(m.group(1)); if (m.find()) { platinum = Integer.parseInt(m.group(1)); } } } } // Check to see if we already have a record of this game queryParams[0] = uid; c = cr.query(Games.CONTENT_URI, GAMES_PROJECTION, Games.ACCOUNT_ID + "=" + accountId + " AND " + Games.UID + "=?", queryParams, null); changed = true; try { if (c == null || !c.moveToFirst()) // New game { title = ""; if ((m = PATTERN_GAME_TITLE.matcher(group)).find()) title = htmlDecode(m.group(1)); iconUrl = null; if ((m = PATTERN_GAME_ICON.matcher(group)).find()) iconUrl = getAvatarImage(m.group(1)); cv = new ContentValues(15); cv.put(Games.ACCOUNT_ID, accountId); cv.put(Games.TITLE, title); cv.put(Games.UID, uid); cv.put(Games.ICON_URL, iconUrl); cv.put(Games.PROGRESS, progress); cv.put(Games.SORT_ORDER, rowNo); cv.put(Games.UNLOCKED_PLATINUM, platinum); cv.put(Games.UNLOCKED_GOLD, gold); cv.put(Games.UNLOCKED_SILVER, silver); cv.put(Games.UNLOCKED_BRONZE, bronze); cv.put(Games.TROPHIES_DIRTY, 1); cv.put(Games.LAST_UPDATED, updated); newCvs.add(cv); } else // Existing game { boolean isDirty = false; long gameId = c.getLong(COLUMN_GAME_ID); cv = new ContentValues(15); if (c.getInt(COLUMN_GAME_PROGRESS) != progress) { isDirty = true; cv.put(Games.PROGRESS, progress); } if (c.getInt(COLUMN_GAME_BRONZE) != bronze) { isDirty = true; cv.put(Games.UNLOCKED_BRONZE, bronze); } if (c.getInt(COLUMN_GAME_SILVER) != silver) { isDirty = true; cv.put(Games.UNLOCKED_SILVER, silver); } if (c.getInt(COLUMN_GAME_GOLD) != gold) { isDirty = true; cv.put(Games.UNLOCKED_GOLD, gold); } if (c.getInt(COLUMN_GAME_PLATINUM) != platinum) { isDirty = true; cv.put(Games.UNLOCKED_PLATINUM, platinum); } if (isDirty) cv.put(Games.TROPHIES_DIRTY, 1); cv.put(Games.SORT_ORDER, rowNo); cv.put(Games.LAST_UPDATED, updated); cr.update(Games.CONTENT_URI, cv, Games._ID + "=" + gameId, null); } } finally { if (c != null) c.close(); } } if (App.getConfig().logToConsole()) started = displayTimeTaken("Game page processing", started); if (newCvs.size() > 0) { changed = true; ContentValues[] cvs = new ContentValues[newCvs.size()]; newCvs.toArray(cvs); cr.bulkInsert(Games.CONTENT_URI, cvs); if (App.getConfig().logToConsole()) displayTimeTaken("Game page insertion", started); } account.refresh(Preferences.get(mContext)); account.setLastGameUpdate(System.currentTimeMillis()); account.save(Preferences.get(mContext)); if (changed) cr.notifyChange(Games.CONTENT_URI, null); } protected void parseTrophies(PsnAccount account, long gameId) throws ParserException, IOException { // Find game record in local DB ContentResolver cr = mContext.getContentResolver(); String gameUid = Games.getUid(mContext, gameId); List<NameValuePair> inputs = new ArrayList<NameValuePair>(); inputs.add(new BasicNameValuePair("sortBy", "id_asc")); inputs.add(new BasicNameValuePair("titleId", gameUid)); String page = getResponse(String.format(URL_TROPHIES, URLEncoder.encode(account.getScreenName(), "UTF-8")), inputs, true); int index = 0; long started = System.currentTimeMillis(); Matcher achievements = PATTERN_TROPHIES.matcher(page); Matcher m; List<ContentValues> cvList = new ArrayList<ContentValues>(100); while (achievements.find()) { String trophyRow = achievements.group(2); String title = mContext.getString(R.string.secret_trophy); if ((m = PATTERN_TROPHY_TITLE.matcher(trophyRow)).find()) title = htmlDecode(m.group(1)); String description = mContext.getString(R.string.this_is_a_secret_trophy); if ((m = PATTERN_TROPHY_DESCRIPTION.matcher(trophyRow)).find()) description = htmlDecode(m.group(1)); String iconUrl = null; if ((m = PATTERN_TROPHY_ICON.matcher(trophyRow)).find()) iconUrl = getAvatarImage(m.group(1)); long earned = 0; if ((m = PATTERN_TROPHY_EARNED.matcher(trophyRow)).find()) { try { earned = TROPHY_DATE_FORMAT.parse(m.group(1)).getTime(); } catch (ParseException e) { if (App.getConfig().logToConsole()) e.printStackTrace(); } } boolean isSecret = false; int type = 0; if ((m = PATTERN_TROPHY_TYPE.matcher(trophyRow)).find()) { String trophyType = m.group(1).toUpperCase(); if (trophyType.equals("BRONZE")) type = PSN.TROPHY_BRONZE; else if (trophyType.equals("SILVER")) type = PSN.TROPHY_SILVER; else if (trophyType.equals("GOLD")) type = PSN.TROPHY_GOLD; else if (trophyType.equals("PLATINUM")) type = PSN.TROPHY_PLATINUM; else if (trophyType.equals("HIDDEN")) isSecret = true; } ContentValues cv = new ContentValues(20); cv.put(Trophies.TITLE, title); cv.put(Trophies.DESCRIPTION, description); cv.put(Trophies.SORT_ORDER, index); cv.put(Trophies.EARNED, earned); cv.put(Trophies.ICON_URL, iconUrl); cv.put(Trophies.GAME_ID, gameId); cv.put(Trophies.IS_SECRET, isSecret ? 1 : 0); cv.put(Trophies.TYPE, type); cvList.add(cv); index++; } if (App.getConfig().logToConsole()) started = displayTimeTaken("New trophy parsing", started); ContentValues[] cva = new ContentValues[cvList.size()]; cvList.toArray(cva); cr.delete(Trophies.CONTENT_URI, Trophies.GAME_ID + "=" + gameId, null); // Bulk-insert new trophies cr.bulkInsert(Trophies.CONTENT_URI, cva); cr.notifyChange(Trophies.CONTENT_URI, null); if (App.getConfig().logToConsole()) started = displayTimeTaken("New trophy processing", started); // Update the game to remove the 'dirty' attribute ContentValues cv = new ContentValues(10); cv.put(Games.TROPHIES_DIRTY, 0); cr.update(Games.CONTENT_URI, cv, Games._ID + "=" + gameId, null); cr.notifyChange(ContentUris.withAppendedId(Games.CONTENT_URI, gameId), null); if (App.getConfig().logToConsole()) displayTimeTaken("Updating Game", started); } protected String getAvatarImage(String iconUrl) { int index = iconUrl.indexOf("="); if (index >= 0) return iconUrl.substring(index + 1); return iconUrl; } protected void parseFriends(PsnAccount account) throws ParserException, IOException { synchronized (PsnUsParser.class) { String url = String.format(URL_FRIENDS, Math.random()); HttpUriRequest request = new HttpGet(url); request.addHeader("Referer", "http://us.playstation.com/myfriends/"); request.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); String page = getResponse(request, null); ContentResolver cr = mContext.getContentResolver(); final long accountId = account.getId(); ContentValues cv; List<ContentValues> newCvs = new ArrayList<ContentValues>(100); int rowsInserted = 0; int rowsUpdated = 0; int rowsDeleted = 0; long updated = System.currentTimeMillis(); long started = updated; Matcher gameMatcher = PATTERN_FRIENDS.matcher(page); while (gameMatcher.find()) { String friendGt = htmlDecode(gameMatcher.group(1)); GamerProfileInfo gpi; try { gpi = parseGamerProfile(account, friendGt); } catch (IOException e) { if (App.getConfig().logToConsole()) App.logv("Friend " + friendGt + " threw an IOException"); // Update the DeleteMarker, so that the GT is not removed cv = new ContentValues(15); cv.put(Friends.DELETE_MARKER, updated); cr.update(Friends.CONTENT_URI, cv, Friends.ACCOUNT_ID + "=" + account.getId() + " AND " + Friends.ONLINE_ID + "=?", new String[] { friendGt }); continue; } catch (Exception e) { // The rest of the exceptions assume problems with Friend // and potentially remove him/her if (App.getConfig().logToConsole()) { App.logv("Friend " + friendGt + " threw an Exception"); e.printStackTrace(); } continue; } Cursor c = cr.query(Friends.CONTENT_URI, FRIEND_ID_PROJECTION, Friends.ACCOUNT_ID + "=" + account.getId() + " AND " + Friends.ONLINE_ID + "=?", new String[] { friendGt }, null); long friendId = -1; if (c != null) { try { if (c.moveToFirst()) friendId = c.getLong(0); } finally { c.close(); } } cv = new ContentValues(15); cv.put(Friends.ONLINE_ID, gpi.OnlineId); cv.put(Friends.ICON_URL, gpi.AvatarUrl); cv.put(Friends.LEVEL, gpi.Level); cv.put(Friends.PROGRESS, gpi.Progress); cv.put(Friends.ONLINE_STATUS, gpi.OnlineStatus); cv.put(Friends.TROPHIES_PLATINUM, gpi.PlatinumTrophies); cv.put(Friends.TROPHIES_GOLD, gpi.GoldTrophies); cv.put(Friends.TROPHIES_SILVER, gpi.SilverTrophies); cv.put(Friends.TROPHIES_BRONZE, gpi.BronzeTrophies); cv.put(Friends.PLAYING, gpi.Playing); cv.put(Friends.DELETE_MARKER, updated); cv.put(Friends.LAST_UPDATED, updated); if (friendId < 0) { // New cv.put(Friends.ACCOUNT_ID, accountId); newCvs.add(cv); } else { cr.update(ContentUris.withAppendedId(Friends.CONTENT_URI, friendId), cv, null, null); rowsUpdated++; } } // Remove friends rowsDeleted = cr.delete(Friends.CONTENT_URI, Friends.ACCOUNT_ID + "=" + accountId + " AND " + Friends.DELETE_MARKER + "!=" + updated, null); if (newCvs.size() > 0) { ContentValues[] cvs = new ContentValues[newCvs.size()]; newCvs.toArray(cvs); rowsInserted = cr.bulkInsert(Friends.CONTENT_URI, cvs); } account.refresh(Preferences.get(mContext)); account.setLastFriendUpdate(System.currentTimeMillis()); account.save(Preferences.get(mContext)); cr.notifyChange(Friends.CONTENT_URI, null); if (App.getConfig().logToConsole()) started = displayTimeTaken("Friend page processing [I:" + rowsInserted + ";U:" + rowsUpdated + ";D:" + rowsDeleted + "]", started); } } protected GamerProfileInfo parseGamerProfile(PsnAccount account, String onlineId) throws ParserException, IOException { onlineId = onlineId.trim(); String url = String.format(URL_GAMER_DETAIL, URLEncoder.encode(onlineId), Math.random()); String page = getResponse(url, true); Matcher m; if (!(m = PATTERN_ONLINE_ID.matcher(page)).find()) throw new ParserException(mContext, R.string.psn_profile_not_found_f, onlineId); GamerProfileInfo gpi = new GamerProfileInfo(); gpi.OnlineId = htmlDecode(m.group(1).trim()); if ((m = PATTERN_PROGRESS.matcher(page)).find()) gpi.Progress = Integer.parseInt(m.group(1)); if ((m = PATTERN_LEVEL.matcher(page)).find()) gpi.Level = Integer.parseInt(m.group(1)); if ((m = PATTERN_AVATAR_CONTAINER.matcher(page)).find()) { if ((m = PATTERN_URL.matcher(m.group(1))).find()) gpi.AvatarUrl = m.group(1); } m = PATTERN_TROPHY_COUNT.matcher(page); while (m.find()) { String type = m.group(1); if (type.equalsIgnoreCase("bronze")) gpi.BronzeTrophies = Integer.parseInt(m.group(2)); else if (type.equalsIgnoreCase("silver")) gpi.SilverTrophies = Integer.parseInt(m.group(2)); else if (type.equalsIgnoreCase("gold")) gpi.GoldTrophies = Integer.parseInt(m.group(2)); else if (type.equalsIgnoreCase("platinum")) gpi.PlatinumTrophies = Integer.parseInt(m.group(2)); } gpi.OnlineStatus = PSN.STATUS_OTHER; gpi.Playing = null; return gpi; } protected void parseFriendSummary(PsnAccount account, String friendOnlineId) throws ParserException, IOException { long updated = System.currentTimeMillis(); long started = updated; GamerProfileInfo gpi = parseGamerProfile(account, friendOnlineId); ContentResolver cr = mContext.getContentResolver(); Cursor c = cr.query(Friends.CONTENT_URI, FRIEND_ID_PROJECTION, Friends.ACCOUNT_ID + "=" + account.getId() + " AND " + Friends.ONLINE_ID + "=?", new String[] { friendOnlineId }, null); long friendId = -1; try { if (c != null && c.moveToFirst()) friendId = c.getLong(0); } finally { if (c != null) c.close(); } ContentValues cv = new ContentValues(15); cv.put(Friends.ONLINE_ID, gpi.OnlineId); cv.put(Friends.ICON_URL, gpi.AvatarUrl); cv.put(Friends.LEVEL, gpi.Level); cv.put(Friends.PROGRESS, gpi.Progress); cv.put(Friends.ONLINE_STATUS, gpi.OnlineStatus); cv.put(Friends.TROPHIES_PLATINUM, gpi.PlatinumTrophies); cv.put(Friends.TROPHIES_GOLD, gpi.GoldTrophies); cv.put(Friends.TROPHIES_SILVER, gpi.SilverTrophies); cv.put(Friends.TROPHIES_BRONZE, gpi.BronzeTrophies); cv.put(Friends.PLAYING, gpi.Playing); cv.put(Friends.LAST_UPDATED, updated); if (friendId < 0) { // New cv.put(Friends.ACCOUNT_ID, account.getId()); cr.insert(Friends.CONTENT_URI, cv); } else { cr.update(ContentUris.withAppendedId(Friends.CONTENT_URI, friendId), cv, null, null); } if (App.getConfig().logToConsole()) started = displayTimeTaken("Friend page processing", started); cr.notifyChange(ContentUris.withAppendedId(Friends.CONTENT_URI, friendId), null); } protected ComparedGameInfo parseCompareGames(PsnAccount account, String friendId) throws ParserException, IOException { String profileUrl = String.format(URL_GAMER_DETAIL, URLEncoder.encode(friendId), Math.random()); String profilePage = getResponse(profileUrl, true); Matcher m; String avatarUrl = null; if ((m = PATTERN_AVATAR_CONTAINER.matcher(profilePage)).find()) { if ((m = PATTERN_URL.matcher(m.group(1))).find()) avatarUrl = m.group(1); } String selfPage = getResponse(String.format(URL_GAMES, URLEncoder.encode(account.getScreenName(), "UTF-8")), true); String oppPage = getResponse(String.format(URL_GAMES, URLEncoder.encode(friendId, "UTF-8")), true); String uid; int bronze; int silver; int gold; int platinum; int progress; Map<Integer, Object> game; ArrayList<Map<Integer, Object>> games = new ArrayList<Map<Integer, Object>>(); Map<String, Map<Integer, Object>> gameMap = new HashMap<String, Map<Integer, Object>>(); long started = System.currentTimeMillis(); // Own games Matcher gameMatcher = PATTERN_GAMES.matcher(selfPage); for (; gameMatcher.find();) { String group = gameMatcher.group(1); if (!(m = PATTERN_GAME_UID.matcher(group)).find()) continue; uid = m.group(2); //if (!gamePtr.containsKey(uid)) //{ game = new HashMap<Integer, Object>(); game.put(ComparedGameCursor.COLUMN_UID, uid); if ((m = PATTERN_GAME_TITLE.matcher(group)).find()) game.put(ComparedGameCursor.COLUMN_TITLE, htmlDecode(m.group(1))); else game.put(ComparedGameCursor.COLUMN_TITLE, null); if ((m = PATTERN_GAME_ICON.matcher(group)).find()) game.put(ComparedGameCursor.COLUMN_ICON_URL, getAvatarImage(m.group(1))); else game.put(ComparedGameCursor.COLUMN_ICON_URL, null); games.add(game); gameMap.put(uid, game); //} //else //{ // game = gamePtr.get(uid); //} progress = bronze = silver = gold = platinum = 0; if ((m = PATTERN_GAME_PROGRESS.matcher(group)).find()) progress = Integer.parseInt(m.group(1)); if ((m = PATTERN_GAME_TROPHIES.matcher(group)).find()) { bronze = Integer.parseInt(m.group(1)); if (m.find()) { silver = Integer.parseInt(m.group(1)); if (m.find()) { gold = Integer.parseInt(m.group(1)); if (m.find()) platinum = Integer.parseInt(m.group(1)); } } } game.put(ComparedGameCursor.COLUMN_SELF_PLAYED, true); game.put(ComparedGameCursor.COLUMN_SELF_PROGRESS, progress); game.put(ComparedGameCursor.COLUMN_SELF_BRONZE, bronze); game.put(ComparedGameCursor.COLUMN_SELF_SILVER, silver); game.put(ComparedGameCursor.COLUMN_SELF_GOLD, gold); game.put(ComparedGameCursor.COLUMN_SELF_PLATINUM, platinum); // Defaults for opponent game.put(ComparedGameCursor.COLUMN_OPP_PLAYED, false); game.put(ComparedGameCursor.COLUMN_OPP_PROGRESS, 0); game.put(ComparedGameCursor.COLUMN_OPP_BRONZE, 0); game.put(ComparedGameCursor.COLUMN_OPP_SILVER, 0); game.put(ComparedGameCursor.COLUMN_OPP_GOLD, 0); game.put(ComparedGameCursor.COLUMN_OPP_PLATINUM, 0); } if (App.getConfig().logToConsole()) started = displayTimeTaken("parseCompareGames/Own page processing", started); // Opponent's games gameMatcher = PATTERN_GAMES.matcher(oppPage); for (; gameMatcher.find();) { String group = gameMatcher.group(1); if (!(m = PATTERN_GAME_UID.matcher(group)).find()) continue; uid = m.group(2); if (!gameMap.containsKey(uid)) { game = new HashMap<Integer, Object>(); game.put(ComparedGameCursor.COLUMN_UID, uid); if ((m = PATTERN_GAME_TITLE.matcher(group)).find()) game.put(ComparedGameCursor.COLUMN_TITLE, htmlDecode(m.group(1))); else game.put(ComparedGameCursor.COLUMN_TITLE, null); if ((m = PATTERN_GAME_ICON.matcher(group)).find()) game.put(ComparedGameCursor.COLUMN_ICON_URL, getAvatarImage(m.group(1))); else game.put(ComparedGameCursor.COLUMN_ICON_URL, null); game.put(ComparedGameCursor.COLUMN_SELF_PROGRESS, 0); game.put(ComparedGameCursor.COLUMN_SELF_PLAYED, false); game.put(ComparedGameCursor.COLUMN_SELF_BRONZE, 0); game.put(ComparedGameCursor.COLUMN_SELF_SILVER, 0); game.put(ComparedGameCursor.COLUMN_SELF_GOLD, 0); game.put(ComparedGameCursor.COLUMN_SELF_PLATINUM, 0); games.add(game); gameMap.put(uid, game); } else { game = gameMap.get(uid); } progress = bronze = silver = gold = platinum = 0; if ((m = PATTERN_GAME_PROGRESS.matcher(group)).find()) progress = Integer.parseInt(m.group(1)); if ((m = PATTERN_GAME_TROPHIES.matcher(group)).find()) { bronze = Integer.parseInt(m.group(1)); if (m.find()) { silver = Integer.parseInt(m.group(1)); if (m.find()) { gold = Integer.parseInt(m.group(1)); if (m.find()) platinum = Integer.parseInt(m.group(1)); } } } game.put(ComparedGameCursor.COLUMN_OPP_PLAYED, true); game.put(ComparedGameCursor.COLUMN_OPP_PROGRESS, progress); game.put(ComparedGameCursor.COLUMN_OPP_BRONZE, bronze); game.put(ComparedGameCursor.COLUMN_OPP_SILVER, silver); game.put(ComparedGameCursor.COLUMN_OPP_GOLD, gold); game.put(ComparedGameCursor.COLUMN_OPP_PLATINUM, platinum); } if (App.getConfig().logToConsole()) started = displayTimeTaken("parseCompareGames/Opp. page processing", started); ComparedGameInfo cgi = new ComparedGameInfo(mContext.getContentResolver()); cgi.myAvatarIconUrl = account.getIconUrl(); cgi.yourAvatarIconUrl = avatarUrl; for (Map<Integer, Object> g : games) { cgi.cursor.addItem((String) g.get(ComparedGameCursor.COLUMN_UID), (String) g.get(ComparedGameCursor.COLUMN_TITLE), (String) g.get(ComparedGameCursor.COLUMN_ICON_URL), (Boolean) g.get(ComparedGameCursor.COLUMN_SELF_PLAYED), (Integer) g.get(ComparedGameCursor.COLUMN_SELF_PLATINUM), (Integer) g.get(ComparedGameCursor.COLUMN_SELF_GOLD), (Integer) g.get(ComparedGameCursor.COLUMN_SELF_SILVER), (Integer) g.get(ComparedGameCursor.COLUMN_SELF_BRONZE), (Integer) g.get(ComparedGameCursor.COLUMN_SELF_PROGRESS), (Boolean) g.get(ComparedGameCursor.COLUMN_OPP_PLAYED), (Integer) g.get(ComparedGameCursor.COLUMN_OPP_PLATINUM), (Integer) g.get(ComparedGameCursor.COLUMN_OPP_GOLD), (Integer) g.get(ComparedGameCursor.COLUMN_OPP_SILVER), (Integer) g.get(ComparedGameCursor.COLUMN_OPP_BRONZE), (Integer) g.get(ComparedGameCursor.COLUMN_OPP_PROGRESS)); } return cgi; } protected GameCatalogItemDetails parseGameCatalogItemDetails(GameCatalogItem item) throws ParserException, IOException { if (item == null) return null; GameCatalogItemDetails details = GameCatalogItemDetails.fromItem(item); long started = System.currentTimeMillis(); String detailPage = getResponse(item.DetailUrl); if (App.getConfig().logToConsole()) started = displayTimeTaken("parseGameCatalogItemDetails/data fetch", started); Matcher m = PATTERN_GAME_CATALOG_DETAILS.matcher(detailPage); while (m.find()) { String key = m.group(1); String value = m.group(2); if (key.equals("gameTitle")) details.Description = value; else if (key.equals("gameLongDescription")) details.Description = value; } m = PATTERN_GAME_CATALOG_GAME_XML.matcher(detailPage); if (m.find()) { // PS3 screenshots (Flash-based) String xmlFile = m.group(1); String xmlContents = null; try { xmlContents = getResponse(xmlFile); } catch (Exception e) { } if (xmlContents != null) { ArrayList<HashMap<String, String>> pairList = parsePairsInSimpleXml(xmlContents, "element"); ArrayList<String> thumbs = new ArrayList<String>(); ArrayList<String> screens = new ArrayList<String>(); for (HashMap<String, String> pairs : pairList) { if (pairs.containsKey("thumb") && pairs.containsKey("large")) { thumbs.add(SCREENSHOT_BASE_URL + pairs.get("thumb")); screens.add(SCREENSHOT_BASE_URL + pairs.get("large")); } } details.ScreenshotsThumb = new String[thumbs.size()]; thumbs.toArray(details.ScreenshotsThumb); details.ScreenshotsLarge = new String[screens.size()]; screens.toArray(details.ScreenshotsLarge); } } else { // Try Vita screenshots ArrayList<String> thumbs = new ArrayList<String>(); m = PATTERN_GAME_CATALOG_PSV_THUMBS.matcher(detailPage); while (m.find()) thumbs.add(m.group(1)); ArrayList<String> screens = new ArrayList<String>(); m = PATTERN_GAME_CATALOG_PSV_LARGE.matcher(detailPage); while (m.find()) screens.add(m.group(1)); if (thumbs.size() > 0 && screens.size() == thumbs.size()) { details.ScreenshotsThumb = new String[thumbs.size()]; thumbs.toArray(details.ScreenshotsThumb); details.ScreenshotsLarge = new String[screens.size()]; screens.toArray(details.ScreenshotsLarge); } } if (App.getConfig().logToConsole()) displayTimeTaken("parseGameCatalogItemDetails/parsing", started); return details; } @Override protected GameCatalogList parseGameCatalog(int console, int page, int releaseStatus, int sortOrder) throws ParserException, IOException { String beginsWith = null; String[] rating = null; String[] genre = null; String publisher = null; String sortOrderSpec = null; String consoleSpec = null; if (console == PSN.CATALOG_CONSOLE_PSVITA) consoleSpec = "psvita"; else if (console == PSN.CATALOG_CONSOLE_PSP) consoleSpec = "psp3000,pspgo"; else if (console == PSN.CATALOG_CONSOLE_PS2) consoleSpec = "ps2"; else consoleSpec = "ps3"; if (sortOrder == PSN.CATALOG_SORT_BY_ALPHA) sortOrderSpec = "a-z"; else sortOrderSpec = "rDatenf"; int pageSize = 12; List<NameValuePair> inputs = new ArrayList<NameValuePair>(3); addValue(inputs, "MaxReleaseDateValue", "2"); // ? addValue(inputs, "MinReleaseDateValue", "6"); // ? addValue(inputs, "beginsWith", (beginsWith == null) ? "Any" : beginsWith); addValue(inputs, "console", consoleSpec); addValue(inputs, "esrb", (rating == null) ? "All" : joinString(rating, ",")); addValue(inputs, "genre", (genre == null) ? "All" : joinString(genre, ",")); addValue(inputs, "lastAjaxCallTimeStamp", System.currentTimeMillis() - 36000000); addValue(inputs, "publisher", (publisher == null) ? "Any" : publisher); addValue(inputs, "recordsOnPage", pageSize + ""); addValue(inputs, "sortOrder", sortOrderSpec); addValue(inputs, "throughAjax", "true"); addValue(inputs, "viewType", "gridView"); addValue(inputs, "page", page + ""); long started = System.currentTimeMillis(); String catalogPage = getResponse(URL_GAME_CATALOG, inputs, true); catalogPage = htmlDecode(catalogPage); int spacePos; int records = 0; if ((spacePos = catalogPage.indexOf(" ")) > 0) { try { records = Integer.parseInt(catalogPage.substring(0, spacePos)); } catch (NumberFormatException ex) { } } if (App.getConfig().logToConsole()) started = displayTimeTaken("parseGameCatalog/data fetch", started); Matcher m; Matcher itemMatcher = PATTERN_GAME_CATALOG_ITEMS.matcher(catalogPage); GameCatalogList catalog = new GameCatalogList(); catalog.PageNumber = page; catalog.PageSize = pageSize; //SimpleDateFormat myParser = new SimpleDateFormat("MM.yyyy"); //Pattern myDetector = Pattern.compile("^\\d{2}\\.\\d{4}$"); boolean noMatches = false; while (itemMatcher.find()) { String content = itemMatcher.group(1); if (!(m = PATTERN_GAME_CATALOG_ESSENTIALS.matcher(content)).find()) { noMatches = true; if (App.getConfig().logToConsole()) App.logv("PATTERN_GAME_CATALOG_ESSENTIALS matched nothing"); continue; } GameCatalogItem item = new GameCatalogItem(); item.BoxartUrl = m.group(1); item.DetailUrl = m.group(2); item.Title = htmlDecode(m.group(3)); item.Overview = htmlDecode(m.group(4)); m = PATTERN_GAME_CATALOG_STATS.matcher(content); for (int row = 0; m.find(); row++) { if (row == 0) { /* AK: Gone, daddy gone... item.ReleaseDate = htmlDecode(m.group(2)); item.ReleaseDateTicks = 0; String parseDate = null; if (myDetector.matcher(item.ReleaseDate).find()) parseDate = item.ReleaseDate; if (parseDate != null) { try { item.ReleaseDateTicks = myParser.parse(parseDate).getTime(); } catch(Exception e) { } } */ } else if (row == 1) // Platform { } else if (row == 2) // Genre { item.Genre = htmlDecode(m.group(2)); } else if (row == 3) // Players { } else if (row == 4) // Online players { } else if (row == 5) // Publisher { } } catalog.Items.add(item); } if (noMatches && catalog.Items.size() < 1) catalog.MorePages = false; else catalog.MorePages = (catalog.PageNumber * catalog.PageSize) < records; if (App.getConfig().logToConsole()) App.logv("pN: " + catalog.PageNumber + " ;pS: " + catalog.PageSize + " ;records: " + records + " ;more? " + catalog.MorePages); if (App.getConfig().logToConsole()) displayTimeTaken("parseGameCatalog/parsing", started); return catalog; } protected ComparedTrophyInfo parseCompareTrophies(PsnAccount account, String friendId, String gameId) throws ParserException, IOException { String url = String.format(URL_COMPARE_TROPHIES, URLEncoder.encode(gameId, "UTF-8"), URLEncoder.encode(account.getScreenName(), "UTF-8"), URLEncoder.encode(friendId, "UTF-8")); HttpUriRequest request = new HttpGet(url); request.addHeader("Referer", String.format(URL_COMPARE_TROPHIES_REFERER, URLEncoder.encode(friendId, "UTF-8"))); // Fetch the "main" page String page = getResponse(request, null); ComparedTrophyInfo cti = new ComparedTrophyInfo(mContext.getContentResolver()); Map<Integer, Object> trophy; ArrayList<Map<Integer, Object>> trophies = new ArrayList<Map<Integer, Object>>(); Map<String, Map<Integer, Object>> trophyMap = new HashMap<String, Map<Integer, Object>>(); Matcher m; if (!(m = PATTERN_COMPARE_TROPHIES_TITLE_ID.matcher(page)).find()) { if (App.getConfig().logToConsole()) App.logv("No title ID for " + gameId); return cti; } String nullUnlockString = PSN.getShortTrophyUnlockString(mContext, 0); String titleId = m.group(1); long started = System.currentTimeMillis(); Matcher matcher = PATTERN_COMPARE_TROPHIES.matcher(page); for (; matcher.find();) { String trophyId = matcher.group(1); String group = matcher.group(3); trophy = new HashMap<Integer, Object>(); if ((m = PATTERN_COMPARE_TROPHIES_TITLE.matcher(group)).find()) { trophy.put(ComparedTrophyCursor.COLUMN_TITLE, htmlDecode(m.group(1).trim())); if ((m = PATTERN_TROPHY_DESCRIPTION.matcher(group)).find()) trophy.put(ComparedTrophyCursor.COLUMN_DESCRIPTION, htmlDecode(m.group(1).trim())); else trophy.put(ComparedTrophyCursor.COLUMN_DESCRIPTION, null); } else { trophy.put(ComparedTrophyCursor.COLUMN_TITLE, mContext.getString(R.string.secret_trophy)); trophy.put(ComparedTrophyCursor.COLUMN_DESCRIPTION, mContext.getString(R.string.this_is_a_secret_trophy)); } if ((m = PATTERN_TROPHY_ICON.matcher(group)).find()) trophy.put(ComparedTrophyCursor.COLUMN_ICON_URL, getAvatarImage(m.group(1))); else trophy.put(ComparedTrophyCursor.COLUMN_ICON_URL, null); if ((m = PATTERN_TROPHY_TYPE.matcher(group)).find()) { String trophyType = m.group(1).toUpperCase(); if (trophyType.equals("BRONZE")) trophy.put(ComparedTrophyCursor.COLUMN_TYPE, PSN.TROPHY_BRONZE); else if (trophyType.equals("SILVER")) trophy.put(ComparedTrophyCursor.COLUMN_TYPE, PSN.TROPHY_SILVER); else if (trophyType.equals("GOLD")) trophy.put(ComparedTrophyCursor.COLUMN_TYPE, PSN.TROPHY_GOLD); else if (trophyType.equals("PLATINUM")) trophy.put(ComparedTrophyCursor.COLUMN_TYPE, PSN.TROPHY_PLATINUM); else if (trophyType.equals("HIDDEN")) trophy.put(ComparedTrophyCursor.COLUMN_TYPE, PSN.TROPHY_SECRET); } trophy.put(ComparedTrophyCursor.COLUMN_IS_LOCKED, true); trophy.put(ComparedTrophyCursor.COLUMN_SELF_EARNED, nullUnlockString); trophy.put(ComparedTrophyCursor.COLUMN_OPP_EARNED, nullUnlockString); trophyMap.put(trophyId, trophy); trophies.add(trophy); } if (App.getConfig().logToConsole()) started = displayTimeTaken("parseCompareTrophies/processing", started); JSONArray array; JSONObject json; // Run the compare requests url = String.format(URL_COMPARE_TROPHIES_DETAIL, URLEncoder.encode(titleId, "UTF-8"), URLEncoder.encode(account.getScreenName(), "UTF-8")); page = getResponse(url, true); json = getJSONObject(page); if ((array = json.optJSONArray("trophyList")) != null) { int n = array.length(); for (int i = 0; i < n; i++) { if ((json = array.optJSONObject(i)) != null) { String trophyId = json.optString("id"); if (trophyId != null && trophyMap.containsKey(trophyId)) { long earned; String dateTime = json.optString("stampDate") + " " + json.optString("stampTime"); try { earned = COMPARE_TROPHY_DATE_FORMAT.parse(dateTime).getTime(); } catch (ParseException e) { if (App.getConfig().logToConsole()) e.printStackTrace(); continue; } trophyMap.get(trophyId).put(ComparedTrophyCursor.COLUMN_IS_LOCKED, false); trophyMap.get(trophyId).put(ComparedTrophyCursor.COLUMN_SELF_EARNED, PSN.getShortTrophyUnlockString(mContext, earned)); } } } } url = String.format(URL_COMPARE_TROPHIES_DETAIL, URLEncoder.encode(titleId, "UTF-8"), URLEncoder.encode(friendId, "UTF-8")); page = getResponse(url, true); json = getJSONObject(page); if ((array = json.optJSONArray("trophyList")) != null) { int n = array.length(); for (int i = 0; i < n; i++) { if ((json = array.optJSONObject(i)) != null) { String trophyId = json.optString("id"); if (trophyId != null && trophyMap.containsKey(trophyId)) { long earned; String dateTime = json.optString("stampDate") + " " + json.optString("stampTime"); try { earned = COMPARE_TROPHY_DATE_FORMAT.parse(dateTime).getTime(); } catch (ParseException e) { if (App.getConfig().logToConsole()) e.printStackTrace(); continue; } trophyMap.get(trophyId).put(ComparedTrophyCursor.COLUMN_OPP_EARNED, PSN.getShortTrophyUnlockString(mContext, earned)); } } } } for (Map<Integer, Object> t : trophies) { cti.cursor.addItem((String) t.get(ComparedTrophyCursor.COLUMN_TITLE), (String) t.get(ComparedTrophyCursor.COLUMN_DESCRIPTION), (String) t.get(ComparedTrophyCursor.COLUMN_ICON_URL), (Integer) t.get(ComparedTrophyCursor.COLUMN_TYPE), (Integer) t.get(ComparedTrophyCursor.COLUMN_TYPE) == PSN.TROPHY_SECRET, (Boolean) t.get(ComparedTrophyCursor.COLUMN_IS_LOCKED), (String) t.get(ComparedTrophyCursor.COLUMN_SELF_EARNED), (String) t.get(ComparedTrophyCursor.COLUMN_OPP_EARNED)); } return cti; } public void fetchSummary(PsnAccount account) throws AuthenticationException, IOException, ParserException { for (int i = 0;; i++) { if (!authenticate(account, true)) throw new AuthenticationException( mContext.getString(R.string.error_invalid_credentials_f, account.getEmailAddress())); try { parseAccountSummary(account); saveSession(account); } catch (ClientProtocolException e) { // We'll allow one ClientProtocolException, in case the // session data is invalid and redirection has failed. // If that happens, we re-authenticate if (i < 1) { if (App.getConfig().logToConsole()) App.logv("Re-authenticating"); deleteSession(account); continue; } throw e; } break; } } public void fetchFriends(PsnAccount account) throws AuthenticationException, IOException, ParserException { for (int i = 0;; i++) { if (!authenticate(account, true)) throw new AuthenticationException( mContext.getString(R.string.error_invalid_credentials_f, account.getEmailAddress())); try { parseFriends(account); saveSession(account); } catch (ClientProtocolException e) { // We'll allow one ClientProtocolException, in case the // session data is invalid and redirection has failed. // If that happens, we re-authenticate if (i < 1) { if (App.getConfig().logToConsole()) App.logv("Re-authenticating"); deleteSession(account); continue; } throw e; } break; } } public void fetchGames(PsnAccount account) throws AuthenticationException, IOException, ParserException { for (int i = 0;; i++) { if (!authenticate(account, true)) throw new AuthenticationException( mContext.getString(R.string.error_invalid_credentials_f, account.getEmailAddress())); try { parseGames(account); saveSession(account); } catch (ClientProtocolException e) { // We'll allow one ClientProtocolException, in case the // session data is invalid and redirection has failed. // If that happens, we re-authenticate if (i < 1) { if (App.getConfig().logToConsole()) App.logv("Re-authenticating"); deleteSession(account); continue; } throw e; } break; } } public void fetchTrophies(PsnAccount account, long gameId) throws AuthenticationException, IOException, ParserException { for (int i = 0;; i++) { if (!authenticate(account, true)) throw new AuthenticationException( mContext.getString(R.string.error_invalid_credentials_f, account.getEmailAddress())); try { parseTrophies(account, gameId); saveSession(account); } catch (ClientProtocolException e) { // We'll allow one ClientProtocolException, in case the // session data is invalid and redirection has failed. // If that happens, we re-authenticate if (i < 1) { if (App.getConfig().logToConsole()) App.logv("Re-authenticating"); deleteSession(account); continue; } throw e; } break; } } public void fetchFriendSummary(PsnAccount account, String friendId) throws AuthenticationException, IOException, ParserException { for (int i = 0;; i++) { if (!authenticate(account, true)) throw new AuthenticationException( mContext.getString(R.string.error_invalid_credentials_f, account.getEmailAddress())); try { parseFriendSummary(account, friendId); saveSession(account); } catch (ClientProtocolException e) { // We'll allow one ClientProtocolException, in case the // session data is invalid and redirection has failed. // If that happens, we re-authenticate if (i < 1) { if (App.getConfig().logToConsole()) App.logv("Re-authenticating"); deleteSession(account); continue; } throw e; } break; } } public ComparedGameInfo compareGames(PsnAccount account, String friendId) throws AuthenticationException, IOException, ParserException { ComparedGameInfo cgi; for (int i = 0;; i++) { if (!authenticate(account, true)) throw new AuthenticationException( mContext.getString(R.string.error_invalid_credentials_f, account.getEmailAddress())); try { cgi = parseCompareGames(account, friendId); } catch (ClientProtocolException e) { // We'll allow one ClientProtocolException, in case the // session data is invalid and redirection has failed. // If that happens, we re-authenticate if (i < 1) { if (App.getConfig().logToConsole()) App.logv("Re-authenticating"); deleteSession(account); continue; } throw e; } break; } return cgi; } public ComparedTrophyInfo compareTrophies(PsnAccount account, String friendId, String gameId) throws AuthenticationException, IOException, ParserException { ComparedTrophyInfo cti; for (int i = 0;; i++) { if (!authenticate(account, true)) throw new AuthenticationException( mContext.getString(R.string.error_invalid_credentials_f, account.getEmailAddress())); try { cti = parseCompareTrophies(account, friendId, gameId); } catch (ClientProtocolException e) { // We'll allow one ClientProtocolException, in case the // session data is invalid and redirection has failed. // If that happens, we re-authenticate if (i < 1) { if (App.getConfig().logToConsole()) App.logv("Re-authenticating"); deleteSession(account); continue; } throw e; } break; } return cti; } @Override public ContentValues validateAccount(BasicAccount account) throws AuthenticationException, IOException, ParserException { if (!authenticate(account, false)) throw new AuthenticationException( mContext.getString(R.string.error_invalid_credentials_f, account.getLogonId())); ContentValues cv = parseSummaryData((PsnAccount) account); cv.put(Profiles.ACCOUNT_ID, account.getId()); cv.put(Profiles.UUID, account.getUuid()); return cv; } @Override public void deleteAccount(BasicAccount account) { ContentResolver cr = mContext.getContentResolver(); long accountId = account.getId(); // Clear games & achievements StringBuffer buffer = new StringBuffer(); Cursor c = cr.query(Games.CONTENT_URI, new String[] { Games._ID }, Games.ACCOUNT_ID + "=" + accountId, null, null); try { if (c != null) { while (c.moveToNext()) { if (buffer.length() > 0) buffer.append(","); buffer.append(c.getLong(0)); } } } finally { if (c != null) c.close(); } // Clear trophies cr.delete(Trophies.CONTENT_URI, Trophies.GAME_ID + " IN (" + buffer.toString() + ")", null); // Clear rest of data cr.delete(Games.CONTENT_URI, Games.ACCOUNT_ID + "=" + accountId, null); cr.delete(Profiles.CONTENT_URI, Profiles.ACCOUNT_ID + "=" + accountId, null); cr.delete(Friends.CONTENT_URI, Friends.ACCOUNT_ID + "=" + accountId, null); // Send notifications cr.notifyChange(Profiles.CONTENT_URI, null); cr.notifyChange(Trophies.CONTENT_URI, null); cr.notifyChange(Games.CONTENT_URI, null); cr.notifyChange(Friends.CONTENT_URI, null); // Delete authenticated session deleteSession(account); } public void createAccount(BasicAccount account, ContentValues cv) { // Add profile to database ContentResolver cr = mContext.getContentResolver(); cr.insert(Profiles.CONTENT_URI, cv); cr.notifyChange(Profiles.CONTENT_URI, null); PsnAccount psnAccount = (PsnAccount) account; // Save changes to preferences psnAccount.setOnlineId(cv.getAsString(Profiles.ONLINE_ID)); psnAccount.setIconUrl(cv.getAsString(Profiles.ICON_URL)); psnAccount.setLastSummaryUpdate(System.currentTimeMillis()); account.save(Preferences.get(mContext)); } @Override public RssChannel fetchBlog() throws ParserException { try { return RssHandler.getFeed(URL_BLOG); } catch (Exception e) { throw new ParserException(mContext, R.string.error_loading_blog); } } }