From 9836eb67b2a7ef9932c20566092fc55534d5d2cd Mon Sep 17 00:00:00 2001 From: Sergey Eremin Date: Thu, 9 Mar 2017 18:44:59 +0300 Subject: [PATCH] Token dispenser extracted to play-store-api, wrapper builder introduced --- app/build.gradle | 2 +- .../yalpstore/AccountTypeDialogBuilder.java | 3 +- .../yalpstore/CredentialsDialogBuilder.java | 1 + .../yalpstore/GoogleApiAsyncTask.java | 2 +- .../yalpstore/PlayStoreApiAuthenticator.java | 93 +++++++++++++++ .../yalpstore/PlayStoreApiWrapper.java | 112 +++--------------- .../yeriomin/yalpstore/TokenDispenser.java | 64 ---------- .../yalpstore/TokenDispenserException.java | 14 --- .../UserProvidedAccountDialogBuilder.java | 5 +- .../yeriomin/yalpstore/YalpStoreActivity.java | 2 +- 10 files changed, 114 insertions(+), 184 deletions(-) create mode 100644 app/src/main/java/com/github/yeriomin/yalpstore/PlayStoreApiAuthenticator.java delete mode 100644 app/src/main/java/com/github/yeriomin/yalpstore/TokenDispenser.java delete mode 100644 app/src/main/java/com/github/yeriomin/yalpstore/TokenDispenserException.java diff --git a/app/build.gradle b/app/build.gradle index 6d45a0a39..3906a3cf7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,6 +24,6 @@ android { } dependencies { - compile 'com.github.yeriomin:play-store-api:7c9171e' + compile 'com.github.yeriomin:play-store-api:73a06ec' compile 'eu.chainfire:libsuperuser:1.0.0.201608240809' } diff --git a/app/src/main/java/com/github/yeriomin/yalpstore/AccountTypeDialogBuilder.java b/app/src/main/java/com/github/yeriomin/yalpstore/AccountTypeDialogBuilder.java index 36161f73c..24695daea 100644 --- a/app/src/main/java/com/github/yeriomin/yalpstore/AccountTypeDialogBuilder.java +++ b/app/src/main/java/com/github/yeriomin/yalpstore/AccountTypeDialogBuilder.java @@ -61,8 +61,7 @@ public class AccountTypeDialogBuilder extends CredentialsDialogBuilder { @Override protected Throwable doInBackground(String[] params) { try { - PlayStoreApiWrapper wrapper = new PlayStoreApiWrapper(context); - wrapper.login(params[0]); + new PlayStoreApiAuthenticator(context).login(params[0]); } catch (Throwable e) { return e; } diff --git a/app/src/main/java/com/github/yeriomin/yalpstore/CredentialsDialogBuilder.java b/app/src/main/java/com/github/yeriomin/yalpstore/CredentialsDialogBuilder.java index 8f296afa5..53715b8aa 100644 --- a/app/src/main/java/com/github/yeriomin/yalpstore/CredentialsDialogBuilder.java +++ b/app/src/main/java/com/github/yeriomin/yalpstore/CredentialsDialogBuilder.java @@ -10,6 +10,7 @@ import android.util.Log; import android.widget.Toast; import com.github.yeriomin.playstoreapi.AuthException; +import com.github.yeriomin.playstoreapi.TokenDispenserException; import java.io.IOException; diff --git a/app/src/main/java/com/github/yeriomin/yalpstore/GoogleApiAsyncTask.java b/app/src/main/java/com/github/yeriomin/yalpstore/GoogleApiAsyncTask.java index 76f69f0b8..0bfe14473 100644 --- a/app/src/main/java/com/github/yeriomin/yalpstore/GoogleApiAsyncTask.java +++ b/app/src/main/java/com/github/yeriomin/yalpstore/GoogleApiAsyncTask.java @@ -81,7 +81,7 @@ abstract class GoogleApiAsyncTask extends AsyncTask { Log.w(getClass().getName(), "Credentials empty"); } else { toast(this.context, R.string.error_incorrect_password); - new PlayStoreApiWrapper(this.context).forceTokenRefresh(); + new PlayStoreApiAuthenticator(context).logout(); } AccountTypeDialogBuilder builder = new AccountTypeDialogBuilder(this.context); builder.setTaskClone(this.taskClone); diff --git a/app/src/main/java/com/github/yeriomin/yalpstore/PlayStoreApiAuthenticator.java b/app/src/main/java/com/github/yeriomin/yalpstore/PlayStoreApiAuthenticator.java new file mode 100644 index 000000000..a50297cb4 --- /dev/null +++ b/app/src/main/java/com/github/yeriomin/yalpstore/PlayStoreApiAuthenticator.java @@ -0,0 +1,93 @@ +package com.github.yeriomin.yalpstore; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +import com.github.yeriomin.playstoreapi.ApiBuilderException; +import com.github.yeriomin.playstoreapi.GooglePlayAPI; + +import java.io.IOException; +import java.util.Locale; + +public class PlayStoreApiAuthenticator { + + private Context context; + + private static GooglePlayAPI api; + + public PlayStoreApiAuthenticator(Context context) { + this.context = context; + } + + public GooglePlayAPI getApi() throws IOException { + if (api == null) { + api = build(); + } + return api; + } + + public void login(String email) throws IOException { + build(email); + } + + public void login(String email, String password) throws IOException { + build(email, password); + } + + public void logout() { + SharedPreferences.Editor prefs = PreferenceManager.getDefaultSharedPreferences(context).edit(); + prefs.remove(PreferenceActivity.PREFERENCE_EMAIL); + prefs.remove(PreferenceActivity.PREFERENCE_AUTH_TOKEN); + prefs.apply(); + api = null; + } + + private GooglePlayAPI build() throws IOException { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + String email = prefs.getString(PreferenceActivity.PREFERENCE_EMAIL, ""); + return build(email); + } + + private GooglePlayAPI build(String email) throws IOException { + return build(email, null); + } + + private GooglePlayAPI build(String email, String password) throws IOException { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + String gsfId = prefs.getString(PreferenceActivity.PREFERENCE_GSF_ID, ""); + String token = prefs.getString(PreferenceActivity.PREFERENCE_AUTH_TOKEN, ""); + if (email.isEmpty()) { + throw new CredentialsEmptyException(); + } + + NativeDeviceInfoProvider deviceInfoProvider = new NativeDeviceInfoProvider(); + deviceInfoProvider.setContext(context); + deviceInfoProvider.setLocaleString(Locale.getDefault().toString()); + com.github.yeriomin.playstoreapi.PlayStoreApiBuilder builder = new com.github.yeriomin.playstoreapi.PlayStoreApiBuilder() + .setDeviceInfoProvider(deviceInfoProvider) + .setEmail(email) + ; + if (null != password) { + builder.setPassword(password); + } + if (!gsfId.isEmpty()) { + builder.setGsfId(gsfId); + } + if (!token.isEmpty()) { + builder.setToken(token); + } + try { + api = builder.build(); + } catch (ApiBuilderException e) { + // Should not happen + } + + SharedPreferences.Editor prefsEditor = prefs.edit(); + prefsEditor.putString(PreferenceActivity.PREFERENCE_EMAIL, email); + prefsEditor.putString(PreferenceActivity.PREFERENCE_GSF_ID, gsfId); + prefsEditor.putString(PreferenceActivity.PREFERENCE_AUTH_TOKEN, token); + prefsEditor.apply(); + return api; + } +} diff --git a/app/src/main/java/com/github/yeriomin/yalpstore/PlayStoreApiWrapper.java b/app/src/main/java/com/github/yeriomin/yalpstore/PlayStoreApiWrapper.java index 5072bdf1f..e9b238bf7 100644 --- a/app/src/main/java/com/github/yeriomin/yalpstore/PlayStoreApiWrapper.java +++ b/app/src/main/java/com/github/yeriomin/yalpstore/PlayStoreApiWrapper.java @@ -28,7 +28,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; /** @@ -42,64 +41,10 @@ public class PlayStoreApiWrapper { private static final String BACKEND_DOCID_SIMILAR_APPS = "similar_apps"; private static final String BACKEND_DOCID_USERS_ALSO_INSTALLED = "users_also_installed"; - private Context context; - private String email; - private String password; - - private static GooglePlayAPI api; private static AppSearchResultIterator searchResultIterator; private static CategoryAppsIterator categoryAppsIterator; - private GooglePlayAPI getApi() throws IOException { - if (api == null) { - api = buildApi(); - } - return api; - } - - private GooglePlayAPI constructApi() throws IOException { - NativeDeviceInfoProvider deviceInfoProvider = new NativeDeviceInfoProvider(); - deviceInfoProvider.setContext(context); - deviceInfoProvider.setLocaleString(Locale.getDefault().toString()); - GooglePlayAPI api = new GooglePlayAPI(email); - api.setDeviceInfoProvider(deviceInfoProvider); - api.setLocale(Locale.getDefault()); - return api; - } - - private GooglePlayAPI buildApi() throws IOException { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - String email = this.email == null ? prefs.getString(PreferenceActivity.PREFERENCE_EMAIL, "") : this.email; - String gsfId = prefs.getString(PreferenceActivity.PREFERENCE_GSF_ID, ""); - String token = prefs.getString(PreferenceActivity.PREFERENCE_AUTH_TOKEN, ""); - if (email.isEmpty()) { - throw new CredentialsEmptyException(); - } - - GooglePlayAPI api = constructApi(); - - SharedPreferences.Editor prefsEditor = prefs.edit(); - boolean needToUploadDeviceConfig = false; - if (gsfId.isEmpty()) { - needToUploadDeviceConfig = true; - String ac2dmToken = null == password ? TokenDispenser.getTokenAc2dm(email) : api.getAC2DMToken(password); - gsfId = api.getGsfId(ac2dmToken); - prefsEditor.putString(PreferenceActivity.PREFERENCE_GSF_ID, gsfId); - prefsEditor.apply(); - } - api.setGsfId(gsfId); - if (token.isEmpty()) { - token = null == password ? TokenDispenser.getToken(email) : api.getToken(password); - prefsEditor.putString(PreferenceActivity.PREFERENCE_EMAIL, email); - prefsEditor.putString(PreferenceActivity.PREFERENCE_AUTH_TOKEN, token); - prefsEditor.apply(); - } - api.setToken(token); - if (needToUploadDeviceConfig) { - api.uploadDeviceConfig(); - } - return api; - } + private Context context; public PlayStoreApiWrapper(Context context) { this.context = context; @@ -107,39 +52,9 @@ public class PlayStoreApiWrapper { AppBuilder.suffixBil = context.getString(R.string.suffix_billion); } - public GooglePlayAPI login(String email) throws IOException { - this.email = email; - PlayStoreApiWrapper.api = null; - return getApi(); - } - - public GooglePlayAPI login(String email, String password) throws IOException { - this.password = password; - return login(email); - } - - public void logout() { - this.email = null; - this.password = null; - SharedPreferences.Editor prefs = PreferenceManager.getDefaultSharedPreferences(context).edit(); - prefs.remove(PreferenceActivity.PREFERENCE_EMAIL); - prefs.remove(PreferenceActivity.PREFERENCE_GSF_ID); - prefs.remove(PreferenceActivity.PREFERENCE_AUTH_TOKEN); - prefs.apply(); - PlayStoreApiWrapper.api = null; - } - - public void forceTokenRefresh() { - this.password = null; - SharedPreferences.Editor prefs = PreferenceManager.getDefaultSharedPreferences(context).edit(); - prefs.remove(PreferenceActivity.PREFERENCE_AUTH_TOKEN); - prefs.apply(); - PlayStoreApiWrapper.api = null; - } - public List getReviews(String packageId, int offset, int numberOfResults) throws IOException { List reviews = new ArrayList<>(); - for (com.github.yeriomin.playstoreapi.Review review: getApi().reviews( + for (com.github.yeriomin.playstoreapi.Review review: new PlayStoreApiAuthenticator(context).getApi().reviews( packageId, GooglePlayAPI.REVIEW_SORT.HELPFUL, offset, @@ -151,7 +66,7 @@ public class PlayStoreApiWrapper { } public Review addOrEditReview(String packageId, Review inputReview) throws IOException { - ReviewResponse response = getApi().addOrEditReview( + ReviewResponse response = new PlayStoreApiAuthenticator(context).getApi().addOrEditReview( packageId, inputReview.getComment(), inputReview.getTitle(), @@ -161,11 +76,11 @@ public class PlayStoreApiWrapper { } public void deleteReview(String packageId) throws IOException { - getApi().deleteReview(packageId); + new PlayStoreApiAuthenticator(context).getApi().deleteReview(packageId); } public App getDetails(String packageId) throws IOException { - DetailsResponse response = getApi().details(packageId); + DetailsResponse response = new PlayStoreApiAuthenticator(context).getApi().details(packageId); App app = AppBuilder.build(response.getDocV2()); if (response.hasUserReview()) { app.setUserReview(ReviewBuilder.build(response.getUserReview())); @@ -192,7 +107,7 @@ public class PlayStoreApiWrapper { List apps = new ArrayList<>(); int i = -1; boolean hideNonFree = hideNonFree(); - for (BulkDetailsEntry details: getApi().bulkDetails(packageIds).getEntryList()) { + for (BulkDetailsEntry details: new PlayStoreApiAuthenticator(context).getApi().bulkDetails(packageIds).getEntryList()) { i++; if (!details.hasDoc()) { Log.i(this.getClass().getName(), "Empty response for " + packageIds.get(i)); @@ -222,7 +137,7 @@ public class PlayStoreApiWrapper { || !searchResultIterator.getQuery().equals(query) || !searchResultIterator.getCategoryId().equals(categoryId) ) { - searchResultIterator = new AppSearchResultIterator(new SearchIterator(getApi(), query)); + searchResultIterator = new AppSearchResultIterator(new SearchIterator(new PlayStoreApiAuthenticator(context).getApi(), query)); searchResultIterator.setHideNonfreeApps(hideNonFree()); searchResultIterator.setCategoryId(categoryId); } @@ -235,7 +150,7 @@ public class PlayStoreApiWrapper { ) { categoryAppsIterator = new CategoryAppsIterator( new com.github.yeriomin.playstoreapi.CategoryAppsIterator( - getApi(), + new PlayStoreApiAuthenticator(context).getApi(), categoryId, GooglePlayAPI.SUBCATEGORY.TOP_FREE ) @@ -246,18 +161,18 @@ public class PlayStoreApiWrapper { public List getSearchSuggestions(String query) throws IOException { List suggestions = new ArrayList<>(); - for (SearchSuggestEntry suggestion: api.searchSuggest(query).getEntryList()) { + for (SearchSuggestEntry suggestion: new PlayStoreApiAuthenticator(context).getApi().searchSuggest(query).getEntryList()) { suggestions.add(suggestion.getSuggestedQuery()); } return suggestions; } public Map getCategories() throws IOException { - return buildCategoryMap(getApi().categories()); + return buildCategoryMap(new PlayStoreApiAuthenticator(context).getApi().categories()); } public Map getCategories(String category) throws IOException { - return buildCategoryMap(getApi().categories(category)); + return buildCategoryMap(new PlayStoreApiAuthenticator(context).getApi().categories(category)); } private Map buildCategoryMap(BrowseResponse response) { @@ -274,12 +189,13 @@ public class PlayStoreApiWrapper { public AndroidAppDeliveryData purchaseOrDeliver(App app) throws IOException, NotPurchasedException { if (app.isFree()) { - return getApi() + return new PlayStoreApiAuthenticator(context).getApi() .purchase(app.getPackageName(), app.getVersionCode(), app.getOfferType()) .getPurchaseStatusResponse() .getAppDeliveryData(); } - DeliveryResponse response = getApi().delivery(app.getPackageName(), app.getVersionCode(), app.getOfferType()); + DeliveryResponse response = new PlayStoreApiAuthenticator(context).getApi().delivery( + app.getPackageName(), app.getVersionCode(), app.getOfferType()); if (response.hasAppDeliveryData()) { return response.getAppDeliveryData(); } else { diff --git a/app/src/main/java/com/github/yeriomin/yalpstore/TokenDispenser.java b/app/src/main/java/com/github/yeriomin/yalpstore/TokenDispenser.java deleted file mode 100644 index 5b789d5fa..000000000 --- a/app/src/main/java/com/github/yeriomin/yalpstore/TokenDispenser.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.github.yeriomin.yalpstore; - -import android.util.Log; - -import com.github.yeriomin.playstoreapi.AuthException; - -import java.io.IOException; -import java.util.concurrent.TimeUnit; - -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; - -public class TokenDispenser { - - static private final String DISPENSER_URL = "http://tokendispenser-yeriomin.rhcloud.com/"; - - static private final String RESOURCE_TOKEN = "token"; - static private final String RESOURCE_TOKEN_AC2DM = "token-ac2dm"; - - static private final String PARAMETER_EMAIL = "email"; - - static public String getToken(String email) throws IOException { - return request(getUrl(email, RESOURCE_TOKEN)); - } - - static public String getTokenAc2dm(String email) throws IOException { - return request(getUrl(email, RESOURCE_TOKEN_AC2DM)); - } - - static private HttpUrl getUrl(String email, String path) { - HttpUrl.Builder urlBuilder = HttpUrl.parse(DISPENSER_URL).newBuilder(); - urlBuilder.addPathSegment(path); - urlBuilder.addPathSegment(PARAMETER_EMAIL); - urlBuilder.addPathSegment(email); - return urlBuilder.build(); - } - - static private String request(HttpUrl url) throws IOException { - Log.i(TokenDispenser.class.getName(), "Requesting " + url.toString()); - Request.Builder requestBuilder = new Request.Builder(); - Request request = requestBuilder.url(url).build(); - OkHttpClient.Builder builder = new OkHttpClient.Builder(); - OkHttpClient client = builder - .connectTimeout(1, TimeUnit.MINUTES) - .readTimeout(1, TimeUnit.MINUTES) - .writeTimeout(1, TimeUnit.MINUTES) - .build(); - Response response; - try { - response = client.newCall(request).execute(); - } catch (Throwable e) { - throw new TokenDispenserException(e); - } - int code = response.code(); - if (code == 401 || code == 403) { - throw new AuthException("TokenDispenser failed to get a token"); - } else if (code >= 400) { - throw new TokenDispenserException("Error " + code); - } - return new String(response.body().bytes()); - } -} diff --git a/app/src/main/java/com/github/yeriomin/yalpstore/TokenDispenserException.java b/app/src/main/java/com/github/yeriomin/yalpstore/TokenDispenserException.java deleted file mode 100644 index fa289f2ac..000000000 --- a/app/src/main/java/com/github/yeriomin/yalpstore/TokenDispenserException.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.github.yeriomin.yalpstore; - -import java.io.IOException; - -public class TokenDispenserException extends IOException { - - public TokenDispenserException(String message) { - super(message); - } - - public TokenDispenserException(Throwable cause) { - super(cause); - } -} diff --git a/app/src/main/java/com/github/yeriomin/yalpstore/UserProvidedAccountDialogBuilder.java b/app/src/main/java/com/github/yeriomin/yalpstore/UserProvidedAccountDialogBuilder.java index 55dac7675..1d1d8ae23 100644 --- a/app/src/main/java/com/github/yeriomin/yalpstore/UserProvidedAccountDialogBuilder.java +++ b/app/src/main/java/com/github/yeriomin/yalpstore/UserProvidedAccountDialogBuilder.java @@ -82,13 +82,12 @@ public class UserProvidedAccountDialogBuilder extends CredentialsDialogBuilder { || params[1] == null || params[0].isEmpty() || params[1].isEmpty() - ) { + ) { return new CredentialsEmptyException(); } previousEmail = params[0]; try { - PlayStoreApiWrapper wrapper = new PlayStoreApiWrapper(this.context); - wrapper.login(params[0], params[1]); + new PlayStoreApiAuthenticator(context).login(params[0], params[1]); } catch (Throwable e) { return e; } diff --git a/app/src/main/java/com/github/yeriomin/yalpstore/YalpStoreActivity.java b/app/src/main/java/com/github/yeriomin/yalpstore/YalpStoreActivity.java index 3a862bc44..939c5140b 100644 --- a/app/src/main/java/com/github/yeriomin/yalpstore/YalpStoreActivity.java +++ b/app/src/main/java/com/github/yeriomin/yalpstore/YalpStoreActivity.java @@ -77,7 +77,7 @@ public abstract class YalpStoreActivity extends Activity { .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { - new PlayStoreApiWrapper(getApplicationContext()).logout(); + new PlayStoreApiAuthenticator(getApplicationContext()).logout(); dialogInterface.dismiss(); finishAll(); }