Token dispenser extracted to play-store-api, wrapper builder introduced

This commit is contained in:
Sergey Eremin
2017-03-09 18:44:59 +03:00
parent a790d9847e
commit 9836eb67b2
10 changed files with 114 additions and 184 deletions

View File

@@ -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'
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -81,7 +81,7 @@ abstract class GoogleApiAsyncTask extends AsyncTask<String, Void, Throwable> {
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);

View File

@@ -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;
}
}

View File

@@ -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<Review> getReviews(String packageId, int offset, int numberOfResults) throws IOException {
List<Review> 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<App> 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<String> getSearchSuggestions(String query) throws IOException {
List<String> 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<String, String> getCategories() throws IOException {
return buildCategoryMap(getApi().categories());
return buildCategoryMap(new PlayStoreApiAuthenticator(context).getApi().categories());
}
public Map<String, String> getCategories(String category) throws IOException {
return buildCategoryMap(getApi().categories(category));
return buildCategoryMap(new PlayStoreApiAuthenticator(context).getApi().categories(category));
}
private Map<String, String> 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 {

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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();
}