soul-browser/sources/java/com/google/api/client/googleapis/batch/BatchUnparsedResponse.java
KaKi87 550ddb0413
Decompile and recreate Soul Browser v1.4.85 with APK CI builds (#1)
* Decompile and recreate Soul Browser v1.4.85 with APK CI builds

Co-authored-by: KaKi87 <KaKi87@pm.me>

* Fix CI build: include bundled google.jks and remove PR trigger

The apktool build failed in CI because app/unknown/.../google.jks was
excluded by the *.jks gitignore rule. Whitelist app-bundled JKS files
and commit the missing resource.

Also remove the redundant pull_request workflow trigger.

Co-authored-by: KaKi87 <KaKi87@pm.me>

* Change package ID to com.github.kaki87.soulbrowser

Rename the application ID so the rebuilt APK can be installed
alongside the original Soul Browser from Google Play.

- Update AndroidManifest package, permissions, providers, and actions
- Update R class references in smali (com/github/kaki87/soulbrowser/R)
- Update shortcuts.xml targetPackage
- Rename app label to "Soul Rebuild" for easy identification

Co-authored-by: KaKi87 <KaKi87@pm.me>

* Fix install compatibility: remove split APK metadata and bundle native libs

The rebuilt APK still declared requiredSplitTypes and Play Store split
metadata, causing Android to reject standalone installs as incompatible.

- Remove requiredSplitTypes and split-related manifest meta-data
- Merge native libs from all ABI splits with uncompressed storage
- Page-align before signing for extractNativeLibs=false
- Add .so to apktool doNotCompress list

Co-authored-by: KaKi87 <KaKi87@pm.me>

* Fix install failure: use apksigner v2/v3 and bundle arm64 native libs

Target SDK 36 requires APK Signature Scheme v2+, but jarsigner only
produces v1 signatures, causing Android to reject the install.

- Sign with apksigner (v1+v2+v3) instead of jarsigner
- Add arm64-v8a native libraries from universal Soul Browser 1.4.79
- Set extractNativeLibs=true for reliable sideload installs
- Commit stable debug keystore for consistent signatures across builds

Co-authored-by: KaKi87 <KaKi87@pm.me>

* Add committed debug keystore for consistent APK signatures

Co-authored-by: KaKi87 <KaKi87@pm.me>

* Fix startup crash: use original classes2.dex desugar libraries

Apktool recompiles smali_classes2 into a broken classes2.dex, causing
ClassNotFoundException for j$.com.android.tools.r8.a at ML Kit init.

Inject the original classes2.dex (Java 8+ desugar libs) after apktool
build instead of using the recompiled version.

Co-authored-by: KaKi87 <KaKi87@pm.me>

* Fix missing drawable resources from density split APKs

The base APK is an app bundle module; density-specific drawables like
seek_thumb_nor_b live in config.xhdpi.apk and were missing after rebuild,
causing Resources$NotFoundException at runtime.

- Merge non-9-patch resources from split APKs before apktool build
- Sync public.xml IDs from R smali only when backing files exist
- Add 713 density-specific resource IDs to public.xml

Co-authored-by: KaKi87 <KaKi87@pm.me>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-08-10 16:07:14 +02:00

314 lines
12 KiB
Java

package com.google.api.client.googleapis.batch;
import android.support.v4.media.a;
import com.google.android.gms.ads.RequestConfiguration;
import com.google.api.client.googleapis.batch.BatchRequest;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpContent;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpStatusCodes;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.HttpUnsuccessfulResponseHandler;
import com.google.api.client.http.LowLevelHttpRequest;
import com.google.api.client.http.LowLevelHttpResponse;
import com.google.api.client.util.ByteStreams;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FilterInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/* loaded from: classes3.dex */
final class BatchUnparsedResponse {
private final String boundary;
private final InputStream inputStream;
private final List<BatchRequest.RequestInfo<?, ?>> requestInfos;
private final boolean retryAllowed;
boolean hasNext = true;
List<BatchRequest.RequestInfo<?, ?>> unsuccessfulRequestInfos = new ArrayList();
private int contentId = 0;
/* loaded from: classes3.dex */
public static class FakeLowLevelHttpRequest extends LowLevelHttpRequest {
private List<String> headerNames;
private List<String> headerValues;
private InputStream partContent;
private int statusCode;
public FakeLowLevelHttpRequest(InputStream inputStream, int i, List<String> list, List<String> list2) {
this.partContent = inputStream;
this.statusCode = i;
this.headerNames = list;
this.headerValues = list2;
}
@Override // com.google.api.client.http.LowLevelHttpRequest
public void addHeader(String str, String str2) {
}
@Override // com.google.api.client.http.LowLevelHttpRequest
public LowLevelHttpResponse execute() {
return new FakeLowLevelHttpResponse(this.partContent, this.statusCode, this.headerNames, this.headerValues);
}
}
/* loaded from: classes3.dex */
public static class FakeLowLevelHttpResponse extends LowLevelHttpResponse {
private List<String> headerNames;
private List<String> headerValues;
private InputStream partContent;
private int statusCode;
public FakeLowLevelHttpResponse(InputStream inputStream, int i, List<String> list, List<String> list2) {
this.headerNames = new ArrayList();
new ArrayList();
this.partContent = inputStream;
this.statusCode = i;
this.headerNames = list;
this.headerValues = list2;
}
@Override // com.google.api.client.http.LowLevelHttpResponse
public InputStream getContent() {
return this.partContent;
}
@Override // com.google.api.client.http.LowLevelHttpResponse
public String getContentEncoding() {
return null;
}
@Override // com.google.api.client.http.LowLevelHttpResponse
public long getContentLength() {
return 0L;
}
@Override // com.google.api.client.http.LowLevelHttpResponse
public String getContentType() {
return null;
}
@Override // com.google.api.client.http.LowLevelHttpResponse
public int getHeaderCount() {
return this.headerNames.size();
}
@Override // com.google.api.client.http.LowLevelHttpResponse
public String getHeaderName(int i) {
return this.headerNames.get(i);
}
@Override // com.google.api.client.http.LowLevelHttpResponse
public String getHeaderValue(int i) {
return this.headerValues.get(i);
}
@Override // com.google.api.client.http.LowLevelHttpResponse
public String getReasonPhrase() {
return null;
}
@Override // com.google.api.client.http.LowLevelHttpResponse
public int getStatusCode() {
return this.statusCode;
}
@Override // com.google.api.client.http.LowLevelHttpResponse
public String getStatusLine() {
return null;
}
}
/* loaded from: classes3.dex */
public static class FakeResponseHttpTransport extends HttpTransport {
private List<String> headerNames;
private List<String> headerValues;
private InputStream partContent;
private int statusCode;
public FakeResponseHttpTransport(int i, InputStream inputStream, List<String> list, List<String> list2) {
this.statusCode = i;
this.partContent = inputStream;
this.headerNames = list;
this.headerValues = list2;
}
@Override // com.google.api.client.http.HttpTransport
public LowLevelHttpRequest buildRequest(String str, String str2) {
return new FakeLowLevelHttpRequest(this.partContent, this.statusCode, this.headerNames, this.headerValues);
}
}
public BatchUnparsedResponse(InputStream inputStream, String str, List<BatchRequest.RequestInfo<?, ?>> list, boolean z) {
this.boundary = str;
this.requestInfos = list;
this.retryAllowed = z;
this.inputStream = inputStream;
checkForFinalBoundary(readLine());
}
private void checkForFinalBoundary(String str) {
if (str.equals(this.boundary + "--")) {
this.hasNext = false;
this.inputStream.close();
}
}
private HttpResponse getFakeResponse(int i, InputStream inputStream, List<String> list, List<String> list2) {
HttpRequest buildPostRequest = new FakeResponseHttpTransport(i, inputStream, list, list2).createRequestFactory().buildPostRequest(new GenericUrl("http://google.com/"), null);
buildPostRequest.setLoggingEnabled(false);
buildPostRequest.setThrowExceptionOnExecuteError(false);
return buildPostRequest.execute();
}
private <A, T, E> A getParsedDataClass(Class<A> cls, HttpResponse httpResponse, BatchRequest.RequestInfo<T, E> requestInfo) {
if (cls == Void.class) {
return null;
}
return (A) requestInfo.request.getParser().parseAndClose(httpResponse.getContent(), httpResponse.getContentCharset(), (Class) cls);
}
/* JADX WARN: Multi-variable type inference failed */
private <T, E> void parseAndCallback(BatchRequest.RequestInfo<T, E> requestInfo, int i, HttpResponse httpResponse) {
boolean z;
boolean z2;
BatchCallback<T, E> batchCallback = requestInfo.callback;
HttpHeaders headers = httpResponse.getHeaders();
HttpUnsuccessfulResponseHandler unsuccessfulResponseHandler = requestInfo.request.getUnsuccessfulResponseHandler();
if (HttpStatusCodes.isSuccess(i)) {
if (batchCallback != 0) {
batchCallback.onSuccess(getParsedDataClass(requestInfo.dataClass, httpResponse, requestInfo), headers);
return;
}
return;
}
HttpContent content = requestInfo.request.getContent();
boolean z3 = true;
if (this.retryAllowed && (content == null || content.retrySupported())) {
z = true;
} else {
z = false;
}
if (unsuccessfulResponseHandler != null) {
z2 = unsuccessfulResponseHandler.handleResponse(requestInfo.request, httpResponse, z);
} else {
z2 = false;
}
if (z2 || !requestInfo.request.handleRedirect(httpResponse.getStatusCode(), httpResponse.getHeaders())) {
z3 = false;
}
if (z && (z2 || z3)) {
this.unsuccessfulRequestInfos.add(requestInfo);
} else {
if (batchCallback == 0) {
return;
}
batchCallback.onFailure(getParsedDataClass(requestInfo.errorClass, httpResponse, requestInfo), headers);
}
}
private String readLine() {
return trimCrlf(readRawLine());
}
private String readRawLine() {
int read = this.inputStream.read();
if (read == -1) {
return null;
}
StringBuilder sb = new StringBuilder();
while (read != -1) {
sb.append((char) read);
if (read == 10) {
break;
}
read = this.inputStream.read();
}
return sb.toString();
}
private static String trimCrlf(String str) {
if (str.endsWith("\r\n")) {
return a.d(2, 0, str);
}
return str.endsWith("\n") ? a.d(1, 0, str) : str;
}
public void parseNextResponse() {
String readLine;
String readLine2;
InputStream inputStream;
String readRawLine;
this.contentId++;
do {
readLine = readLine();
if (readLine == null) {
break;
}
} while (!readLine.equals(RequestConfiguration.MAX_AD_CONTENT_RATING_UNSPECIFIED));
int parseInt = Integer.parseInt(readLine().split(" ")[1]);
ArrayList arrayList = new ArrayList();
ArrayList arrayList2 = new ArrayList();
long j = -1;
while (true) {
readLine2 = readLine();
if (readLine2 == null || readLine2.equals(RequestConfiguration.MAX_AD_CONTENT_RATING_UNSPECIFIED)) {
break;
}
String[] split2 = readLine2.split(": ", 2);
String str = split2[0];
String str2 = split2[1];
arrayList.add(str);
arrayList2.add(str2);
if ("Content-Length".equalsIgnoreCase(str.trim())) {
j = Long.parseLong(str2);
}
}
if (j == -1) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
while (true) {
readRawLine = readRawLine();
if (readRawLine == null || readRawLine.startsWith(this.boundary)) {
break;
} else {
byteArrayOutputStream.write(readRawLine.getBytes("ISO-8859-1"));
}
}
inputStream = trimCrlf(byteArrayOutputStream.toByteArray());
readLine2 = trimCrlf(readRawLine);
} else {
inputStream = new FilterInputStream(ByteStreams.limit(this.inputStream, j)) { // from class: com.google.api.client.googleapis.batch.BatchUnparsedResponse.1
@Override // java.io.FilterInputStream, java.io.InputStream, java.io.Closeable, java.lang.AutoCloseable
public void close() {
}
};
}
parseAndCallback(this.requestInfos.get(this.contentId - 1), parseInt, getFakeResponse(parseInt, inputStream, arrayList, arrayList2));
while (true) {
if (inputStream.skip(j) <= 0 && inputStream.read() == -1) {
break;
}
}
if (j != -1) {
readLine2 = readLine();
}
while (readLine2 != null && readLine2.length() == 0) {
readLine2 = readLine();
}
checkForFinalBoundary(readLine2);
}
private static InputStream trimCrlf(byte[] bArr) {
int length = bArr.length;
if (length > 0 && bArr[length - 1] == 10) {
length--;
}
if (length > 0 && bArr[length - 1] == 13) {
length--;
}
return new ByteArrayInputStream(bArr, 0, length);
}
}