mirror of
https://github.com/whyorean/AuroraStore.git
synced 2026-06-16 03:31:02 -04:00
Moved former google-play-crawler to a separate library
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'com.google.protobuf'
|
||||
|
||||
android {
|
||||
compileSdkVersion 25
|
||||
@@ -21,50 +20,8 @@ android {
|
||||
lintOptions {
|
||||
disable 'GoogleAppIndexingWarning','GoogleAppIndexingApiWarning'
|
||||
}
|
||||
sourceSets {
|
||||
debug {
|
||||
java {
|
||||
srcDirs 'src/main/java', "${buildDir}/generated/source/proto/debug/javalite"
|
||||
}
|
||||
}
|
||||
release {
|
||||
java {
|
||||
srcDirs 'src/main/java', "${buildDir}/generated/source/proto/release/javalite"
|
||||
}
|
||||
}
|
||||
main {
|
||||
proto {
|
||||
srcDir 'src/main/proto'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile group: 'com.google.protobuf', name: 'protobuf-lite', version: '3.0.0'
|
||||
compile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.5.0'
|
||||
compile 'com.github.yeriomin:play-store-api:2eee3e034b'
|
||||
}
|
||||
|
||||
protobuf {
|
||||
protoc {
|
||||
artifact = 'com.google.protobuf:protoc:3.0.0'
|
||||
}
|
||||
plugins {
|
||||
grpc {
|
||||
artifact = 'io.grpc:protoc-gen-grpc-java:1.0.1'
|
||||
}
|
||||
javalite {
|
||||
artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0'
|
||||
}
|
||||
}
|
||||
generateProtoTasks {
|
||||
all()*.plugins {
|
||||
javalite { }
|
||||
}
|
||||
ofNonTest()*.plugins {
|
||||
grpc {
|
||||
option 'lite'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package com.github.yeriomin.playstoreapi;
|
||||
|
||||
public interface DeviceInfoProvider {
|
||||
|
||||
AndroidCheckinRequest generateAndroidCheckinRequest();
|
||||
DeviceConfigurationProto getDeviceConfigurationProto();
|
||||
String getUserAgentString();
|
||||
int getSdkVersion();
|
||||
}
|
||||
@@ -1,436 +0,0 @@
|
||||
package com.github.yeriomin.playstoreapi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* This class provides
|
||||
* <code>checkin, search, details, bulkDetails, browse, list and download</code>
|
||||
* capabilities. It uses <code>Apache Commons HttpClient</code> for POST and GET
|
||||
* requests.
|
||||
* <p>
|
||||
* <p>
|
||||
* <b>XXX : DO NOT call checkin, login and download consecutively. To allow
|
||||
* server to catch up, sleep for a while before download! (5 sec will do!) Also
|
||||
* it is recommended to call checkin once and use generated android-id for
|
||||
* further operations.</b>
|
||||
* </p>
|
||||
*
|
||||
* @author akdeniz
|
||||
*/
|
||||
public class GooglePlayAPI {
|
||||
|
||||
private static final String SCHEME = "https://";
|
||||
private static final String HOST = "android.clients.google.com";
|
||||
private static final String CHECKIN_URL = SCHEME + HOST + "/checkin";
|
||||
private static final String URL_LOGIN = SCHEME + HOST + "/auth";
|
||||
private static final String C2DM_REGISTER_URL = SCHEME + HOST + "/c2dm/register2";
|
||||
private static final String FDFE_URL = SCHEME + HOST + "/fdfe/";
|
||||
private static final String LIST_URL = FDFE_URL + "list";
|
||||
private static final String BROWSE_URL = FDFE_URL + "browse";
|
||||
private static final String DETAILS_URL = FDFE_URL + "details";
|
||||
private static final String SEARCH_URL = FDFE_URL + "search";
|
||||
private static final String BULKDETAILS_URL = FDFE_URL + "bulkDetails";
|
||||
private static final String PURCHASE_URL = FDFE_URL + "purchase";
|
||||
private static final String REVIEWS_URL = FDFE_URL + "rev";
|
||||
private static final String UPLOADDEVICECONFIG_URL = FDFE_URL + "uploadDeviceConfig";
|
||||
private static final String RECOMMENDATIONS_URL = FDFE_URL + "rec";
|
||||
|
||||
private static final String ACCOUNT_TYPE_HOSTED_OR_GOOGLE = "HOSTED_OR_GOOGLE";
|
||||
|
||||
public enum REVIEW_SORT {
|
||||
NEWEST(0), HIGHRATING(1), HELPFUL(2);
|
||||
|
||||
public int value;
|
||||
|
||||
REVIEW_SORT(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public enum RECOMMENDATION_TYPE {
|
||||
ALSO_VIEWED(1), ALSO_INSTALLED(2);
|
||||
|
||||
public int value;
|
||||
|
||||
RECOMMENDATION_TYPE(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
private String token;
|
||||
private String gsfId;
|
||||
private String email;
|
||||
private String password;
|
||||
private ThrottledOkHttpClient client;
|
||||
private Locale locale;
|
||||
private DeviceInfoProvider deviceInfoProvider;
|
||||
private Map<String, String> searchNextPage = new HashMap<>();
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public void setGsfId(String gsfId) {
|
||||
this.gsfId = gsfId;
|
||||
}
|
||||
|
||||
public void setDeviceInfoProvider(DeviceInfoProvider deviceInfoProvider) {
|
||||
this.deviceInfoProvider = deviceInfoProvider;
|
||||
}
|
||||
|
||||
private ThrottledOkHttpClient getClient() {
|
||||
if (this.client == null) {
|
||||
this.client = new ThrottledOkHttpClient();
|
||||
}
|
||||
return this.client;
|
||||
}
|
||||
|
||||
public void setLocale(Locale locale) {
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this constructor is used, Android ID must be generated by calling
|
||||
* <code>checkin()</code> or set by using <code>setGsfId</code> before
|
||||
* using other abilities.
|
||||
*/
|
||||
public GooglePlayAPI(String email, String password) {
|
||||
this.email = email;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs authentication on "ac2dm" service and match up android id,
|
||||
* security token and email by checking them in on this server.
|
||||
* <p>
|
||||
* This function sets check-inded android ID and that can be taken either by
|
||||
* using <code>getToken()</code> or from returned
|
||||
* {@link AndroidCheckinResponse} instance.
|
||||
*/
|
||||
public String getGsfId() throws IOException {
|
||||
AndroidCheckinRequest request = this.deviceInfoProvider.generateAndroidCheckinRequest();
|
||||
|
||||
// this first checkin is for generating android-id
|
||||
AndroidCheckinResponse checkinResponse = checkin(request.toByteArray());
|
||||
this.gsfId = BigInteger.valueOf(checkinResponse.getAndroidId()).toString(16);
|
||||
String securityToken = BigInteger.valueOf(checkinResponse.getSecurityToken()).toString(16);
|
||||
|
||||
AndroidCheckinRequest.Builder checkInbuilder = AndroidCheckinRequest.newBuilder(request);
|
||||
String AC2DMToken = getAC2DMToken();
|
||||
AndroidCheckinRequest build = checkInbuilder
|
||||
.setId(new BigInteger(this.gsfId, 16).longValue())
|
||||
.setSecurityToken(new BigInteger(securityToken, 16).longValue())
|
||||
.addAccountCookie("[" + this.email + "]")
|
||||
.addAccountCookie(AC2DMToken)
|
||||
.build();
|
||||
// this is the second checkin to match credentials with android-id
|
||||
checkin(build.toByteArray());
|
||||
return this.gsfId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts given check-in request content and returns
|
||||
* {@link AndroidCheckinResponse}.
|
||||
*/
|
||||
private AndroidCheckinResponse checkin(byte[] request) throws IOException {
|
||||
Map<String, String> headers = getDefaultHeaders();
|
||||
headers.put("Content-Type", "application/x-protobuffer");
|
||||
byte[] content = getClient().post(CHECKIN_URL, request, headers);
|
||||
return AndroidCheckinResponse.parseFrom(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticates on server with given email and password and sets
|
||||
* authentication token. This token can be used to login instead of using
|
||||
* email and password every time.
|
||||
*/
|
||||
public String getToken() throws IOException {
|
||||
Map<String, String> params = getDefaultLoginParams();
|
||||
params.put("service", "androidmarket");
|
||||
params.put("app", "com.android.vending");
|
||||
params.put("androidId", this.getGsfId());
|
||||
byte[] responseBytes = getClient().post(URL_LOGIN, params, getDefaultHeaders());
|
||||
Map<String, String> response = parseResponse(new String(responseBytes));
|
||||
if (response.containsKey("Auth")) {
|
||||
return response.get("Auth");
|
||||
} else {
|
||||
throw new GooglePlayException("Authentication failed! (login)");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logins AC2DM server and returns authentication string.
|
||||
* <p>
|
||||
* <p>
|
||||
* client_sig is SHA1 digest of encoded certificate on
|
||||
* <i>GoogleLoginService(package name : com.google.android.gsf)</i> system APK.
|
||||
* But google doesn't seem to care of value of this parameter.
|
||||
*/
|
||||
public String getAC2DMToken() throws IOException {
|
||||
Map<String, String> params = getDefaultLoginParams();
|
||||
params.put("service", "ac2dm");
|
||||
params.put("add_account", "1");
|
||||
params.put("app", "com.google.android.gsf");
|
||||
byte[] responseBytes = getClient().post(URL_LOGIN, params, getDefaultHeaders());
|
||||
Map<String, String> response = parseResponse(new String(responseBytes));
|
||||
if (response.containsKey("Auth")) {
|
||||
return response.get("Auth");
|
||||
} else {
|
||||
throw new GooglePlayException("Authentication failed! (loginAC2DM)");
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, String> c2dmRegister(String application, String sender) throws IOException {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("app", application);
|
||||
params.put("sender", sender);
|
||||
params.put("device", new BigInteger(this.getGsfId(), 16).toString());
|
||||
Map<String, String> headers = getDefaultHeaders();
|
||||
headers.put("Authorization", "GoogleLogin auth=" + getAC2DMToken());
|
||||
byte[] responseBytes = getClient().post(C2DM_REGISTER_URL, params, headers);
|
||||
return parseResponse(new String(responseBytes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent of <code>search(query, null, null)</code>
|
||||
*/
|
||||
public SearchResponse search(String query) throws IOException {
|
||||
return search(query, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a search results for given query. Offset and numberOfResults
|
||||
* parameters are optional and <code>null</code> can be passed!
|
||||
*
|
||||
* Warning! offset and numberOfResults do not seem to work anymore.
|
||||
* The api always returns first 30 results. Fetching further results is done through
|
||||
* nextPageUrl returned with the search result.
|
||||
*/
|
||||
public SearchResponse search(String query, Integer offset, Integer numberOfResults) throws IOException {
|
||||
String url = SEARCH_URL;
|
||||
Map<String, String> params = new HashMap<>();
|
||||
if (this.searchNextPage.containsKey(query)) {
|
||||
url = this.searchNextPage.get(query);
|
||||
if (null == url) {
|
||||
throw new GooglePlayException("No more results for query " + query);
|
||||
}
|
||||
} else {
|
||||
params = getDefaultGetParams(offset, numberOfResults);
|
||||
params.put("q", query);
|
||||
}
|
||||
|
||||
byte[] content = getClient().get(url, params, getDefaultHeaders());
|
||||
ResponseWrapper responseWrapper = ResponseWrapper.parseFrom(content);
|
||||
SearchResponse response = responseWrapper.getPayload().getSearchResponse();
|
||||
if (response.getDocCount() > 0
|
||||
&& response.getDocList().get(0).hasContainerMetadata()
|
||||
&& response.getDocList().get(0).getContainerMetadata().hasNextPageUrl()
|
||||
) {
|
||||
this.searchNextPage.put(query, FDFE_URL + response.getDocList().get(0).getContainerMetadata().getNextPageUrl());
|
||||
} else {
|
||||
this.searchNextPage.put(query, null);
|
||||
}
|
||||
return responseWrapper.getPayload().getSearchResponse();
|
||||
}
|
||||
|
||||
public boolean hasNextSearchPage(String query) {
|
||||
return !this.searchNextPage.containsKey(query) || this.searchNextPage.get(query) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches detailed information about passed package name. If it is needed
|
||||
* to fetch information about more than one application, consider to use
|
||||
* <code>bulkDetails</code>.
|
||||
*/
|
||||
public DetailsResponse details(String packageName) throws IOException {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("doc", packageName);
|
||||
|
||||
byte[] content = getClient().get(DETAILS_URL, params, getDefaultHeaders());
|
||||
ResponseWrapper responseWrapper = ResponseWrapper.parseFrom(content);
|
||||
return responseWrapper.getPayload().getDetailsResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent of details but bulky one!
|
||||
*/
|
||||
public BulkDetailsResponse bulkDetails(List<String> packageNames) throws IOException {
|
||||
BulkDetailsRequest.Builder bulkDetailsRequestBuilder = BulkDetailsRequest.newBuilder();
|
||||
bulkDetailsRequestBuilder.addAllDocid(packageNames);
|
||||
byte[] request = bulkDetailsRequestBuilder.build().toByteArray();
|
||||
|
||||
byte[] content = getClient().post(BULKDETAILS_URL, request, getDefaultHeaders());
|
||||
ResponseWrapper responseWrapper = ResponseWrapper.parseFrom(content);
|
||||
return responseWrapper.getPayload().getBulkDetailsResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches available categories
|
||||
*/
|
||||
public BrowseResponse browse() throws IOException {
|
||||
return browse(null, null);
|
||||
}
|
||||
|
||||
public BrowseResponse browse(String categoryId, String subCategoryId) throws IOException {
|
||||
Map<String, String> params = getDefaultGetParams(null, null);
|
||||
params.put("cat", categoryId);
|
||||
params.put("ctr", subCategoryId);
|
||||
byte[] content = getClient().get(BROWSE_URL, params, getDefaultHeaders());
|
||||
ResponseWrapper responseWrapper = ResponseWrapper.parseFrom(content);
|
||||
return responseWrapper.getPayload().getBrowseResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent of <code>list(categoryId, null, null, null)</code>. It fetches
|
||||
* sub-categories of given category!
|
||||
*/
|
||||
public ListResponse list(String categoryId) throws IOException {
|
||||
return list(categoryId, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches applications within supplied category and sub-category. If
|
||||
* <code>null</code> is given for sub-category, it fetches sub-categories of
|
||||
* passed category.
|
||||
* <p>
|
||||
* Default values for offset and numberOfResult are "0" and "20"
|
||||
* respectively. These values are determined by Google Play Store.
|
||||
*/
|
||||
public ListResponse list(String categoryId, String subCategoryId, Integer offset, Integer numberOfResults) throws IOException {
|
||||
Map<String, String> params = getDefaultGetParams(offset, numberOfResults);
|
||||
params.put("cat", categoryId);
|
||||
params.put("ctr", subCategoryId);
|
||||
byte[] content = getClient().get(LIST_URL, params, getDefaultHeaders());
|
||||
ResponseWrapper responseWrapper = ResponseWrapper.parseFrom(content);
|
||||
return responseWrapper.getPayload().getListResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is used for fetching download url and download cookie,
|
||||
* rather than actual purchasing.
|
||||
*/
|
||||
public BuyResponse purchase(String packageName, int versionCode, int offerType) throws IOException {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("ot", String.valueOf(offerType));
|
||||
params.put("doc", packageName);
|
||||
params.put("vc", String.valueOf(versionCode));
|
||||
byte[] content = getClient().post(PURCHASE_URL, params, getDefaultHeaders());
|
||||
ResponseWrapper responseWrapper = ResponseWrapper.parseFrom(content);
|
||||
return responseWrapper.getPayload().getBuyResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the reviews of given package name by sorting passed choice.
|
||||
* <p>
|
||||
* Default values for offset and numberOfResult are "0" and "20"
|
||||
* respectively. These values are determined by Google Play Store.
|
||||
*/
|
||||
public ReviewResponse reviews(String packageName, REVIEW_SORT sort, Integer offset, Integer numberOfResults) throws IOException {
|
||||
Map<String, String> params = getDefaultGetParams(offset, numberOfResults);
|
||||
params.put("doc", packageName);
|
||||
params.put("sort", (sort == null) ? null : String.valueOf(sort.value));
|
||||
byte[] content = getClient().get(REVIEWS_URL, params, getDefaultHeaders());
|
||||
ResponseWrapper responseWrapper = ResponseWrapper.parseFrom(content);
|
||||
return responseWrapper.getPayload().getReviewResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads device configuration to google server so that can be seen from
|
||||
* web as a registered device!!
|
||||
*/
|
||||
public UploadDeviceConfigResponse uploadDeviceConfig() throws IOException {
|
||||
UploadDeviceConfigRequest request = UploadDeviceConfigRequest.newBuilder()
|
||||
.setDeviceConfiguration(this.deviceInfoProvider.getDeviceConfigurationProto())
|
||||
.build();
|
||||
byte[] content = getClient().post(UPLOADDEVICECONFIG_URL, request.toByteArray(), getDefaultHeaders());
|
||||
ResponseWrapper responseWrapper = ResponseWrapper.parseFrom(content);
|
||||
return responseWrapper.getPayload().getUploadDeviceConfigResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the recommendations of given package name.
|
||||
* <p>
|
||||
* Default values for offset and numberOfResult are "0" and "20"
|
||||
* respectively. These values are determined by Google Play Store.
|
||||
*/
|
||||
public ListResponse recommendations(String packageName, RECOMMENDATION_TYPE type, Integer offset, Integer numberOfResults) throws IOException {
|
||||
Map<String, String> params = getDefaultGetParams(offset, numberOfResults);
|
||||
params.put("doc", packageName);
|
||||
params.put("rt", (type == null) ? null : String.valueOf(type.value));
|
||||
byte[] content = getClient().get(RECOMMENDATIONS_URL, params, getDefaultHeaders());
|
||||
ResponseWrapper responseWrapper = ResponseWrapper.parseFrom(content);
|
||||
return responseWrapper.getPayload().getListResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* login methods use this
|
||||
* Most likely not all of these are required, but the Market app sends them, so we will too
|
||||
*
|
||||
*/
|
||||
private Map<String, String> getDefaultLoginParams() {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("Email", this.email);
|
||||
params.put("Passwd", this.password);
|
||||
params.put("accountType", ACCOUNT_TYPE_HOSTED_OR_GOOGLE);
|
||||
params.put("has_permission", "1");
|
||||
params.put("source", "android");
|
||||
params.put("device_country", this.locale.getCountry().toLowerCase());
|
||||
params.put("lang", this.locale.getLanguage().toLowerCase());
|
||||
params.put("sdk_version", String.valueOf(this.deviceInfoProvider.getSdkVersion()));
|
||||
params.put("client_sig", "38918a453d07199354f8b19af05ec6562ced5788");
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Using Accept-Language you can fetch localized informations such as reviews and descriptions.
|
||||
* Note that changing this value has no affect on localized application list that
|
||||
* server provides. It depends on only your IP location.
|
||||
*
|
||||
*/
|
||||
private Map<String, String> getDefaultHeaders() {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
if (this.token != null && !this.token.isEmpty()) {
|
||||
headers.put("Authorization", "GoogleLogin auth=" + this.token);
|
||||
}
|
||||
headers.put("User-Agent", this.deviceInfoProvider.getUserAgentString());
|
||||
if (this.gsfId != null && !this.gsfId.isEmpty()) {
|
||||
headers.put("X-DFE-Device-Id", this.gsfId);
|
||||
}
|
||||
headers.put("Accept-Language", this.locale.toString().replace("_", "-"));
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Most list requests (apps, categories,..) take these params
|
||||
*
|
||||
* @param offset
|
||||
* @param numberOfResults
|
||||
*/
|
||||
private Map<String, String> getDefaultGetParams(Integer offset, Integer numberOfResults) {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("c", "3");
|
||||
if (offset != null) {
|
||||
params.put("o", String.valueOf(offset));
|
||||
}
|
||||
if (numberOfResults != null) {
|
||||
params.put("n", String.valueOf(numberOfResults));
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
private static Map<String, String> parseResponse(String response) {
|
||||
Map<String, String> keyValueMap = new HashMap<>();
|
||||
StringTokenizer st = new StringTokenizer(response, "\n\r");
|
||||
while (st.hasMoreTokens()) {
|
||||
String[] keyValue = st.nextToken().split("=");
|
||||
keyValueMap.put(keyValue[0], keyValue[1]);
|
||||
}
|
||||
return keyValueMap;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.github.yeriomin.playstoreapi;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class GooglePlayException extends IOException {
|
||||
|
||||
private int code;
|
||||
|
||||
public GooglePlayException(String message) {
|
||||
super(message);
|
||||
}
|
||||
public GooglePlayException(String message, int code) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public GooglePlayException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return this.code;
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package com.github.yeriomin.playstoreapi;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Properties;
|
||||
|
||||
public class PropertiesDeviceInfoProvider implements DeviceInfoProvider {
|
||||
|
||||
private Properties properties;
|
||||
private String localeString;
|
||||
|
||||
public void setProperties(Properties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public void setLocaleString(String localeString) {
|
||||
this.localeString = localeString;
|
||||
}
|
||||
|
||||
public int getSdkVersion() {
|
||||
return Integer.parseInt(this.properties.getProperty("Build.VERSION.SDK_INT"));
|
||||
}
|
||||
|
||||
public String getUserAgentString() {
|
||||
return "Android-Finsky/7.1.15 ("
|
||||
+ "api=3"
|
||||
+ ",versionCode=80711500"
|
||||
+ ",sdk=" + this.properties.getProperty("Build.VERSION.SDK_INT")
|
||||
+ ",device=" + this.properties.getProperty("Build.DEVICE")
|
||||
+ ",hardware=" + this.properties.getProperty("Build.HARDWARE")
|
||||
+ ",product=" + this.properties.getProperty("Build.PRODUCT")
|
||||
+ ")";
|
||||
}
|
||||
|
||||
public AndroidCheckinRequest generateAndroidCheckinRequest() {
|
||||
return AndroidCheckinRequest.newBuilder()
|
||||
.setId(0)
|
||||
.setCheckin(
|
||||
AndroidCheckinProto.newBuilder()
|
||||
.setBuild(
|
||||
AndroidBuildProto.newBuilder()
|
||||
.setId(this.properties.getProperty("Build.FINGERPRINT"))
|
||||
.setProduct(this.properties.getProperty("Build.HARDWARE"))
|
||||
.setCarrier(this.properties.getProperty("Build.BRAND"))
|
||||
.setRadio(this.properties.getProperty("Build.RADIO"))
|
||||
.setBootloader(this.properties.getProperty("Build.BOOTLOADER"))
|
||||
.setDevice(this.properties.getProperty("Build.DEVICE"))
|
||||
.setSdkVersion(Integer.getInteger(this.properties.getProperty("Build.VERSION.SDK_INT")))
|
||||
.setModel(this.properties.getProperty("Build.MODEL"))
|
||||
.setManufacturer(this.properties.getProperty("Build.MANUFACTURER"))
|
||||
.setBuildProduct(this.properties.getProperty("Build.PRODUCT"))
|
||||
.setClient(this.properties.getProperty("Client"))
|
||||
.setOtaInstalled(Boolean.getBoolean(this.properties.getProperty("OtaInstalled")))
|
||||
.setTimestamp(System.currentTimeMillis() / 1000)
|
||||
.setGoogleServices(Integer.getInteger(this.properties.getProperty("GSF.version")))
|
||||
)
|
||||
.setLastCheckinMsec(0)
|
||||
.setCellOperator(this.properties.getProperty("CellOperator"))
|
||||
.setSimOperator(this.properties.getProperty("SimOperator"))
|
||||
.setRoaming(this.properties.getProperty("Roaming"))
|
||||
.setUserNumber(0)
|
||||
)
|
||||
.setLocale(this.localeString)
|
||||
.setTimeZone(this.properties.getProperty("TimeZone"))
|
||||
.setVersion(3)
|
||||
.setDeviceConfiguration(getDeviceConfigurationProto())
|
||||
.setFragment(0)
|
||||
.build();
|
||||
}
|
||||
|
||||
public DeviceConfigurationProto getDeviceConfigurationProto() {
|
||||
return DeviceConfigurationProto.newBuilder()
|
||||
.setTouchScreen(Integer.getInteger(this.properties.getProperty("TouchScreen")))
|
||||
.setKeyboard(Integer.getInteger(this.properties.getProperty("Keyboard")))
|
||||
.setNavigation(Integer.getInteger(this.properties.getProperty("Navigation")))
|
||||
.setScreenLayout(Integer.getInteger(this.properties.getProperty("ScreenLayout")))
|
||||
.setHasHardKeyboard(Boolean.getBoolean(this.properties.getProperty("HasHardKeyboard")))
|
||||
.setHasFiveWayNavigation(Boolean.getBoolean(this.properties.getProperty("HasFiveWayNavigation")))
|
||||
.setScreenDensity(Integer.getInteger(this.properties.getProperty("Screen.Density")))
|
||||
.setScreenWidth(Integer.getInteger(this.properties.getProperty("Screen.Width")))
|
||||
.setScreenHeight(Integer.getInteger(this.properties.getProperty("Screen.Height")))
|
||||
.addAllNativePlatform(Arrays.asList(this.properties.getProperty("Platforms").split(",")))
|
||||
.addAllSystemSharedLibrary(Arrays.asList(this.properties.getProperty("SharedLibraries").split(",")))
|
||||
.addAllSystemAvailableFeature(Arrays.asList(this.properties.getProperty("Features").split(",")))
|
||||
.addAllSystemSupportedLocale(Arrays.asList(this.properties.getProperty("Locales").split(",")))
|
||||
.setGlEsVersion(Integer.getInteger(this.properties.getProperty("GL.Version")))
|
||||
.addAllGlExtension(Arrays.asList(this.properties.getProperty("GL.Extensions").split(",")))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
package com.github.yeriomin.playstoreapi;
|
||||
|
||||
import android.os.SystemClock;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.Cookie;
|
||||
import okhttp3.CookieJar;
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.Headers;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
class ThrottledOkHttpClient {
|
||||
|
||||
private static final long DEFAULT_REQUEST_INTERVAL = 2000;
|
||||
|
||||
private long lastRequestTime;
|
||||
private long requestInterval = DEFAULT_REQUEST_INTERVAL;
|
||||
private OkHttpClient client;
|
||||
|
||||
public void setRequestInterval(long requestInterval) {
|
||||
this.requestInterval = requestInterval;
|
||||
}
|
||||
|
||||
public ThrottledOkHttpClient() {
|
||||
this.client = new OkHttpClient.Builder()
|
||||
.connectTimeout(3, TimeUnit.SECONDS)
|
||||
.readTimeout(3, TimeUnit.SECONDS)
|
||||
.cookieJar(new CookieJar() {
|
||||
private final HashMap<HttpUrl, List<Cookie>> cookieStore = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
|
||||
cookieStore.put(url, cookies);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Cookie> loadForRequest(HttpUrl url) {
|
||||
List<Cookie> cookies = cookieStore.get(url);
|
||||
return cookies != null ? cookies : new ArrayList<Cookie>();
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
public byte[] get(String url, Map<String, String> params) throws IOException {
|
||||
return get(url, params, null);
|
||||
}
|
||||
|
||||
public byte[] get(String url, Map<String, String> params, Map<String, String> headers) throws IOException {
|
||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
|
||||
if (null != params && !params.isEmpty()) {
|
||||
for (String name: params.keySet()) {
|
||||
urlBuilder.addQueryParameter(name, params.get(name));
|
||||
}
|
||||
}
|
||||
|
||||
Request.Builder requestBuilder = new Request.Builder()
|
||||
.url(urlBuilder.build())
|
||||
.get();
|
||||
|
||||
return request(requestBuilder, headers);
|
||||
}
|
||||
|
||||
public byte[] post(String url, Map<String, String> params, Map<String, String> headers) throws IOException {
|
||||
headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
|
||||
|
||||
FormBody.Builder bodyBuilder = new FormBody.Builder();
|
||||
if (null != params && !params.isEmpty()) {
|
||||
for (String name: params.keySet()) {
|
||||
bodyBuilder.add(name, params.get(name));
|
||||
}
|
||||
}
|
||||
|
||||
Request.Builder requestBuilder = new Request.Builder()
|
||||
.url(url)
|
||||
.post(bodyBuilder.build());
|
||||
|
||||
return post(url, requestBuilder, headers);
|
||||
}
|
||||
|
||||
public byte[] post(String url, byte[] body, Map<String, String> headers) throws IOException {
|
||||
if (!headers.containsKey("Content-Type")) {
|
||||
headers.put("Content-Type", "application/x-protobuf");
|
||||
}
|
||||
|
||||
Request.Builder requestBuilder = new Request.Builder()
|
||||
.url(url)
|
||||
.post(RequestBody.create(MediaType.parse("application/x-protobuf"), body));
|
||||
|
||||
return post(url, requestBuilder, headers);
|
||||
}
|
||||
|
||||
private byte[] post(String url, Request.Builder requestBuilder, Map<String, String> headers) throws IOException {
|
||||
requestBuilder.url(url);
|
||||
|
||||
return request(requestBuilder, headers);
|
||||
}
|
||||
|
||||
private byte[] request(Request.Builder requestBuilder, Map<String, String> headers) throws IOException {
|
||||
if (this.lastRequestTime > 0) {
|
||||
long msecRemaining = this.requestInterval - System.currentTimeMillis() + this.lastRequestTime;
|
||||
if (msecRemaining > 0) {
|
||||
SystemClock.sleep(msecRemaining);
|
||||
}
|
||||
}
|
||||
|
||||
Request request = requestBuilder
|
||||
.headers(Headers.of(headers))
|
||||
.build();
|
||||
System.out.println("Requesting: " + request.url().toString());
|
||||
|
||||
Response response = client.newCall(request).execute();
|
||||
|
||||
int code = response.code();
|
||||
byte[] content = response.body().bytes();
|
||||
|
||||
if (code >= 400) {
|
||||
throw new GooglePlayException(String.valueOf(code) + " Probably an auth error: " + new String(content), code);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
@@ -1,281 +0,0 @@
|
||||
package com.github.yeriomin.playstoreapi;
|
||||
|
||||
/**
|
||||
* @author akdeniz
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
/*
|
||||
*
|
||||
|
||||
id: "Xiaomi/gemini/gemini:6.0/MRA58K/V7.2.8.0.MAACNDB:user/release-keys"
|
||||
product: "qcom"
|
||||
carrier: "Xiaomi"
|
||||
radio: "TH20.c1.3-0321_1155_57c5007"
|
||||
bootloader: "unknown"
|
||||
client: "unknown"
|
||||
timestamp: 1458643019
|
||||
googleServices: 10084448
|
||||
device: "gemini"
|
||||
sdkVersion: 23
|
||||
model: "MI 5"
|
||||
manufacturer: "Xiaomi"
|
||||
buildProduct: "gemini"
|
||||
otaInstalled: 0
|
||||
|
||||
|
||||
Build.BOARD msm8996
|
||||
Build.BOOTLOADER unknown
|
||||
Build.BRAND Xiaomi
|
||||
Build.DEVICE gemini
|
||||
Build.DISPLAY MRA58K
|
||||
Build.FINGERPRINT Xiaomi/gemini/gemini:6.0/MRA58K/V7.2.8.0.MAACNDB:user/release-keys
|
||||
Build.HARDWARE qcom
|
||||
Build.HOST qh-miui-ota-bd72.bj
|
||||
Build.ID MRA58K
|
||||
Build.MANUFACTURER Xiaomi
|
||||
Build.MODEL MI 5
|
||||
Build.PRODUCT gemini
|
||||
Build.SERIAL d0189fa4
|
||||
Build.TAGS release-keys
|
||||
Build.TYPE user
|
||||
Build.UNKNOWN unknown
|
||||
Build.USER builder
|
||||
Build.CPU_ABI arm64-v8a
|
||||
Build.CPU_ABI2
|
||||
Build.TIME 1458643019000
|
||||
Build.VERSION.CODENAME REL
|
||||
Build.VERSION.INCREMENTAL V7.2.8.0.0.MAACNDB
|
||||
Build.VERSION.RELEASE 6.0
|
||||
Build.VERSION.SDK_INT 23
|
||||
*/
|
||||
|
||||
// /**
|
||||
// * Generates android checkin request with properties of "Galaxy S3".
|
||||
// * <p>
|
||||
// * <a href=
|
||||
// * "http://www.glbenchmark.com/phonedetails.jsp?benchmark=glpro25&D=Samsung+GT-I9300+Galaxy+S+III&testgroup=system"
|
||||
// * > http://www.glbenchmark.com/phonedetails.jsp?benchmark=glpro25&D=Samsung
|
||||
// * +GT-I9300+Galaxy+S+III&testgroup=system </a>
|
||||
// */
|
||||
// public static AndroidCheckinRequest generateAndroidCheckinRequest() {
|
||||
//
|
||||
// return AndroidCheckinRequest
|
||||
// .newBuilder()
|
||||
// .setId(0)
|
||||
// .setCheckin(
|
||||
// AndroidCheckinProto.newBuilder()
|
||||
// .setBuild(
|
||||
// AndroidBuildProto.newBuilder()
|
||||
// .setId(Build.FINGERPRINT)
|
||||
// .setProduct(Build.HARDWARE)
|
||||
// .setCarrier(Build.BRAND)
|
||||
// .setRadio(Build.RADIO)
|
||||
// .setBootloader(Build.BOOTLOADER)
|
||||
// .setDevice(Build.DEVICE)
|
||||
// .setSdkVersion(Build.VERSION.SDK_INT)
|
||||
// .setModel(Build.MODEL)
|
||||
// .setManufacturer(Build.MANUFACTURER)
|
||||
// .setBuildProduct(Build.PRODUCT)
|
||||
// .setClient("android-google")
|
||||
// .setOtaInstalled(false)
|
||||
// .setTimestamp(new Date().getTime() / 1000)
|
||||
// .setGoogleServices(16)
|
||||
// )
|
||||
// .setLastCheckinMsec(0)
|
||||
// .setCellOperator("310260")
|
||||
// .setSimOperator("310260")
|
||||
// .setRoaming("mobile-notroaming")
|
||||
// .setUserNumber(0)
|
||||
// )
|
||||
// .setLocale(Locale.getDefault().toString())
|
||||
// .setTimeZone(TimeZone.getDefault().getID())
|
||||
// .setVersion(3)
|
||||
// .setDeviceConfiguration(getDeviceConfigurationProto())
|
||||
// .setFragment(0)
|
||||
// .build();
|
||||
// }
|
||||
//
|
||||
// public static AndroidCheckinRequest generateAndroidCheckinRequestOriginal() {
|
||||
//
|
||||
// return AndroidCheckinRequest
|
||||
// .newBuilder()
|
||||
// .setId(0)
|
||||
// .setCheckin(
|
||||
// AndroidCheckinProto.newBuilder()
|
||||
// .setBuild(
|
||||
// AndroidBuildProto.newBuilder()
|
||||
// .setId("samsung/m0xx/m0:4.0.4/IMM76D/I9300XXALF2:user/release-keys")
|
||||
// .setProduct("smdk4x12")
|
||||
// .setCarrier("Google")
|
||||
// .setRadio("I9300XXALF2")
|
||||
// .setBootloader("PRIMELA03")
|
||||
// .setClient("android-google")
|
||||
// .setTimestamp(new Date().getTime() / 1000)
|
||||
// .setGoogleServices(16)
|
||||
// .setDevice("m0")
|
||||
// .setSdkVersion(16)
|
||||
// .setModel("GT-I9300")
|
||||
// .setManufacturer("Samsung")
|
||||
// .setBuildProduct("m0xx")
|
||||
// .setOtaInstalled(false)
|
||||
// )
|
||||
// .setLastCheckinMsec(0)
|
||||
// .setCellOperator("310260")
|
||||
// .setSimOperator("310260")
|
||||
// .setRoaming("mobile-notroaming")
|
||||
// .setUserNumber(0)
|
||||
// )
|
||||
// .setLocale("en_US")
|
||||
// .setTimeZone("Europe/Istanbul")
|
||||
// .setVersion(3)
|
||||
// .setDeviceConfiguration(getDeviceConfigurationProto())
|
||||
// .setFragment(0)
|
||||
// .build();
|
||||
// }
|
||||
//
|
||||
// public static AndroidCheckinRequest generateAndroidCheckinRequestNviennot() {
|
||||
//
|
||||
// return AndroidCheckinRequest
|
||||
// .newBuilder()
|
||||
// .setId(0)
|
||||
// .setCheckin(
|
||||
// AndroidCheckinProto.newBuilder()
|
||||
// .setBuild(
|
||||
// AndroidBuildProto.newBuilder()
|
||||
// .setId("google/yakju/maguro:4.1.1/JRO03C/398337:user/release-keys")
|
||||
// .setProduct("tuna")
|
||||
// .setCarrier("Google")
|
||||
// .setRadio("I9250XXLA2")
|
||||
// .setBootloader("PRIMELA03")
|
||||
// .setClient("android-google")
|
||||
// .setTimestamp(new Date().getTime()/1000)
|
||||
// .setGoogleServices(16)
|
||||
// .setDevice("maguro")
|
||||
// .setSdkVersion(16)
|
||||
// .setModel("Galaxy Nexus")
|
||||
// .setManufacturer("Samsung")
|
||||
// .setBuildProduct("yakju")
|
||||
// .setOtaInstalled(false)
|
||||
// )
|
||||
// .setLastCheckinMsec(0)
|
||||
// .setCellOperator("310260")
|
||||
// .setSimOperator("310260")
|
||||
// .setRoaming("mobile-notroaming")
|
||||
// .setUserNumber(0)
|
||||
// )
|
||||
// .setLocale("en_US")
|
||||
// .setTimeZone("Europe/Istanbul")
|
||||
// .setVersion(3)
|
||||
// .setDeviceConfiguration(getDeviceConfigurationProto())
|
||||
// .setFragment(0)
|
||||
// .build();
|
||||
// }
|
||||
//
|
||||
// public static AndroidCheckinRequest generateAndroidCheckinRequestRacoon() {
|
||||
//
|
||||
// return AndroidCheckinRequest
|
||||
// .newBuilder()
|
||||
// .setId(0)
|
||||
// .setCheckin(
|
||||
// AndroidCheckinProto.newBuilder()
|
||||
// .setBuild(
|
||||
// AndroidBuildProto.newBuilder()
|
||||
// .setId("samsung/nobleltejv/noblelte:6.0.1/MMB29K/N920CXXU2BPD6:user/release-keys")
|
||||
// .setProduct("noblelte")
|
||||
// .setCarrier("Google")
|
||||
// .setRadio("I9300XXALF2")
|
||||
// .setBootloader("PRIMELA03")
|
||||
// .setClient("android-google")
|
||||
// .setTimestamp(new Date().getTime() / 1000)
|
||||
// .setGoogleServices(16)
|
||||
// .setDevice("noblelte")
|
||||
// .setSdkVersion(23)
|
||||
// .setModel("SM-N920C")
|
||||
// .setManufacturer("Samsung")
|
||||
// .setBuildProduct("noblelte")
|
||||
// .setOtaInstalled(false)
|
||||
// )
|
||||
// .setLastCheckinMsec(0)
|
||||
// .setCellOperator("310260")
|
||||
// .setSimOperator("310260")
|
||||
// .setRoaming("mobile-notroaming")
|
||||
// .setUserNumber(0)
|
||||
// )
|
||||
// .setLocale("en_US")
|
||||
// .setTimeZone("Europe/Berlin")
|
||||
// .setVersion(3)
|
||||
// .setDeviceConfiguration(getDeviceConfigurationProto())
|
||||
// .setFragment(0)
|
||||
// .build();
|
||||
// }
|
||||
//
|
||||
// public static DeviceConfigurationProto getDeviceConfigurationProtoAAAAA() {
|
||||
//// DisplayMetrics metrics = new DisplayMetrics();
|
||||
//// WindowManager wm = (WindowManager) this.context.getSystemService(Context.WINDOW_SERVICE);
|
||||
//// wm.getDefaultDisplay().getMetrics(metrics);
|
||||
// return DeviceConfigurationProto.newBuilder()
|
||||
// .setTouchScreen(3)
|
||||
// .setKeyboard(1)
|
||||
// .setNavigation(1)
|
||||
// .setScreenLayout(2)
|
||||
// .setHasHardKeyboard(false)
|
||||
// .setHasFiveWayNavigation(false)
|
||||
// .setScreenDensity(320)
|
||||
// .setScreenWidth(720)
|
||||
// .setScreenHeight(1184)
|
||||
// .setGlEsVersion(131072)
|
||||
// .addAllNativePlatform(Arrays.asList(Build.CPU_ABI, Build.CPU_ABI2))
|
||||
//// .addAllNativePlatform(Arrays.asList("armeabi-v7a", "armeabi"))
|
||||
// .addAllSystemSharedLibrary(
|
||||
// Arrays.asList("android.test.runner", "com.android.future.usb.accessory", "com.android.location.provider",
|
||||
// "com.android.nfc_extras", "com.google.android.maps", "com.google.android.media.effects",
|
||||
// "com.google.widevine.software.drm", "javax.obex"))
|
||||
// .addAllSystemAvailableFeature(
|
||||
// Arrays.asList("android.hardware.bluetooth", "android.hardware.camera",
|
||||
// "android.hardware.camera.autofocus", "android.hardware.camera.flash",
|
||||
// "android.hardware.camera.front", "android.hardware.faketouch", "android.hardware.location",
|
||||
// "android.hardware.location.gps", "android.hardware.location.network",
|
||||
// "android.hardware.microphone", "android.hardware.nfc", "android.hardware.screen.landscape",
|
||||
// "android.hardware.screen.portrait", "android.hardware.sensor.accelerometer",
|
||||
// "android.hardware.sensor.barometer", "android.hardware.sensor.compass",
|
||||
// "android.hardware.sensor.gyroscope", "android.hardware.sensor.light",
|
||||
// "android.hardware.sensor.proximity", "android.hardware.telephony",
|
||||
// "android.hardware.telephony.gsm", "android.hardware.touchscreen",
|
||||
// "android.hardware.touchscreen.multitouch", "android.hardware.touchscreen.multitouch.distinct",
|
||||
// "android.hardware.touchscreen.multitouch.jazzhand", "android.hardware.usb.accessory",
|
||||
// "android.hardware.usb.host", "android.hardware.wifi", "android.hardware.wifi.direct",
|
||||
// "android.software.live_wallpaper", "android.software.sip", "android.software.sip.voip",
|
||||
// "com.cyanogenmod.android", "com.cyanogenmod.nfc.enhanced",
|
||||
// "com.google.android.feature.GOOGLE_BUILD", "com.nxp.mifare", "com.tmobile.software.themes"))
|
||||
// .addAllSystemSupportedLocale(
|
||||
// Arrays.asList("af", "af_ZA", "am", "am_ET", "ar", "ar_EG", "bg", "bg_BG", "ca", "ca_ES", "cs", "cs_CZ",
|
||||
// "da", "da_DK", "de", "de_AT", "de_CH", "de_DE", "de_LI", "el", "el_GR", "en", "en_AU", "en_CA",
|
||||
// "en_GB", "en_NZ", "en_SG", "en_US", "es", "es_ES", "es_US", "fa", "fa_IR", "fi", "fi_FI", "fr",
|
||||
// "fr_BE", "fr_CA", "fr_CH", "fr_FR", "hi", "hi_IN", "hr", "hr_HR", "hu", "hu_HU", "in", "in_ID",
|
||||
// "it", "it_CH", "it_IT", "iw", "iw_IL", "ja", "ja_JP", "ko", "ko_KR", "lt", "lt_LT", "lv",
|
||||
// "lv_LV", "ms", "ms_MY", "nb", "nb_NO", "nl", "nl_BE", "nl_NL", "pl", "pl_PL", "pt", "pt_BR",
|
||||
// "pt_PT", "rm", "rm_CH", "ro", "ro_RO", "ru", "ru_RU", "sk", "sk_SK", "sl", "sl_SI", "sr",
|
||||
// "sr_RS", "sv", "sv_SE", "sw", "sw_TZ", "th", "th_TH", "tl", "tl_PH", "tr", "tr_TR", "ug",
|
||||
// "ug_CN", "uk", "uk_UA", "vi", "vi_VN", "zh_CN", "zh_TW", "zu", "zu_ZA"))
|
||||
// .addAllGlExtension(
|
||||
// Arrays.asList("GL_EXT_debug_marker", "GL_EXT_discard_framebuffer", "GL_EXT_multi_draw_arrays",
|
||||
// "GL_EXT_shader_texture_lod", "GL_EXT_texture_format_BGRA8888",
|
||||
// "GL_IMG_multisampled_render_to_texture", "GL_IMG_program_binary", "GL_IMG_read_format",
|
||||
// "GL_IMG_shader_binary", "GL_IMG_texture_compression_pvrtc", "GL_IMG_texture_format_BGRA8888",
|
||||
// "GL_IMG_texture_npot", "GL_IMG_vertex_array_object", "GL_OES_EGL_image",
|
||||
// "GL_OES_EGL_image_external", "GL_OES_blend_equation_separate", "GL_OES_blend_func_separate",
|
||||
// "GL_OES_blend_subtract", "GL_OES_byte_coordinates", "GL_OES_compressed_ETC1_RGB8_texture",
|
||||
// "GL_OES_compressed_paletted_texture", "GL_OES_depth24", "GL_OES_depth_texture",
|
||||
// "GL_OES_draw_texture", "GL_OES_egl_sync", "GL_OES_element_index_uint",
|
||||
// "GL_OES_extended_matrix_palette", "GL_OES_fixed_point", "GL_OES_fragment_precision_high",
|
||||
// "GL_OES_framebuffer_object", "GL_OES_get_program_binary", "GL_OES_mapbuffer",
|
||||
// "GL_OES_matrix_get", "GL_OES_matrix_palette", "GL_OES_packed_depth_stencil",
|
||||
// "GL_OES_point_size_array", "GL_OES_point_sprite", "GL_OES_query_matrix", "GL_OES_read_format",
|
||||
// "GL_OES_required_internalformat", "GL_OES_rgb8_rgba8", "GL_OES_single_precision",
|
||||
// "GL_OES_standard_derivatives", "GL_OES_stencil8", "GL_OES_stencil_wrap",
|
||||
// "GL_OES_texture_cube_map", "GL_OES_texture_env_crossbar", "GL_OES_texture_float",
|
||||
// "GL_OES_texture_half_float", "GL_OES_texture_mirrored_repeat", "GL_OES_vertex_array_object",
|
||||
// "GL_OES_vertex_half_float")).build();
|
||||
// }
|
||||
}
|
||||
@@ -12,6 +12,7 @@ buildscript {
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
maven { url 'https://jitpack.io' }
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user