* 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>
224 lines
7.7 KiB
Java
224 lines
7.7 KiB
Java
package com.google.api.client.googleapis.media;
|
|
|
|
import com.google.api.client.http.GenericUrl;
|
|
import com.google.api.client.http.HttpHeaders;
|
|
import com.google.api.client.http.HttpRequest;
|
|
import com.google.api.client.http.HttpRequestFactory;
|
|
import com.google.api.client.http.HttpRequestInitializer;
|
|
import com.google.api.client.http.HttpResponse;
|
|
import com.google.api.client.http.HttpTransport;
|
|
import com.google.api.client.util.Preconditions;
|
|
import com.google.common.base.MoreObjects;
|
|
import com.google.common.io.ByteStreams;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
|
|
/* loaded from: classes3.dex */
|
|
public final class MediaHttpDownloader {
|
|
public static final int MAXIMUM_CHUNK_SIZE = 33554432;
|
|
private long bytesDownloaded;
|
|
private long mediaContentLength;
|
|
private MediaHttpDownloaderProgressListener progressListener;
|
|
private final HttpRequestFactory requestFactory;
|
|
private final HttpTransport transport;
|
|
private boolean directDownloadEnabled = false;
|
|
private int chunkSize = MAXIMUM_CHUNK_SIZE;
|
|
private DownloadState downloadState = DownloadState.NOT_STARTED;
|
|
private long lastBytePos = -1;
|
|
|
|
/* loaded from: classes3.dex */
|
|
public enum DownloadState {
|
|
NOT_STARTED,
|
|
MEDIA_IN_PROGRESS,
|
|
MEDIA_COMPLETE
|
|
}
|
|
|
|
public MediaHttpDownloader(HttpTransport httpTransport, HttpRequestInitializer httpRequestInitializer) {
|
|
HttpRequestFactory createRequestFactory;
|
|
this.transport = (HttpTransport) Preconditions.checkNotNull(httpTransport);
|
|
if (httpRequestInitializer == null) {
|
|
createRequestFactory = httpTransport.createRequestFactory();
|
|
} else {
|
|
createRequestFactory = httpTransport.createRequestFactory(httpRequestInitializer);
|
|
}
|
|
this.requestFactory = createRequestFactory;
|
|
}
|
|
|
|
private HttpResponse executeCurrentRequest(long j, GenericUrl genericUrl, HttpHeaders httpHeaders, OutputStream outputStream) {
|
|
HttpRequest buildGetRequest = this.requestFactory.buildGetRequest(genericUrl);
|
|
if (httpHeaders != null) {
|
|
buildGetRequest.getHeaders().putAll(httpHeaders);
|
|
}
|
|
if (this.bytesDownloaded != 0 || j != -1) {
|
|
StringBuilder sb = new StringBuilder("bytes=");
|
|
sb.append(this.bytesDownloaded);
|
|
sb.append("-");
|
|
if (j != -1) {
|
|
sb.append(j);
|
|
}
|
|
buildGetRequest.getHeaders().setRange(sb.toString());
|
|
}
|
|
HttpResponse execute = buildGetRequest.execute();
|
|
try {
|
|
InputStream content = execute.getContent();
|
|
int i = ByteStreams.f12470a;
|
|
content.getClass();
|
|
outputStream.getClass();
|
|
byte[] bArr = new byte[8192];
|
|
while (true) {
|
|
int read = content.read(bArr);
|
|
if (read == -1) {
|
|
return execute;
|
|
}
|
|
outputStream.write(bArr, 0, read);
|
|
}
|
|
} finally {
|
|
execute.disconnect();
|
|
}
|
|
}
|
|
|
|
private long getNextByteIndex(String str) {
|
|
if (str == null) {
|
|
return 0L;
|
|
}
|
|
return Long.parseLong(str.substring(str.indexOf(45) + 1, str.indexOf(47))) + 1;
|
|
}
|
|
|
|
private void setMediaContentLength(String str) {
|
|
if (str != null && this.mediaContentLength == 0) {
|
|
this.mediaContentLength = Long.parseLong(str.substring(str.indexOf(47) + 1));
|
|
}
|
|
}
|
|
|
|
private void updateStateAndNotifyListener(DownloadState downloadState) {
|
|
this.downloadState = downloadState;
|
|
MediaHttpDownloaderProgressListener mediaHttpDownloaderProgressListener = this.progressListener;
|
|
if (mediaHttpDownloaderProgressListener != null) {
|
|
mediaHttpDownloaderProgressListener.progressChanged(this);
|
|
}
|
|
}
|
|
|
|
public void download(GenericUrl genericUrl, OutputStream outputStream) {
|
|
download(genericUrl, null, outputStream);
|
|
}
|
|
|
|
public int getChunkSize() {
|
|
return this.chunkSize;
|
|
}
|
|
|
|
public DownloadState getDownloadState() {
|
|
return this.downloadState;
|
|
}
|
|
|
|
public long getLastBytePosition() {
|
|
return this.lastBytePos;
|
|
}
|
|
|
|
public long getNumBytesDownloaded() {
|
|
return this.bytesDownloaded;
|
|
}
|
|
|
|
public double getProgress() {
|
|
long j = this.mediaContentLength;
|
|
if (j == 0) {
|
|
return 0.0d;
|
|
}
|
|
return this.bytesDownloaded / j;
|
|
}
|
|
|
|
public MediaHttpDownloaderProgressListener getProgressListener() {
|
|
return this.progressListener;
|
|
}
|
|
|
|
public HttpTransport getTransport() {
|
|
return this.transport;
|
|
}
|
|
|
|
public boolean isDirectDownloadEnabled() {
|
|
return this.directDownloadEnabled;
|
|
}
|
|
|
|
public MediaHttpDownloader setBytesDownloaded(long j) {
|
|
boolean z;
|
|
if (j >= 0) {
|
|
z = true;
|
|
} else {
|
|
z = false;
|
|
}
|
|
Preconditions.checkArgument(z);
|
|
this.bytesDownloaded = j;
|
|
return this;
|
|
}
|
|
|
|
public MediaHttpDownloader setChunkSize(int i) {
|
|
boolean z;
|
|
if (i > 0 && i <= 33554432) {
|
|
z = true;
|
|
} else {
|
|
z = false;
|
|
}
|
|
Preconditions.checkArgument(z);
|
|
this.chunkSize = i;
|
|
return this;
|
|
}
|
|
|
|
public MediaHttpDownloader setContentRange(long j, long j2) {
|
|
Preconditions.checkArgument(j2 >= j);
|
|
setBytesDownloaded(j);
|
|
this.lastBytePos = j2;
|
|
return this;
|
|
}
|
|
|
|
public MediaHttpDownloader setDirectDownloadEnabled(boolean z) {
|
|
this.directDownloadEnabled = z;
|
|
return this;
|
|
}
|
|
|
|
public MediaHttpDownloader setProgressListener(MediaHttpDownloaderProgressListener mediaHttpDownloaderProgressListener) {
|
|
this.progressListener = mediaHttpDownloaderProgressListener;
|
|
return this;
|
|
}
|
|
|
|
public void download(GenericUrl genericUrl, HttpHeaders httpHeaders, OutputStream outputStream) {
|
|
Preconditions.checkArgument(this.downloadState == DownloadState.NOT_STARTED);
|
|
genericUrl.put("alt", "media");
|
|
if (this.directDownloadEnabled) {
|
|
updateStateAndNotifyListener(DownloadState.MEDIA_IN_PROGRESS);
|
|
long longValue = ((Long) MoreObjects.a(executeCurrentRequest(this.lastBytePos, genericUrl, httpHeaders, outputStream).getHeaders().getContentLength(), Long.valueOf(this.mediaContentLength))).longValue();
|
|
this.mediaContentLength = longValue;
|
|
this.bytesDownloaded = longValue;
|
|
updateStateAndNotifyListener(DownloadState.MEDIA_COMPLETE);
|
|
return;
|
|
}
|
|
while (true) {
|
|
long j = (this.bytesDownloaded + this.chunkSize) - 1;
|
|
long j2 = this.lastBytePos;
|
|
if (j2 != -1) {
|
|
j = Math.min(j2, j);
|
|
}
|
|
String contentRange = executeCurrentRequest(j, genericUrl, httpHeaders, outputStream).getHeaders().getContentRange();
|
|
long nextByteIndex = getNextByteIndex(contentRange);
|
|
setMediaContentLength(contentRange);
|
|
long j3 = this.lastBytePos;
|
|
if (j3 != -1 && j3 <= nextByteIndex) {
|
|
this.bytesDownloaded = j3;
|
|
updateStateAndNotifyListener(DownloadState.MEDIA_COMPLETE);
|
|
return;
|
|
}
|
|
long j4 = this.mediaContentLength;
|
|
if (j4 <= nextByteIndex) {
|
|
this.bytesDownloaded = j4;
|
|
updateStateAndNotifyListener(DownloadState.MEDIA_COMPLETE);
|
|
return;
|
|
} else {
|
|
this.bytesDownloaded = nextByteIndex;
|
|
updateStateAndNotifyListener(DownloadState.MEDIA_IN_PROGRESS);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Deprecated
|
|
public MediaHttpDownloader setContentRange(long j, int i) {
|
|
return setContentRange(j, i);
|
|
}
|
|
}
|