From 056aa237feea2de4db250c22da6af790aa650357 Mon Sep 17 00:00:00 2001 From: Nikolay Elenkov Date: Tue, 13 Aug 2013 22:00:45 +0900 Subject: [PATCH 01/12] backported fix for #512 to master --- .../v2/AccountManagerAuthenticator.java | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/src/com/github/andlyticsproject/console/v2/AccountManagerAuthenticator.java b/src/com/github/andlyticsproject/console/v2/AccountManagerAuthenticator.java index 9a6467eb..2daf0471 100644 --- a/src/com/github/andlyticsproject/console/v2/AccountManagerAuthenticator.java +++ b/src/com/github/andlyticsproject/console/v2/AccountManagerAuthenticator.java @@ -7,6 +7,7 @@ import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; @@ -37,6 +38,10 @@ public class AccountManagerAuthenticator extends BaseAuthenticator { private static final String TAG = AccountManagerAuthenticator.class.getSimpleName(); private static final String DEVELOPER_CONSOLE_URL = "https://play.google.com/apps/publish/"; + private static final Uri ISSUE_AUTH_TOKEN_URL = Uri + .parse("https://www.google.com/accounts/IssueAuthToken?service=gaia&Session=false"); + private static final Uri TOKEN_AUTH_URL = Uri + .parse("https://www.google.com/accounts/TokenAuth"); private static final int REQUEST_AUTHENTICATE = 42; @@ -140,14 +145,44 @@ private SessionCredentials authenticateInternal(Activity activity, boolean inval Log.d(TAG, "Weblogin URL: " + webloginUrl); } - if (!webloginUrl.contains("MergeSession")) { + if (!webloginUrl.contains("MergeSession") || webloginUrl.contains("WILL_NOT_SIGN_IN")) { Log.d(TAG, "Most probably additional verification is required, " + "opening browser"); - openAuthUrlInBrowser(activity); + String sid = accountManager + .getAuthToken(account, "SID", null, activity, null, null).getResult() + .getString(AccountManager.KEY_AUTHTOKEN); + if (sid == null) { + throw new AuthenticationException("Authentication error: cannot get SID."); + } + String lsid = accountManager + .getAuthToken(account, "LSID", null, activity, null, null).getResult() + .getString(AccountManager.KEY_AUTHTOKEN); + if (lsid == null) { + throw new AuthenticationException("Authentication error: cannot get LSID."); + } + + String url = ISSUE_AUTH_TOKEN_URL.buildUpon().appendQueryParameter("SID", sid) + .appendQueryParameter("LSID", lsid).build().toString(); + HttpPost getUberToken = new HttpPost(url); + HttpResponse response = httpClient.execute(getUberToken); + int status = response.getStatusLine().getStatusCode(); + if (status == HttpStatus.SC_UNAUTHORIZED) { + throw new AuthenticationException("Cannot get uber token: " + + response.getStatusLine()); + } + String uberToken = EntityUtils.toString(response.getEntity(), "UTF-8"); + if (uberToken == null || "".equals(uberToken)) { + throw new AuthenticationException("Invalid ubertoken"); + } + if (DEBUG) { + Log.d(TAG, "Uber token: " + uberToken); + } - throw new AuthenticationException("Sign in via the browser, then " - + "get back to Andlytics"); + webloginUrl = TOKEN_AUTH_URL.buildUpon() + .appendQueryParameter("source", "android-browser") + .appendQueryParameter("auth", uberToken) + .appendQueryParameter("continue", DEVELOPER_CONSOLE_URL).build().toString(); } HttpGet getConsole = new HttpGet(webloginUrl); From 5c1c618e6e8e677b62c43562aac77506800d63bf Mon Sep 17 00:00:00 2001 From: Nikolay Elenkov Date: Wed, 14 Aug 2013 14:29:13 +0900 Subject: [PATCH 02/12] bumped version for minor release --- AndroidManifest.xml | 4 ++-- res/raw/changelog.html | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 2d246507..46a91e1b 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -3,8 +3,8 @@ xmlns:tools="http://schemas.android.com/tools" package="com.github.andlyticsproject" android:installLocation="auto" - android:versionCode="233" - android:versionName="2.5.5" > + android:versionCode="238" + android:versionName="2.5.6" > diff --git a/res/raw/changelog.html b/res/raw/changelog.html index fce36f43..f06e6ec1 100755 --- a/res/raw/changelog.html +++ b/res/raw/changelog.html @@ -14,6 +14,11 @@
    +
  • v2.5.6 +
      +
    • Authentication error fix
    • +
    +
  • v2.5.5
    • API update
    • From 16830695628257ba072d28df60ae844b6e1e5b88 Mon Sep 17 00:00:00 2001 From: Nikolay Elenkov Date: Mon, 20 Jan 2014 11:09:31 +0900 Subject: [PATCH 03/12] backported fix for #562 to master --- .gitignore | 2 + AndroidManifest.xml | 6 +-- project.properties | 2 +- res/raw/changelog.html | 6 +++ .../console/v2/JsonParser.java | 37 ++++++++++--------- 5 files changed, 31 insertions(+), 22 deletions(-) mode change 100644 => 100755 .gitignore diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index f415fb10..0ac9c3e8 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ proguard/ local.properties secure.properties /assets +private-strings.xml +res/values/private-strings.xml diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 46a91e1b..e9dade17 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -3,8 +3,8 @@ xmlns:tools="http://schemas.android.com/tools" package="com.github.andlyticsproject" android:installLocation="auto" - android:versionCode="238" - android:versionName="2.5.6" > + android:versionCode="243" + android:versionName="2.5.7" > @@ -19,7 +19,7 @@ + android:targetSdkVersion="19" />
        +
      • v2.5.7 +
          +
        • API Update
        • +
        • Register as a tester for the alpha version to get latest features. This version will not get any new features. Details are in the app description.
        • +
        +
      • v2.5.6
        • Authentication error fix
        • diff --git a/src/com/github/andlyticsproject/console/v2/JsonParser.java b/src/com/github/andlyticsproject/console/v2/JsonParser.java index 3e9e415c..4647d994 100755 --- a/src/com/github/andlyticsproject/console/v2/JsonParser.java +++ b/src/com/github/andlyticsproject/console/v2/JsonParser.java @@ -1,14 +1,5 @@ package com.github.andlyticsproject.console.v2; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Locale; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - import android.util.Log; import com.github.andlyticsproject.console.DevConsoleException; @@ -18,6 +9,15 @@ import com.github.andlyticsproject.model.Comment; import com.github.andlyticsproject.util.FileUtils; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; + /** * This class contains static methods used to parse JSON from {@link DevConsoleV2} * @@ -92,14 +92,14 @@ static void parseStatistics(String json, AppStats stats, int statsType) throws J int latestValue = latestData.getJSONObject("2").getInt("1"); switch (statsType) { - case DevConsoleV2Protocol.STATS_TYPE_TOTAL_USER_INSTALLS: - stats.setTotalDownloads(latestValue); - break; - case DevConsoleV2Protocol.STATS_TYPE_ACTIVE_DEVICE_INSTALLS: - stats.setActiveInstalls(latestValue); - break; - default: - break; + case DevConsoleV2Protocol.STATS_TYPE_TOTAL_USER_INSTALLS: + stats.setTotalDownloads(latestValue); + break; + case DevConsoleV2Protocol.STATS_TYPE_ACTIVE_DEVICE_INSTALLS: + stats.setActiveInstalls(latestValue); + break; + default: + break; } } @@ -264,7 +264,8 @@ static List parseAppInfos(String json, String accountName, boolean skip * Array with app icon [null,null,null,icon] */ // XXX - JSONArray appVersions = jsonAppInfo.optJSONObject("4").optJSONArray("1"); + JSONArray appVersions = jsonAppInfo.getJSONObject("4").getJSONObject("1") + .optJSONArray("1"); if (DEBUG) { pp("appVersions", appVersions); } From ae98d64344f24a7ab455d1e324b38e15cea9b475 Mon Sep 17 00:00:00 2001 From: Nikolay Elenkov Date: Wed, 5 Feb 2014 11:36:33 +0900 Subject: [PATCH 04/12] backported OAuth authenticator --- AndroidManifest.xml | 4 +- res/raw/changelog.html | 6 + .../v2/AccountManagerAuthenticator.java | 63 +---- .../console/v2/BaseAuthenticator.java | 60 ++++- .../console/v2/DevConsoleV2.java | 30 +-- .../v2/OauthAccountManagerAuthenticator.java | 225 ++++++++++++++++++ 6 files changed, 318 insertions(+), 70 deletions(-) create mode 100644 src/com/github/andlyticsproject/console/v2/OauthAccountManagerAuthenticator.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index e9dade17..600cd8a6 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -3,8 +3,8 @@ xmlns:tools="http://schemas.android.com/tools" package="com.github.andlyticsproject" android:installLocation="auto" - android:versionCode="243" - android:versionName="2.5.7" > + android:versionCode="247" + android:versionName="2.5.8" > diff --git a/res/raw/changelog.html b/res/raw/changelog.html index 538e82a5..9704f88f 100755 --- a/res/raw/changelog.html +++ b/res/raw/changelog.html @@ -14,6 +14,12 @@
            +
          • v2.5.8 +
              +
            • New authentication method
            • +
            • Register as a tester for the alpha version to get latest features. This version will not get any new features. Details are in the app description.
            • +
            +
          • v2.5.7
            • API Update
            • diff --git a/src/com/github/andlyticsproject/console/v2/AccountManagerAuthenticator.java b/src/com/github/andlyticsproject/console/v2/AccountManagerAuthenticator.java index 2daf0471..504c01f9 100644 --- a/src/com/github/andlyticsproject/console/v2/AccountManagerAuthenticator.java +++ b/src/com/github/andlyticsproject/console/v2/AccountManagerAuthenticator.java @@ -1,16 +1,5 @@ package com.github.andlyticsproject.console.v2; -import java.io.IOException; -import java.util.List; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.util.EntityUtils; - import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AuthenticatorException; @@ -31,7 +20,17 @@ import com.github.andlyticsproject.console.AuthenticationException; import com.github.andlyticsproject.console.NetworkException; import com.github.andlyticsproject.model.DeveloperConsoleAccount; -import com.github.andlyticsproject.util.FileUtils; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.util.EntityUtils; + +import java.io.IOException; +import java.util.List; public class AccountManagerAuthenticator extends BaseAuthenticator { @@ -208,14 +207,14 @@ private SessionCredentials authenticateInternal(Activity activity, boolean inval DeveloperConsoleAccount[] developerAccounts = findDeveloperAccounts(responseStr); if (developerAccounts == null) { - debugAuthFailure(activity, responseStr); + debugAuthFailure(activity, responseStr, webloginUrl); throw new AuthenticationException("Couldn't get developer account ID."); } String xsrfToken = findXsrfToken(responseStr); if (xsrfToken == null) { - debugAuthFailure(activity, responseStr); + debugAuthFailure(activity, responseStr, webloginUrl); throw new AuthenticationException("Couldn't get XSRF token."); } @@ -237,40 +236,4 @@ private SessionCredentials authenticateInternal(Activity activity, boolean inval } } - private void debugAuthFailure(Activity activity, String responseStr) { - FileUtils.writeToAndlyticsDir("console-response.html", responseStr); - openAuthUrlInBrowser(activity); - } - - private void openAuthUrlInBrowser(Activity activity) { - if (webloginUrl == null) { - Log.d(TAG, "Null webloginUrl?"); - return; - } - - Log.d(TAG, "Opening login URL in browser: " + webloginUrl); - - Intent viewInBrowser = new Intent(Intent.ACTION_VIEW); - viewInBrowser.setData(Uri.parse(webloginUrl)); - - // Always show the notification - // When this occurs, it can often occur in batches, e.g. if a the user also clicks to view - // comments which results in multiple dev consoles opening in their browser without an - // explanation. This is even worse if they have multiple accounts and/or are currently - // signed in via a different account - Context ctx = AndlyticsApp.getInstance(); - Builder builder = new NotificationCompat.Builder(ctx); - builder.setSmallIcon(R.drawable.statusbar_andlytics); - builder.setContentTitle(ctx.getResources().getString(R.string.auth_error, accountName)); - builder.setContentText(ctx.getResources().getString(R.string.auth_error_open_browser, - accountName)); - builder.setAutoCancel(true); - PendingIntent contentIntent = PendingIntent.getActivity(ctx, accountName.hashCode(), - viewInBrowser, PendingIntent.FLAG_UPDATE_CURRENT); - builder.setContentIntent(contentIntent); - - NotificationManager nm = (NotificationManager) ctx - .getSystemService(Context.NOTIFICATION_SERVICE); - nm.notify(accountName.hashCode(), builder.build()); - } } diff --git a/src/com/github/andlyticsproject/console/v2/BaseAuthenticator.java b/src/com/github/andlyticsproject/console/v2/BaseAuthenticator.java index e60e3d62..12b0008c 100644 --- a/src/com/github/andlyticsproject/console/v2/BaseAuthenticator.java +++ b/src/com/github/andlyticsproject/console/v2/BaseAuthenticator.java @@ -1,17 +1,32 @@ package com.github.andlyticsproject.console.v2; +import android.app.Activity; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.support.v4.app.NotificationCompat; +import android.support.v4.app.NotificationCompat.Builder; +import android.util.Log; + +import com.github.andlyticsproject.AndlyticsApp; +import com.github.andlyticsproject.R; +import com.github.andlyticsproject.model.DeveloperConsoleAccount; +import com.github.andlyticsproject.util.FileUtils; + +import org.apache.commons.lang3.StringEscapeUtils; + import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.lang3.StringEscapeUtils; - -import com.github.andlyticsproject.model.DeveloperConsoleAccount; - public abstract class BaseAuthenticator implements DevConsoleAuthenticator { + private static final String TAG = BaseAuthenticator.class.getSimpleName(); + protected static final Pattern DEV_ACC_PATTERN = Pattern .compile("\"DeveloperConsoleAccounts\":\"\\{\\\\\"1\\\\\":\\[\\{\\\\\"1\\\\\":\\\\\"(\\d{20})\\\\\""); protected static final Pattern DEV_ACCS_PATTERN = Pattern @@ -72,4 +87,41 @@ public String getAccountName() { return accountName; } + protected void debugAuthFailure(Activity activity, String responseStr, String webloginUrl) { + FileUtils.writeToAndlyticsDir("console-response.html", responseStr); + openAuthUrlInBrowser(activity, webloginUrl); + } + + protected void openAuthUrlInBrowser(Activity activity, String webloginUrl) { + if (webloginUrl == null) { + Log.d(TAG, "Null webloginUrl?"); + return; + } + + Log.d(TAG, "Opening login URL in browser: " + webloginUrl); + + Intent viewInBrowser = new Intent(Intent.ACTION_VIEW); + viewInBrowser.setData(Uri.parse(webloginUrl)); + + // Always show the notification + // When this occurs, it can often occur in batches, e.g. if a the user also clicks to view + // comments which results in multiple dev consoles opening in their browser without an + // explanation. This is even worse if they have multiple accounts and/or are currently + // signed in via a different account + Context ctx = AndlyticsApp.getInstance(); + Builder builder = new NotificationCompat.Builder(ctx); + builder.setSmallIcon(R.drawable.statusbar_andlytics); + builder.setContentTitle(ctx.getResources().getString(R.string.auth_error, accountName)); + builder.setContentText(ctx.getResources().getString(R.string.auth_error_open_browser, + accountName)); + builder.setAutoCancel(true); + PendingIntent contentIntent = PendingIntent.getActivity(ctx, accountName.hashCode(), + viewInBrowser, PendingIntent.FLAG_UPDATE_CURRENT); + builder.setContentIntent(contentIntent); + + NotificationManager nm = (NotificationManager) ctx + .getSystemService(Context.NOTIFICATION_SERVICE); + nm.notify(accountName.hashCode(), builder.build()); + } + } diff --git a/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java b/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java index 0746a459..191e8bd4 100644 --- a/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java +++ b/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java @@ -1,18 +1,5 @@ package com.github.andlyticsproject.console.v2; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.apache.http.HttpStatus; -import org.apache.http.client.CookieStore; -import org.apache.http.client.HttpResponseException; -import org.apache.http.client.ResponseHandler; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.cookie.Cookie; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.DefaultHttpClient; - import android.annotation.SuppressLint; import android.app.Activity; import android.util.Log; @@ -27,6 +14,19 @@ import com.github.andlyticsproject.model.DeveloperConsoleAccount; import com.github.andlyticsproject.util.Utils; +import org.apache.http.HttpStatus; +import org.apache.http.client.CookieStore; +import org.apache.http.client.HttpResponseException; +import org.apache.http.client.ResponseHandler; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.cookie.Cookie; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.DefaultHttpClient; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + /** * This is a WIP class representing the new v2 version of the developer console. * The aim is to build it from scratch to make it a light weight and as well @@ -61,7 +61,9 @@ public class DevConsoleV2 implements DevConsole { private ResponseHandler responseHandler = HttpClientFactory.createResponseHandler(); public static DevConsoleV2 createForAccount(String accountName, DefaultHttpClient httpClient) { - DevConsoleAuthenticator authenticator = new AccountManagerAuthenticator(accountName, + // DevConsoleAuthenticator authenticator = new AccountManagerAuthenticator(accountName, + // httpClient); + DevConsoleAuthenticator authenticator = new OauthAccountManagerAuthenticator(accountName, httpClient); return new DevConsoleV2(httpClient, authenticator, new DevConsoleV2Protocol()); diff --git a/src/com/github/andlyticsproject/console/v2/OauthAccountManagerAuthenticator.java b/src/com/github/andlyticsproject/console/v2/OauthAccountManagerAuthenticator.java new file mode 100644 index 00000000..b5c1531d --- /dev/null +++ b/src/com/github/andlyticsproject/console/v2/OauthAccountManagerAuthenticator.java @@ -0,0 +1,225 @@ +package com.github.andlyticsproject.console.v2; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; +import android.app.Activity; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.NotificationCompat; +import android.support.v4.app.NotificationCompat.Builder; +import android.util.Log; + +import com.github.andlyticsproject.AndlyticsApp; +import com.github.andlyticsproject.R; +import com.github.andlyticsproject.console.AuthenticationException; +import com.github.andlyticsproject.console.NetworkException; +import com.github.andlyticsproject.model.DeveloperConsoleAccount; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class OauthAccountManagerAuthenticator extends BaseAuthenticator { + + private static final String TAG = OauthAccountManagerAuthenticator.class.getSimpleName(); + + private static final String DEVELOPER_CONSOLE_URL = "https://play.google.com/apps/publish/"; + + private static final String OAUTH_LOGIN_SCOPE = "oauth2:https://www.google.com/accounts/OAuthLogin"; + private static final String OAUTH_LOGIN_URL = "https://accounts.google.com/OAuthLogin?source=ChromiumBrowser&issueuberauth=1"; + private static final String MERGE_SESSION_URL = "https://accounts.google.com/MergeSession"; + + private static final int REQUEST_AUTHENTICATE = 42; + + private static final boolean DEBUG = false; + + private AccountManager accountManager; + + // includes one-time token + private String webloginUrl; + + private DefaultHttpClient httpClient; + + public OauthAccountManagerAuthenticator(String accountName, DefaultHttpClient httpClient) { + super(accountName); + this.accountManager = AccountManager.get(AndlyticsApp.getInstance()); + this.httpClient = httpClient; + } + + @Override + public SessionCredentials authenticate(Activity activity, boolean invalidate) + throws AuthenticationException { + return authenticateInternal(activity, invalidate); + } + + @Override + public SessionCredentials authenticateSilently(boolean invalidate) + throws AuthenticationException { + return authenticateInternal(null, invalidate); + } + + @SuppressWarnings("deprecation") + private SessionCredentials authenticateInternal(Activity activity, boolean invalidate) + throws AuthenticationException { + try { + Account[] accounts = accountManager.getAccountsByType("com.google"); + Account account = null; + for (Account acc : accounts) { + if (acc.name.equals(accountName)) { + account = acc; + break; + } + } + if (account == null) { + throw new AuthenticationException(String.format("Account %s not found on device?", + accountName)); + } + + if (invalidate && webloginUrl != null) { + // probably not needed, since what we are getting is a very + // short-lived token + accountManager.invalidateAuthToken(account.type, webloginUrl); + } + + Bundle authResult = accountManager.getAuthToken(account, OAUTH_LOGIN_SCOPE, false, + null, null).getResult(); + if (authResult.containsKey(AccountManager.KEY_INTENT)) { + Intent authIntent = authResult.getParcelable(AccountManager.KEY_INTENT); + if (DEBUG) { + Log.w(TAG, "Got a reauthenticate intent: " + authIntent); + } + + // silent mode, show notification + if (activity == null) { + Context ctx = AndlyticsApp.getInstance(); + Builder builder = new NotificationCompat.Builder(ctx); + builder.setSmallIcon(R.drawable.statusbar_andlytics); + builder.setContentTitle(ctx.getResources().getString(R.string.auth_error, + accountName)); + builder.setContentText(ctx.getResources().getString(R.string.auth_error, + accountName)); + builder.setAutoCancel(true); + PendingIntent contentIntent = PendingIntent.getActivity(ctx, + accountName.hashCode(), authIntent, PendingIntent.FLAG_UPDATE_CURRENT); + builder.setContentIntent(contentIntent); + + NotificationManager nm = (NotificationManager) ctx + .getSystemService(Context.NOTIFICATION_SERVICE); + nm.notify(accountName.hashCode(), builder.build()); + + return null; + } + + // activity mode, start activity + authIntent.setFlags(authIntent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK); + activity.startActivityForResult(authIntent, REQUEST_AUTHENTICATE); + + return null; + } + + String oauthLoginToken = authResult.getString(AccountManager.KEY_AUTHTOKEN); + if (oauthLoginToken == null) { + throw new AuthenticationException( + "Unexpected authentication error: weblogin URL = null"); + } + if (DEBUG) { + Log.d(TAG, "OAuth Login token: " + webloginUrl); + } + + HttpGet getOauthUberToken = new HttpGet(OAUTH_LOGIN_URL); + getOauthUberToken.addHeader("Authorization", "OAuth " + oauthLoginToken); + HttpResponse response = httpClient.execute(getOauthUberToken); + int status = response.getStatusLine().getStatusCode(); + if (status == HttpStatus.SC_UNAUTHORIZED) { + throw new AuthenticationException("Cannot get uber token: " + + response.getStatusLine()); + } + String uberToken = EntityUtils.toString(response.getEntity(), "UTF-8"); + Log.d(TAG, "uber token: " + uberToken); + if (uberToken == null || "".equals(uberToken) || uberToken.contains("Error")) { + throw new AuthenticationException("Cannot get uber token. Got: " + uberToken); + } + + HttpPost getConsole = new HttpPost(MERGE_SESSION_URL); + List pairs = new ArrayList(); + pairs.add(new BasicNameValuePair("uberauth", uberToken)); + pairs.add(new BasicNameValuePair("continue", DEVELOPER_CONSOLE_URL)); + pairs.add(new BasicNameValuePair("source", "ChromiumBrowser")); + // for debugging? + webloginUrl = Uri.parse(MERGE_SESSION_URL).buildUpon() + .appendQueryParameter("source", "ChromiumBrowser") + .appendQueryParameter("uberauth", uberToken) + .appendQueryParameter("continue", DEVELOPER_CONSOLE_URL).build().toString(); + Log.d(TAG, "MergeSession URL: " + webloginUrl); + + UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(pairs, "UTF-8"); + getConsole.setEntity(formEntity); + response = httpClient.execute(getConsole); + status = response.getStatusLine().getStatusCode(); + if (status == HttpStatus.SC_UNAUTHORIZED) { + throw new AuthenticationException("Authentication token expired: " + + response.getStatusLine()); + } + if (status != HttpStatus.SC_OK) { + throw new AuthenticationException("Authentication error: " + + response.getStatusLine()); + } + HttpEntity entity = response.getEntity(); + if (entity == null) { + throw new AuthenticationException("Authentication error: null result?"); + } + + String responseStr = EntityUtils.toString(entity, "UTF-8"); + if (DEBUG) { + Log.d(TAG, "Response: " + responseStr); + } + + DeveloperConsoleAccount[] developerAccounts = findDeveloperAccounts(responseStr); + if (developerAccounts == null) { + debugAuthFailure(activity, responseStr, webloginUrl); + + throw new AuthenticationException("Couldn't get developer account ID."); + } + + String xsrfToken = findXsrfToken(responseStr); + if (xsrfToken == null) { + debugAuthFailure(activity, responseStr, webloginUrl); + + throw new AuthenticationException("Couldn't get XSRF token."); + } + + List whitelistedFeatures = findWhitelistedFeatures(responseStr); + + SessionCredentials result = new SessionCredentials(accountName, xsrfToken, + developerAccounts); + result.addCookies(httpClient.getCookieStore().getCookies()); + result.addWhitelistedFeatures(whitelistedFeatures); + + return result; + } catch (IOException e) { + throw new NetworkException(e); + } catch (OperationCanceledException e) { + throw new AuthenticationException(e); + } catch (AuthenticatorException e) { + throw new AuthenticationException(e); + } + } + +} From 8d6e512645507d5a8965ca2bea6e7b1f49a037ad Mon Sep 17 00:00:00 2001 From: Will Lunniss Date: Sun, 16 Feb 2014 21:29:39 +0000 Subject: [PATCH 05/12] Update README.md Add link to alpha/beta versions and note that nightly builds are out of date --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 98913fa1..a10eb5a7 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,18 @@ This repository contains the source code for the Andlytics Android app. ![](https://lh4.ggpht.com/ckVylBqx0sS1b-KW99qkg7NYuDNRGGstnZKsw-qe3TnpUOH4em5cH-8QuPXs2NQj9Nou=w705) + Please see the [issues](https://github.com/AndlyticsProject/andlytics/issues) section to report any bugs or feature requests and to see the list of known issues. +### Alpha/Beta Versions +Join the G+ community and register as a tester to get latest alpha/beta versions: + +https://plus.google.com/communities/106533634605835980810 + +https://play.google.com/apps/testing/com.github.andlyticsproject + + ## License Copyright 2012 Timelappse @@ -50,6 +59,8 @@ Keywords: Developer console, developer tools, android developers ## Nightly builds +Not working, don't use + * http://andlytics.schoentoon.com/andlytics.apk You can check which commit it's build from at http://andlytics.schoentoon.com/latest_version and it'll get updated everynight just past midnight (GMT +1) From a3bf6016b2d00b8c2f8592e77f438d3be95c4fff Mon Sep 17 00:00:00 2001 From: Will Lunniss Date: Sun, 16 Feb 2014 22:18:11 +0000 Subject: [PATCH 06/12] Update README.md --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a10eb5a7..5b60569b 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,12 @@ https://plus.google.com/communities/106533634605835980810 https://play.google.com/apps/testing/com.github.andlyticsproject +### Nightly builds + +* http://andlytics.schoentoon.com/andlytics.apk + +You can check which commit it's build from at http://andlytics.schoentoon.com/latest_version and it'll get updated everynight just past midnight (GMT +1) + ## License @@ -57,13 +63,6 @@ For AdMob accounts that are linked to a Google Account you can find the API pass Keywords: Developer console, developer tools, android developers -## Nightly builds - -Not working, don't use - -* http://andlytics.schoentoon.com/andlytics.apk - -You can check which commit it's build from at http://andlytics.schoentoon.com/latest_version and it'll get updated everynight just past midnight (GMT +1) ## Contributing From 57efaf4839e511ee1dd69e4bf37432bb1c58b3cf Mon Sep 17 00:00:00 2001 From: Nikolay Elenkov Date: Fri, 21 Feb 2014 14:16:19 +0900 Subject: [PATCH 07/12] added back full changelog --- res/raw/changelog.html | 146 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/res/raw/changelog.html b/res/raw/changelog.html index d0307cd9..2b94abb7 100755 --- a/res/raw/changelog.html +++ b/res/raw/changelog.html @@ -68,6 +68,152 @@
            • API update
          • +
          • v2.5.4 +
              +
            • API update
            • +
            +
          • +
          • v2.5.3 +
              +
            • API update
            • +
            • Added character count to developer reply
            • +
            +
          • +
          • v2.5.2 +
              +
            • API update
            • +
            +
          • +
          • v2.5.1 +
              +
            • Fixes authentication issue
            • +
            • Properly handle non-ASCII developer names
            • +
            • Other minor bugfixes
            • +
            +
          • +
          • v2.5.0 +
              +
            • [beta] Multi-linked account support
            • +
            • App Info page (click on the app name)
            • +
            • Auto translation for comments (click on the globe to + toggle)
            • +
            • Developer replies to comments
            • +
            • Redirect to browser if Goolge/play requires data + confirmations like cellphone number
            • +
            +
          • +
          • v2.4.3 +
              +
            • API update
            • +
            +
          • +
          • v2.4.2 +
              +
            • API update
            • +
            • Added "Month to Date" time frame option
            • +
            • Added Swedish translations
            • +
            +
          • +
          • v2.4.1 +
              +
            • API update
            • +
            +
          • +
          • v2.4.0 +
              +
            • Minor bugfixes
            • +
            • Minor UI changes
            • +
            • Google Translate app integrated (see preferences)
            • +
            • Toggle data/graph in actionbar
            • +
            • Separate about screen (credits/changelog)
            • +
            +
          • +
          • v2.3.1 +
              +
            • Only collect statistics on published apps
            • +
            +
          • +
          • v2.3 +
              +
            • Now based on the new Developer Console (The number of + comments may go down due to a change in how they are reported and + you will be re-prompted to let Andlytics access your Google + account)
            • +
            • Statistics and comments are no longer repeatedly synced + while actively using the app
            • +
            • Removed the chart/table configuration panel and replaced it + with action items/preferences
            • +
            • Adjusted charts and tables for tablets
            • +
            • Ratings are displayed as colour coded progress bars
            • +
            • Added a global date format option
            • +
            • Added support for viewing developer replies to comments
            • +
            • Added automatic backup and restore for preferences and app + stats (ICS+)
            • +
            • Removed support for Android 2.1
            • +
            • Updated translations
            • +
            • Updated libraries
            • +
            • Lots of other fixes and improvements
            • +
            • Internal preference and database changes
            • +
            +
          • +
          • v2.2.3 +
              +
            • API update
            • +
            +
          • +
          • v2.2.2 +
              +
            • Honeycomb notification bugfix
            • +
            +
          • +
          • v2.2.1 +
              +
            • Minor bugfixes
            • +
            +
          • +
          • v2.2.0 +
              +
            • New account management, switching account via action bar
            • +
            • New bottom bar for the main actions
            • +
            • New Import/Export with Upload to Dropbox (click the export + notification) and Import via Dropbox (via Dropbox app processing + .zip files)
            • +
            • JellyBean Notifications including custom sounds
            • +
            • General/account specific preferences
            • +
            • AdMob can be hidden via preferences
            • +
            • UI improvements: responsiveness, icon fixes, progress + indicator
            • +
            • New language: Japanese, French
            • +
            +
          • +
          • v2.1.3 +
              +
            • API update
            • +
            +
          • +
          • v2.1.0 +
              +
            • ICS compatible ActionBar
            • +
            • HD icons for xlarge screens
            • +
            • New settings screen
            • +
            • New languages: Catalan, Italian, Russian
            • +
            +
          • +
          • v2.0.1 +
              +
            • HotFix
            • +
            +
          • +
          • v2.0.0 +
              +
            • Fixed Authentication
            • +
            • Pro features are now free (since it is open source now)
            • +
            • overhauled design
            • +
            • EPC for AdMob
            • +
            • New languages: German, Dutch, Spanish
            • +
            +
          • +
        From 529a60fc4f70b350b0a80e96600f4c85f5217ebf Mon Sep 17 00:00:00 2001 From: twig Date: Wed, 5 Mar 2014 14:23:55 +1100 Subject: [PATCH 08/12] improved the time it takes to fetch information when there are multiple apps by fetching the app information in parallel rather than one app at a time. --- .../console/v2/DevConsoleV2.java | 139 +++++++++++------- 1 file changed, 86 insertions(+), 53 deletions(-) diff --git a/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java b/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java index 1fd1146b..2e3190b3 100644 --- a/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java +++ b/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java @@ -1,5 +1,18 @@ package com.github.andlyticsproject.console.v2; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.http.HttpStatus; +import org.apache.http.client.CookieStore; +import org.apache.http.client.HttpResponseException; +import org.apache.http.client.ResponseHandler; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.cookie.Cookie; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.DefaultHttpClient; + import android.annotation.SuppressLint; import android.app.Activity; import android.util.Log; @@ -16,34 +29,21 @@ import com.github.andlyticsproject.model.RevenueSummary; import com.github.andlyticsproject.util.Utils; -import org.apache.http.HttpStatus; -import org.apache.http.client.CookieStore; -import org.apache.http.client.HttpResponseException; -import org.apache.http.client.ResponseHandler; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.cookie.Cookie; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.DefaultHttpClient; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - /** * This is a WIP class representing the new v2 version of the developer console. * The aim is to build it from scratch to make it a light weight and as well * documented at the end as possible. Once it is done and available to all * users, we will rip out the old code and replace it with this. - * + * * Once v2 is available to all users, there is scope for better utilising the * available statistics data, so keep that in mind when developing this class. * For now though, keep it simple and get it working. - * + * * See https://github.com/AndlyticsProject/andlytics/wiki/Developer-Console-v2 * for some more documentation - * + * * This class fetches the data, which is then passed using {@link JsonParser} - * + * */ @SuppressLint("DefaultLocale") public class DevConsoleV2 implements DevConsole { @@ -62,6 +62,9 @@ public class DevConsoleV2 implements DevConsole { private ResponseHandler responseHandler = HttpClientFactory.createResponseHandler(); + // This is only used for synchronising fetchAppInfosAndStatistics() + private volatile int fetchAppInfoCounter; + public static DevConsoleV2 createForAccount(String accountName, DefaultHttpClient httpClient) { DevConsoleAuthenticator authenticator = new OauthAccountManagerAuthenticator(accountName, httpClient); @@ -87,7 +90,7 @@ private DevConsoleV2(DefaultHttpClient httpClient, DevConsoleAuthenticator authe /** * Gets a list of available apps for the given account - * + * * @param activity * @return * @throws DevConsoleException @@ -109,39 +112,69 @@ public synchronized List getAppInfo(Activity activity) throws DevConsol } } + private List fetchAppInfosAndStatistics() { // Fetch a list of available apps List apps = fetchAppInfos(); + int total = apps.size(); + final Object lock = new Object(); + fetchAppInfoCounter = 0; + + for (final AppInfo app : apps) { + /* + * Generate a new thread for each app. This removes the queueing behaviour so requests + * are made in parallel. + */ + new Thread(new Runnable() { + @Override + public void run() { + // Fetch remaining app statistics + // Latest stats object, and active/total installs is fetched + // in fetchAppInfos + AppStats stats = app.getLatestStats(); + fetchRatings(app, stats); + stats.setNumberOfComments(fetchCommentsCount(app, Utils.getDisplayLocale())); + + RevenueSummary revenue = fetchRevenueSummary(app); + app.setTotalRevenueSummary(revenue); + // this is currently the same as the last item of the historical + // data, so save some cycles and don't parse historical + // XXX the definition of 'last day' is unclear: GMT? + if (revenue != null) { + stats.setTotalRevenue(revenue.getLastDay()); + } + + // only works on 11+ + // XXX the latest recorded revenue is not necessarily today's + // revenue. Check the date and do something clever or revise + // how of all this is stored? + + // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + // Revenue latestRevenue = fetchLatestTotalRevenue(app); + // if (latestRevenue != null) { + // stats.setTotalRevenue(latestRevenue.getAmount()); + // } + // } + + + // Increment the number of successful fetches, then wake the lock. + synchronized (lock) { + fetchAppInfoCounter++; + lock.notify(); + } + } + }).start(); + } - for (AppInfo app : apps) { - // Fetch remaining app statistics - // Latest stats object, and active/total installs is fetched - // in fetchAppInfos - AppStats stats = app.getLatestStats(); - fetchRatings(app, stats); - stats.setNumberOfComments(fetchCommentsCount(app, Utils.getDisplayLocale())); - - RevenueSummary revenue = fetchRevenueSummary(app); - app.setTotalRevenueSummary(revenue); - // this is currently the same as the last item of the historical - // data, so save some cycles and don't parse historical - // XXX the definition of 'last day' is unclear: GMT? - if (revenue != null) { - stats.setTotalRevenue(revenue.getLastDay()); - } - // only works on 11+ - // XXX the latest recorded revenue is not necessarily today's - // revenue. Check the date and do something clever or revise - // how of all this is stored? - - // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - // Revenue latestRevenue = fetchLatestTotalRevenue(app); - // if (latestRevenue != null) { - // stats.setTotalRevenue(latestRevenue.getAmount()); - // } - // } - } + // Wait for lock to be triggered + synchronized (lock) { + // Keep waiting for the other threads to finish + while (fetchAppInfoCounter < total) { + try { lock.wait(); } catch (InterruptedException e) { } + Log.e("count", String.valueOf(fetchAppInfoCounter)); + } + } return apps; } @@ -149,7 +182,7 @@ private List fetchAppInfosAndStatistics() { /** * Gets a list of comments for the given app based on the startIndex and * count - * + * * @param accountName * @param packageName * @param startIndex @@ -203,7 +236,7 @@ private Comment replyToComment(String packageName, String developerId, String co /** * Fetches a combined list of apps for all avaiable console accounts - * + * * @return combined list of apps * @throws DevConsoleException */ @@ -262,10 +295,10 @@ private List fetchAppInfos() throws DevConsoleException { /** * Fetches statistics for the given packageName of the given statsType and * adds them to the given {@link AppStats} object - * + * * This is not used as statistics can be fetched via fetchAppInfos Can use * it later to get historical etc data - * + * * @param packageName * @param stats * @param statsType @@ -283,7 +316,7 @@ private void fetchStatistics(AppInfo appInfo, AppStats stats, int statsType) /** * Fetches ratings for the given packageName and adds them to the given {@link AppStats} object - * + * * @param packageName * The app to fetch ratings for * @param stats @@ -299,7 +332,7 @@ private void fetchRatings(AppInfo appInfo, AppStats stats) throws DevConsoleExce /** * Fetches the number of comments for the given packageName - * + * * @param packageName * @return * @throws DevConsoleException @@ -396,7 +429,7 @@ private boolean authenticateFromScratch(Activity activity) { /** * Logs into the Android Developer Console - * + * * @param reuseAuthentication * @throws DevConsoleException */ From fc00f8eff2daa6d9a7eb0eb9097118b3c0c50b19 Mon Sep 17 00:00:00 2001 From: twig Date: Wed, 5 Mar 2014 14:30:12 +1100 Subject: [PATCH 09/12] removed debug print. --- src/com/github/andlyticsproject/console/v2/DevConsoleV2.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java b/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java index 2e3190b3..1961a16b 100644 --- a/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java +++ b/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java @@ -172,7 +172,6 @@ public void run() { // Keep waiting for the other threads to finish while (fetchAppInfoCounter < total) { try { lock.wait(); } catch (InterruptedException e) { } - Log.e("count", String.valueOf(fetchAppInfoCounter)); } } From 3d9d2fa384a43c6efd521063e0bd93c45d558452 Mon Sep 17 00:00:00 2001 From: twig Date: Thu, 6 Mar 2014 14:15:38 +1100 Subject: [PATCH 10/12] - switched locking method to CountDownLatch - wrapped code inside of a try/finally block to prevent errors holding locks forever --- .../console/v2/DevConsoleV2.java | 92 +++++++++++-------- 1 file changed, 52 insertions(+), 40 deletions(-) diff --git a/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java b/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java index 1961a16b..f410915b 100644 --- a/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java +++ b/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CountDownLatch; import org.apache.http.HttpStatus; import org.apache.http.client.CookieStore; @@ -63,7 +64,7 @@ public class DevConsoleV2 implements DevConsole { private ResponseHandler responseHandler = HttpClientFactory.createResponseHandler(); // This is only used for synchronising fetchAppInfosAndStatistics() - private volatile int fetchAppInfoCounter; + private CountDownLatch fetchAppInfoCounter; public static DevConsoleV2 createForAccount(String accountName, DefaultHttpClient httpClient) { DevConsoleAuthenticator authenticator = new OauthAccountManagerAuthenticator(accountName, @@ -116,9 +117,7 @@ public synchronized List getAppInfo(Activity activity) throws DevConsol private List fetchAppInfosAndStatistics() { // Fetch a list of available apps List apps = fetchAppInfos(); - int total = apps.size(); - final Object lock = new Object(); - fetchAppInfoCounter = 0; + fetchAppInfoCounter = new CountDownLatch(apps.size()); for (final AppInfo app : apps) { /* @@ -128,39 +127,49 @@ private List fetchAppInfosAndStatistics() { new Thread(new Runnable() { @Override public void run() { - // Fetch remaining app statistics - // Latest stats object, and active/total installs is fetched - // in fetchAppInfos - AppStats stats = app.getLatestStats(); - fetchRatings(app, stats); - stats.setNumberOfComments(fetchCommentsCount(app, Utils.getDisplayLocale())); - - RevenueSummary revenue = fetchRevenueSummary(app); - app.setTotalRevenueSummary(revenue); - // this is currently the same as the last item of the historical - // data, so save some cycles and don't parse historical - // XXX the definition of 'last day' is unclear: GMT? - if (revenue != null) { - stats.setTotalRevenue(revenue.getLastDay()); + try { + Log.d("start app", app.getName()); + + // Fetch remaining app statistics + // Latest stats object, and active/total installs is fetched + // in fetchAppInfos + AppStats stats = app.getLatestStats(); + fetchRatings(app, stats); + stats.setNumberOfComments(fetchCommentsCount(app, Utils.getDisplayLocale())); + + RevenueSummary revenue = fetchRevenueSummary(app); + app.setTotalRevenueSummary(revenue); + // this is currently the same as the last item of the historical + // data, so save some cycles and don't parse historical + // XXX the definition of 'last day' is unclear: GMT? + if (revenue != null) { + stats.setTotalRevenue(revenue.getLastDay()); + } + + // only works on 11+ + // XXX the latest recorded revenue is not necessarily today's + // revenue. Check the date and do something clever or revise + // how of all this is stored? + + // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + // Revenue latestRevenue = fetchLatestTotalRevenue(app); + // if (latestRevenue != null) { + // stats.setTotalRevenue(latestRevenue.getAmount()); + // } + // } + + Log.d("end app", app.getName()); } + finally { + Log.w("finally app", app.getName()); - // only works on 11+ - // XXX the latest recorded revenue is not necessarily today's - // revenue. Check the date and do something clever or revise - // how of all this is stored? + // Increment the number of successful fetches, then wake the lock. + synchronized (fetchAppInfoCounter) { + Log.e("fetch counter", String.valueOf(fetchAppInfoCounter.getCount())); + fetchAppInfoCounter.countDown(); - // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - // Revenue latestRevenue = fetchLatestTotalRevenue(app); - // if (latestRevenue != null) { - // stats.setTotalRevenue(latestRevenue.getAmount()); - // } - // } - - - // Increment the number of successful fetches, then wake the lock. - synchronized (lock) { - fetchAppInfoCounter++; - lock.notify(); + Log.w("finally count", String.valueOf(fetchAppInfoCounter.getCount())); + } } } }).start(); @@ -168,12 +177,15 @@ public void run() { // Wait for lock to be triggered - synchronized (lock) { - // Keep waiting for the other threads to finish - while (fetchAppInfoCounter < total) { - try { lock.wait(); } catch (InterruptedException e) { } - } - } + // Keep waiting for the other threads to finish + try { + Log.e("Waiting", "waiting..."); + fetchAppInfoCounter.await(); + } + catch (InterruptedException e) { + } + + Log.e("Waiting", "DONE!"); return apps; } From 2bfc2790e65b180ce4595894fb7ef7eb82d153c2 Mon Sep 17 00:00:00 2001 From: twig Date: Thu, 6 Mar 2014 14:27:07 +1100 Subject: [PATCH 11/12] - pool the fetchAppInfosAndStatistics() threads by 5 --- .../github/andlyticsproject/console/v2/DevConsoleV2.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java b/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java index f410915b..123b770a 100644 --- a/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java +++ b/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java @@ -4,6 +4,8 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import org.apache.http.HttpStatus; import org.apache.http.client.CookieStore; @@ -116,6 +118,7 @@ public synchronized List getAppInfo(Activity activity) throws DevConsol private List fetchAppInfosAndStatistics() { // Fetch a list of available apps + ExecutorService executor = Executors.newFixedThreadPool(5); // Only process 5 at a time List apps = fetchAppInfos(); fetchAppInfoCounter = new CountDownLatch(apps.size()); @@ -124,7 +127,7 @@ private List fetchAppInfosAndStatistics() { * Generate a new thread for each app. This removes the queueing behaviour so requests * are made in parallel. */ - new Thread(new Runnable() { + Thread thread = new Thread(new Runnable() { @Override public void run() { try { @@ -172,7 +175,9 @@ public void run() { } } } - }).start(); + }); + + executor.execute(thread); } From bc1a298c52d4a6497dcd491f4c77809ed17aacb0 Mon Sep 17 00:00:00 2001 From: twig Date: Thu, 13 Mar 2014 16:56:44 +1100 Subject: [PATCH 12/12] - do 10 requests at a time --- src/com/github/andlyticsproject/console/v2/DevConsoleV2.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java b/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java index 123b770a..4b4c9e52 100644 --- a/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java +++ b/src/com/github/andlyticsproject/console/v2/DevConsoleV2.java @@ -118,7 +118,7 @@ public synchronized List getAppInfo(Activity activity) throws DevConsol private List fetchAppInfosAndStatistics() { // Fetch a list of available apps - ExecutorService executor = Executors.newFixedThreadPool(5); // Only process 5 at a time + ExecutorService executor = Executors.newFixedThreadPool(10); // Only process 10 at a time List apps = fetchAppInfos(); fetchAppInfoCounter = new CountDownLatch(apps.size());