builder = ImmutableMap.builder();
+ for (Type type : values()) {
+ builder.put(type.code(), type);
+ }
+ FROM_SHORT = builder.build();
+ }
+
+ Type(int code) {
+ this.code = Shorts.checkedCast(code);
+ }
+
+ public short code() {
+ return code;
+ }
+
+ public static Type fromCode(short code) {
+ return Preconditions.checkNotNull(FROM_SHORT.get(code), "Unknown chunk type: %s", code);
+ }
}
- Type(int code) {
- this.code = Shorts.checkedCast(code);
+ /**
+ * The byte boundary to pad chunks on.
+ */
+ public static final int PAD_BOUNDARY = 4;
+
+ /**
+ * The number of bytes in every chunk that describes chunk type, header size, and chunk size.
+ */
+ public static final int METADATA_SIZE = 8;
+
+ /**
+ * The offset in bytes, from the start of the chunk, where the chunk size can be found.
+ */
+ private static final int CHUNK_SIZE_OFFSET = 4;
+
+ /**
+ * The parent to this chunk, if any.
+ */
+ @Nullable
+ private final Chunk parent;
+
+ /**
+ * Size of the chunk header in bytes.
+ */
+ protected final int headerSize;
+
+ /**
+ * headerSize + dataSize. The total size of this chunk.
+ */
+ protected final int chunkSize;
+
+ /**
+ * Offset of this chunk from the start of the file.
+ */
+ protected final int offset;
+
+ protected Chunk(ByteBuffer buffer, @Nullable Chunk parent) {
+ this.parent = parent;
+ offset = buffer.position() - 2;
+ headerSize = (buffer.getShort() & 0xFFFF);
+ chunkSize = buffer.getInt();
}
- public short code() {
- return code;
+ /**
+ * Finishes initialization of a chunk. This should be called immediately after the constructor.
+ * This is separate from the constructor so that the header of a chunk can be fully initialized
+ * before the payload of that chunk is initialized for chunks that require such behavior.
+ *
+ * @param buffer The buffer that the payload will be initialized from.
+ */
+ protected void init(ByteBuffer buffer) {
}
- public static Type fromCode(short code) {
- return Preconditions.checkNotNull(FROM_SHORT.get(code), "Unknown chunk type: %s", code);
- }
- }
-
- /** The byte boundary to pad chunks on. */
- public static final int PAD_BOUNDARY = 4;
-
- /** The number of bytes in every chunk that describes chunk type, header size, and chunk size. */
- public static final int METADATA_SIZE = 8;
-
- /** The offset in bytes, from the start of the chunk, where the chunk size can be found. */
- private static final int CHUNK_SIZE_OFFSET = 4;
-
- /** The parent to this chunk, if any. */
- @Nullable
- private final Chunk parent;
-
- /** Size of the chunk header in bytes. */
- protected final int headerSize;
-
- /** headerSize + dataSize. The total size of this chunk. */
- protected final int chunkSize;
-
- /** Offset of this chunk from the start of the file. */
- protected final int offset;
-
- protected Chunk(ByteBuffer buffer, @Nullable Chunk parent) {
- this.parent = parent;
- offset = buffer.position() - 2;
- headerSize = (buffer.getShort() & 0xFFFF);
- chunkSize = buffer.getInt();
- }
-
- /**
- * Finishes initialization of a chunk. This should be called immediately after the constructor.
- * This is separate from the constructor so that the header of a chunk can be fully initialized
- * before the payload of that chunk is initialized for chunks that require such behavior.
- *
- * @param buffer The buffer that the payload will be initialized from.
- */
- protected void init(ByteBuffer buffer) {}
-
- /**
- * Returns the parent to this chunk, if any. A parent is a chunk whose payload contains this
- * chunk. If there's no parent, null is returned.
- */
- @Nullable
- public Chunk getParent() {
- return parent;
- }
-
- protected abstract Type getType();
-
- /** Returns the size of this chunk's header. */
- public final int getHeaderSize() {
- return headerSize;
- }
-
- /**
- * Returns the size of this chunk when it was first read from a buffer. A chunk's size can deviate
- * from this value when its data is modified (e.g. adding an entry, changing a string).
- *
- * A chunk's current size can be determined from the length of the byte array returned from
- * {@link #toByteArray}.
- */
- public final int getOriginalChunkSize() {
- return chunkSize;
- }
-
- /**
- * Reposition the buffer after this chunk. Use this at the end of a Chunk constructor.
- * @param buffer The buffer to be repositioned.
- */
- private final void seekToEndOfChunk(ByteBuffer buffer) {
- buffer.position(offset + chunkSize);
- }
-
- /**
- * Writes the type and header size. We don't know how big this chunk will be (it could be
- * different since the last time we checked), so this needs to be passed in.
- *
- * @param output The buffer that will be written to.
- * @param chunkSize The total size of this chunk in bytes, including the header.
- */
- protected final void writeHeader(ByteBuffer output, int chunkSize) {
- int start = output.position();
- output.putShort(getType().code());
- output.putShort((short) headerSize);
- output.putInt(chunkSize);
- writeHeader(output);
- int headerBytes = output.position() - start;
- Preconditions.checkState(headerBytes == getHeaderSize(),
- "Written header is wrong size. Got %s, want %s", headerBytes, getHeaderSize());
- }
-
- /**
- * Writes the remaining header (after the type, {@code headerSize}, and {@code chunkSize}).
- *
- * @param output The buffer that the header will be written to.
- */
- protected void writeHeader(ByteBuffer output) {}
-
- /**
- * Writes the chunk payload. The payload is data in a chunk which is not in
- * the first {@code headerSize} bytes of the chunk.
- *
- * @param output The stream that the payload will be written to.
- * @param header The already-written header. This can be modified to fix payload offsets.
- * @param shrink True if this payload should be optimized for size.
- * @throws IOException Thrown if {@code output} could not be written to (out of memory).
- */
- protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
- throws IOException {}
-
- /**
- * Pads {@code output} until {@code currentLength} is on a 4-byte boundary.
- *
- * @param output The {@link DataOutput} that will be padded.
- * @param currentLength The current length, in bytes, of {@code output}
- * @return The new length of {@code output}
- * @throws IOException Thrown if {@code output} could not be written to.
- */
- protected int writePad(DataOutput output, int currentLength) throws IOException {
- while (currentLength % PAD_BOUNDARY != 0) {
- output.write(0);
- ++currentLength;
- }
- return currentLength;
- }
-
- @Override
- public final byte[] toByteArray() throws IOException {
- return toByteArray(false);
- }
-
- /**
- * Converts this chunk into an array of bytes representation. Normally you will not need to
- * override this method unless your header changes based on the contents / size of the payload.
- */
- @Override
- public final byte[] toByteArray(boolean shrink) throws IOException {
- ByteBuffer header = ByteBuffer.allocate(getHeaderSize()).order(ByteOrder.LITTLE_ENDIAN);
- writeHeader(header, 0); // The chunk size isn't known yet. This will be filled in later.
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
-
- try (LittleEndianDataOutputStream payload = new LittleEndianDataOutputStream(baos)) {
- writePayload(payload, header, shrink);
+ /**
+ * Returns the parent to this chunk, if any. A parent is a chunk whose payload contains this
+ * chunk. If there's no parent, null is returned.
+ */
+ @Nullable
+ public Chunk getParent() {
+ return parent;
}
- byte[] payloadBytes = baos.toByteArray();
- int chunkSize = getHeaderSize() + payloadBytes.length;
- header.putInt(CHUNK_SIZE_OFFSET, chunkSize);
+ protected abstract Type getType();
- // Combine results
- ByteBuffer result = ByteBuffer.allocate(chunkSize).order(ByteOrder.LITTLE_ENDIAN);
- result.put(header.array());
- result.put(payloadBytes);
- return result.array();
- }
+ /**
+ * Returns the size of this chunk's header.
+ */
+ public final int getHeaderSize() {
+ return headerSize;
+ }
- /**
- * Creates a new chunk whose contents start at {@code buffer}'s current position.
- *
- * @param buffer A buffer positioned at the start of a chunk.
- * @return new chunk
- */
- public static Chunk newInstance(ByteBuffer buffer) {
- return newInstance(buffer, null);
- }
+ /**
+ * Returns the size of this chunk when it was first read from a buffer. A chunk's size can deviate
+ * from this value when its data is modified (e.g. adding an entry, changing a string).
+ *
+ *
A chunk's current size can be determined from the length of the byte array returned from
+ * {@link #toByteArray}.
+ */
+ public final int getOriginalChunkSize() {
+ return chunkSize;
+ }
- /**
- * Creates a new chunk whose contents start at {@code buffer}'s current position.
- *
- * @param buffer A buffer positioned at the start of a chunk.
- * @param parent The parent to this chunk (or null if there's no parent).
- * @return new chunk
- */
- public static Chunk newInstance(ByteBuffer buffer, @Nullable Chunk parent) {
- Chunk result;
- Type type = Type.fromCode(buffer.getShort());
- switch (type) {
- case STRING_POOL:
- result = new StringPoolChunk(buffer, parent);
- break;
- case TABLE:
- result = new ResourceTableChunk(buffer, parent);
- break;
- case XML:
- result = new XmlChunk(buffer, parent);
- break;
- case XML_START_NAMESPACE:
- result = new XmlNamespaceStartChunk(buffer, parent);
- break;
- case XML_END_NAMESPACE:
- result = new XmlNamespaceEndChunk(buffer, parent);
- break;
- case XML_START_ELEMENT:
- result = new XmlStartElementChunk(buffer, parent);
- break;
- case XML_END_ELEMENT:
- result = new XmlEndElementChunk(buffer, parent);
- break;
- case XML_CDATA:
- result = new XmlCdataChunk(buffer, parent);
- break;
- case XML_RESOURCE_MAP:
- result = new XmlResourceMapChunk(buffer, parent);
- break;
- case TABLE_PACKAGE:
- result = new PackageChunk(buffer, parent);
- break;
- case TABLE_TYPE:
- result = new TypeChunk(buffer, parent);
- break;
- case TABLE_TYPE_SPEC:
- result = new TypeSpecChunk(buffer, parent);
- break;
- case TABLE_LIBRARY:
- result = new LibraryChunk(buffer, parent);
- break;
- default:
- result = new UnknownChunk(buffer, parent);
+ /**
+ * Reposition the buffer after this chunk. Use this at the end of a Chunk constructor.
+ *
+ * @param buffer The buffer to be repositioned.
+ */
+ private final void seekToEndOfChunk(ByteBuffer buffer) {
+ buffer.position(offset + chunkSize);
+ }
+
+ /**
+ * Writes the type and header size. We don't know how big this chunk will be (it could be
+ * different since the last time we checked), so this needs to be passed in.
+ *
+ * @param output The buffer that will be written to.
+ * @param chunkSize The total size of this chunk in bytes, including the header.
+ */
+ protected final void writeHeader(ByteBuffer output, int chunkSize) {
+ int start = output.position();
+ output.putShort(getType().code());
+ output.putShort((short) headerSize);
+ output.putInt(chunkSize);
+ writeHeader(output);
+ int headerBytes = output.position() - start;
+ Preconditions.checkState(headerBytes == getHeaderSize(),
+ "Written header is wrong size. Got %s, want %s", headerBytes, getHeaderSize());
+ }
+
+ /**
+ * Writes the remaining header (after the type, {@code headerSize}, and {@code chunkSize}).
+ *
+ * @param output The buffer that the header will be written to.
+ */
+ protected void writeHeader(ByteBuffer output) {
+ }
+
+ /**
+ * Writes the chunk payload. The payload is data in a chunk which is not in
+ * the first {@code headerSize} bytes of the chunk.
+ *
+ * @param output The stream that the payload will be written to.
+ * @param header The already-written header. This can be modified to fix payload offsets.
+ * @param shrink True if this payload should be optimized for size.
+ * @throws IOException Thrown if {@code output} could not be written to (out of memory).
+ */
+ protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
+ throws IOException {
+ }
+
+ /**
+ * Pads {@code output} until {@code currentLength} is on a 4-byte boundary.
+ *
+ * @param output The {@link DataOutput} that will be padded.
+ * @param currentLength The current length, in bytes, of {@code output}
+ * @return The new length of {@code output}
+ * @throws IOException Thrown if {@code output} could not be written to.
+ */
+ protected int writePad(DataOutput output, int currentLength) throws IOException {
+ while (currentLength % PAD_BOUNDARY != 0) {
+ output.write(0);
+ ++currentLength;
+ }
+ return currentLength;
+ }
+
+ @Override
+ public final byte[] toByteArray() throws IOException {
+ return toByteArray(false);
+ }
+
+ /**
+ * Converts this chunk into an array of bytes representation. Normally you will not need to
+ * override this method unless your header changes based on the contents / size of the payload.
+ */
+ @Override
+ public final byte[] toByteArray(boolean shrink) throws IOException {
+ ByteBuffer header = ByteBuffer.allocate(getHeaderSize()).order(ByteOrder.LITTLE_ENDIAN);
+ writeHeader(header, 0); // The chunk size isn't known yet. This will be filled in later.
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ try (LittleEndianDataOutputStream payload = new LittleEndianDataOutputStream(baos)) {
+ writePayload(payload, header, shrink);
+ }
+
+ byte[] payloadBytes = baos.toByteArray();
+ int chunkSize = getHeaderSize() + payloadBytes.length;
+ header.putInt(CHUNK_SIZE_OFFSET, chunkSize);
+
+ // Combine results
+ ByteBuffer result = ByteBuffer.allocate(chunkSize).order(ByteOrder.LITTLE_ENDIAN);
+ result.put(header.array());
+ result.put(payloadBytes);
+ return result.array();
+ }
+
+ /**
+ * Creates a new chunk whose contents start at {@code buffer}'s current position.
+ *
+ * @param buffer A buffer positioned at the start of a chunk.
+ * @return new chunk
+ */
+ public static Chunk newInstance(ByteBuffer buffer) {
+ return newInstance(buffer, null);
+ }
+
+ /**
+ * Creates a new chunk whose contents start at {@code buffer}'s current position.
+ *
+ * @param buffer A buffer positioned at the start of a chunk.
+ * @param parent The parent to this chunk (or null if there's no parent).
+ * @return new chunk
+ */
+ public static Chunk newInstance(ByteBuffer buffer, @Nullable Chunk parent) {
+ Chunk result;
+ Type type = Type.fromCode(buffer.getShort());
+ switch (type) {
+ case STRING_POOL:
+ result = new StringPoolChunk(buffer, parent);
+ break;
+ case TABLE:
+ result = new ResourceTableChunk(buffer, parent);
+ break;
+ case XML:
+ result = new XmlChunk(buffer, parent);
+ break;
+ case XML_START_NAMESPACE:
+ result = new XmlNamespaceStartChunk(buffer, parent);
+ break;
+ case XML_END_NAMESPACE:
+ result = new XmlNamespaceEndChunk(buffer, parent);
+ break;
+ case XML_START_ELEMENT:
+ result = new XmlStartElementChunk(buffer, parent);
+ break;
+ case XML_END_ELEMENT:
+ result = new XmlEndElementChunk(buffer, parent);
+ break;
+ case XML_CDATA:
+ result = new XmlCdataChunk(buffer, parent);
+ break;
+ case XML_RESOURCE_MAP:
+ result = new XmlResourceMapChunk(buffer, parent);
+ break;
+ case TABLE_PACKAGE:
+ result = new PackageChunk(buffer, parent);
+ break;
+ case TABLE_TYPE:
+ result = new TypeChunk(buffer, parent);
+ break;
+ case TABLE_TYPE_SPEC:
+ result = new TypeSpecChunk(buffer, parent);
+ break;
+ case TABLE_LIBRARY:
+ result = new LibraryChunk(buffer, parent);
+ break;
+ default:
+ result = new UnknownChunk(buffer, parent);
+ }
+ result.init(buffer);
+ result.seekToEndOfChunk(buffer);
+ return result;
}
- result.init(buffer);
- result.seekToEndOfChunk(buffer);
- return result;
- }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/ChunkWithChunks.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/ChunkWithChunks.java
index 11ba3a41f..8a311a2b7 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/ChunkWithChunks.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/ChunkWithChunks.java
@@ -24,50 +24,52 @@ import java.nio.ByteBuffer;
import java.util.LinkedHashMap;
import java.util.Map;
-/** Represents a chunk whose payload is a list of sub-chunks. */
+/**
+ * Represents a chunk whose payload is a list of sub-chunks.
+ */
public abstract class ChunkWithChunks extends Chunk {
- private final Map chunks = new LinkedHashMap<>();
+ private final Map chunks = new LinkedHashMap<>();
- protected ChunkWithChunks(ByteBuffer buffer, @Nullable Chunk parent) {
- super(buffer, parent);
- }
-
- @Override
- protected void init(ByteBuffer buffer) {
- super.init(buffer);
- chunks.clear();
- int start = this.offset + getHeaderSize();
- int offset = start;
- int end = this.offset + getOriginalChunkSize();
- int position = buffer.position();
- buffer.position(start);
-
- while (offset < end) {
- Chunk chunk = Chunk.newInstance(buffer, this);
- chunks.put(offset, chunk);
- offset += chunk.getOriginalChunkSize();
+ protected ChunkWithChunks(ByteBuffer buffer, @Nullable Chunk parent) {
+ super(buffer, parent);
}
- buffer.position(position);
- }
+ @Override
+ protected void init(ByteBuffer buffer) {
+ super.init(buffer);
+ chunks.clear();
+ int start = this.offset + getHeaderSize();
+ int offset = start;
+ int end = this.offset + getOriginalChunkSize();
+ int position = buffer.position();
+ buffer.position(start);
- /**
- * Retrieves the @{code chunks} contained in this chunk.
- *
- * @return map of buffer offset -> chunk contained in this chunk.
- */
- public final Map getChunks() {
- return chunks;
- }
+ while (offset < end) {
+ Chunk chunk = Chunk.newInstance(buffer, this);
+ chunks.put(offset, chunk);
+ offset += chunk.getOriginalChunkSize();
+ }
- @Override
- protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
- throws IOException {
- for (Chunk chunk : getChunks().values()) {
- byte[] chunkBytes = chunk.toByteArray(shrink);
- output.write(chunkBytes);
- writePad(output, chunkBytes.length);
+ buffer.position(position);
+ }
+
+ /**
+ * Retrieves the @{code chunks} contained in this chunk.
+ *
+ * @return map of buffer offset -> chunk contained in this chunk.
+ */
+ public final Map getChunks() {
+ return chunks;
+ }
+
+ @Override
+ protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
+ throws IOException {
+ for (Chunk chunk : getChunks().values()) {
+ byte[] chunkBytes = chunk.toByteArray(shrink);
+ output.write(chunkBytes);
+ writePad(output, chunkBytes.length);
+ }
}
- }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/LibraryChunk.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/LibraryChunk.java
index 38236de8b..4a31546f4 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/LibraryChunk.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/LibraryChunk.java
@@ -17,6 +17,7 @@
package com.google.devrel.gmscore.tools.apk.arsc;
import androidx.annotation.Nullable;
+
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -32,105 +33,121 @@ import java.util.Objects;
*/
public final class LibraryChunk extends Chunk {
- /** The number of resources of this type at creation time. */
- private final int entryCount;
+ /**
+ * The number of resources of this type at creation time.
+ */
+ private final int entryCount;
- /** The libraries used in this chunk (package id + name). */
- private final List entries = new ArrayList<>();
+ /**
+ * The libraries used in this chunk (package id + name).
+ */
+ private final List entries = new ArrayList<>();
- protected LibraryChunk(ByteBuffer buffer, @Nullable Chunk parent) {
- super(buffer, parent);
- entryCount = buffer.getInt();
- }
-
- @Override
- protected void init(ByteBuffer buffer) {
- super.init(buffer);
- entries.addAll(enumerateEntries(buffer));
- }
-
- private List enumerateEntries(ByteBuffer buffer) {
- List result = new ArrayList<>(entryCount);
- int offset = this.offset + getHeaderSize();
- int endOffset = offset + Entry.SIZE * entryCount;
-
- while (offset < endOffset) {
- result.add(Entry.create(buffer, offset));
- offset += Entry.SIZE;
- }
- return result;
- }
-
- @Override
- protected Type getType() {
- return Type.TABLE_LIBRARY;
- }
-
- @Override
- protected void writeHeader(ByteBuffer output) {
- super.writeHeader(output);
- output.putInt(entries.size());
- }
-
- @Override
- protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
- throws IOException {
- for (Entry entry : entries) {
- output.write(entry.toByteArray(shrink));
- }
- }
-
- /** A shared library package-id to package name entry. */
- protected static class Entry implements SerializableResource {
-
- /** Library entries only contain a package ID (4 bytes) and a package name. */
- private static final int SIZE = 4 + PackageUtils.PACKAGE_NAME_SIZE;
-
- private final int packageId;
- private final String packageName;
-
- static Entry create(ByteBuffer buffer, int offset) {
- int packageId = buffer.getInt(offset);
- String packageName = PackageUtils.readPackageName(buffer, offset + 4);
- return new Entry(packageId, packageName);
- }
-
- private Entry(int packageId, String packageName) {
- this.packageId = packageId;
- this.packageName = packageName;
- }
-
- /** The id assigned to the shared library at build time. */
- public int packageId() { return packageId; }
-
- /** The package name of the shared library. */
- public String packageName() { return packageName; }
-
- @Override
- public byte[] toByteArray() throws IOException {
- return toByteArray(false);
+ protected LibraryChunk(ByteBuffer buffer, @Nullable Chunk parent) {
+ super(buffer, parent);
+ entryCount = buffer.getInt();
}
@Override
- public byte[] toByteArray(boolean shrink) throws IOException {
- ByteBuffer buffer = ByteBuffer.allocate(SIZE).order(ByteOrder.LITTLE_ENDIAN);
- buffer.putInt(packageId());
- PackageUtils.writePackageName(buffer, packageName());
- return buffer.array();
+ protected void init(ByteBuffer buffer) {
+ super.init(buffer);
+ entries.addAll(enumerateEntries(buffer));
+ }
+
+ private List enumerateEntries(ByteBuffer buffer) {
+ List result = new ArrayList<>(entryCount);
+ int offset = this.offset + getHeaderSize();
+ int endOffset = offset + Entry.SIZE * entryCount;
+
+ while (offset < endOffset) {
+ result.add(Entry.create(buffer, offset));
+ offset += Entry.SIZE;
+ }
+ return result;
}
@Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Entry entry = (Entry)o;
- return packageId == entry.packageId &&
- Objects.equals(packageName, entry.packageName);
+ protected Type getType() {
+ return Type.TABLE_LIBRARY;
}
@Override
- public int hashCode() {
- return Objects.hash(packageId, packageName);
+ protected void writeHeader(ByteBuffer output) {
+ super.writeHeader(output);
+ output.putInt(entries.size());
+ }
+
+ @Override
+ protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
+ throws IOException {
+ for (Entry entry : entries) {
+ output.write(entry.toByteArray(shrink));
+ }
+ }
+
+ /**
+ * A shared library package-id to package name entry.
+ */
+ protected static class Entry implements SerializableResource {
+
+ /**
+ * Library entries only contain a package ID (4 bytes) and a package name.
+ */
+ private static final int SIZE = 4 + PackageUtils.PACKAGE_NAME_SIZE;
+
+ private final int packageId;
+ private final String packageName;
+
+ static Entry create(ByteBuffer buffer, int offset) {
+ int packageId = buffer.getInt(offset);
+ String packageName = PackageUtils.readPackageName(buffer, offset + 4);
+ return new Entry(packageId, packageName);
+ }
+
+ private Entry(int packageId, String packageName) {
+ this.packageId = packageId;
+ this.packageName = packageName;
+ }
+
+ /**
+ * The id assigned to the shared library at build time.
+ */
+ public int packageId() {
+ return packageId;
+ }
+
+ /**
+ * The package name of the shared library.
+ */
+ public String packageName() {
+ return packageName;
+ }
+
+ @Override
+ public byte[] toByteArray() throws IOException {
+ return toByteArray(false);
+ }
+
+ @Override
+ public byte[] toByteArray(boolean shrink) throws IOException {
+ ByteBuffer buffer = ByteBuffer.allocate(SIZE).order(ByteOrder.LITTLE_ENDIAN);
+ buffer.putInt(packageId());
+ PackageUtils.writePackageName(buffer, packageName());
+ return buffer.array();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Entry entry = (Entry) o;
+ return packageId == entry.packageId &&
+ Objects.equals(packageName, entry.packageName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(packageId, packageName);
+ }
}
- }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/PackageChunk.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/PackageChunk.java
index cc8c2e32f..9ede60266 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/PackageChunk.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/PackageChunk.java
@@ -16,11 +16,11 @@
package com.google.devrel.gmscore.tools.apk.arsc;
+import androidx.annotation.Nullable;
import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
-import androidx.annotation.Nullable;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -29,211 +29,251 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
-/** A package chunk is a collection of resource data types within a package. */
+/**
+ * A package chunk is a collection of resource data types within a package.
+ */
public final class PackageChunk extends ChunkWithChunks {
- /** Offset in bytes, from the start of the chunk, where {@code typeStringsOffset} can be found. */
- private static final int TYPE_OFFSET_OFFSET = 268;
+ /**
+ * Offset in bytes, from the start of the chunk, where {@code typeStringsOffset} can be found.
+ */
+ private static final int TYPE_OFFSET_OFFSET = 268;
- /** Offset in bytes, from the start of the chunk, where {@code keyStringsOffset} can be found. */
- private static final int KEY_OFFSET_OFFSET = 276;
+ /**
+ * Offset in bytes, from the start of the chunk, where {@code keyStringsOffset} can be found.
+ */
+ private static final int KEY_OFFSET_OFFSET = 276;
- /** The package id if this is a base package, or 0 if not a base package. */
- private int id;
+ /**
+ * The package id if this is a base package, or 0 if not a base package.
+ */
+ private int id;
- /** The name of the package. */
- private String packageName;
+ /**
+ * The name of the package.
+ */
+ private String packageName;
- /** The offset (from {@code offset}) in the original buffer where type strings start. */
- private final int typeStringsOffset;
+ /**
+ * The offset (from {@code offset}) in the original buffer where type strings start.
+ */
+ private final int typeStringsOffset;
- /** The index into the type string pool of the last public type. */
- private final int lastPublicType;
+ /**
+ * The index into the type string pool of the last public type.
+ */
+ private final int lastPublicType;
- /** An offset to the string pool that contains the key strings for this package. */
- private final int keyStringsOffset;
+ /**
+ * An offset to the string pool that contains the key strings for this package.
+ */
+ private final int keyStringsOffset;
- /** The index into the key string pool of the last public key. */
- private final int lastPublicKey;
+ /**
+ * The index into the key string pool of the last public key.
+ */
+ private final int lastPublicKey;
- /** An offset to the type ID(s). This is undocumented in the original code. */
- private final int typeIdOffset;
+ /**
+ * An offset to the type ID(s). This is undocumented in the original code.
+ */
+ private final int typeIdOffset;
- /** Contains a mapping of a type index to its {@link TypeSpecChunk}. */
- private final Map typeSpecs = new HashMap<>();
+ /**
+ * Contains a mapping of a type index to its {@link TypeSpecChunk}.
+ */
+ private final Map typeSpecs = new HashMap<>();
- /** Contains a mapping of a type index to all of the {@link TypeChunk} with that index. */
- private final Multimap types = ArrayListMultimap.create();
+ /**
+ * Contains a mapping of a type index to all of the {@link TypeChunk} with that index.
+ */
+ private final Multimap types = ArrayListMultimap.create();
- /** May contain a library chunk for mapping dynamic references to resolved references. */
- private Optional libraryChunk = Optional.empty();
+ /**
+ * May contain a library chunk for mapping dynamic references to resolved references.
+ */
+ private Optional libraryChunk = Optional.empty();
- protected PackageChunk(ByteBuffer buffer, @Nullable Chunk parent) {
- super(buffer, parent);
- id = buffer.getInt();
- packageName = PackageUtils.readPackageName(buffer, buffer.position());
- typeStringsOffset = buffer.getInt();
- lastPublicType = buffer.getInt();
- keyStringsOffset = buffer.getInt();
- lastPublicKey = buffer.getInt();
- typeIdOffset = buffer.getInt();
- }
-
- @Override
- protected void init(ByteBuffer buffer) {
- super.init(buffer);
- for (Chunk chunk : getChunks().values()) {
- if (chunk instanceof TypeChunk) {
- TypeChunk typeChunk = (TypeChunk) chunk;
- types.put(typeChunk.getId(), typeChunk);
- } else if (chunk instanceof TypeSpecChunk) {
- TypeSpecChunk typeSpecChunk = (TypeSpecChunk) chunk;
- typeSpecs.put(typeSpecChunk.getId(), typeSpecChunk);
- } else if (chunk instanceof LibraryChunk) {
- if (libraryChunk.isPresent()) {
- throw new IllegalStateException(
- "Multiple library chunks present in package chunk.");
- }
- libraryChunk = Optional.of((LibraryChunk) chunk);
- } else if (!(chunk instanceof StringPoolChunk)) {
- throw new IllegalStateException(
- String.format("PackageChunk contains an unexpected chunk: %s", chunk.getClass()));
- }
+ protected PackageChunk(ByteBuffer buffer, @Nullable Chunk parent) {
+ super(buffer, parent);
+ id = buffer.getInt();
+ packageName = PackageUtils.readPackageName(buffer, buffer.position());
+ typeStringsOffset = buffer.getInt();
+ lastPublicType = buffer.getInt();
+ keyStringsOffset = buffer.getInt();
+ lastPublicKey = buffer.getInt();
+ typeIdOffset = buffer.getInt();
}
- }
- /** Returns the package id if this is a base package, or 0 if not a base package. */
- public int getId() {
- return id;
- }
-
- /** Sets the package id */
- public void setId(int id) {
- this.id = id;
- }
-
- /**
- * Returns the string pool that contains the names of the resources in this package.
- */
- public StringPoolChunk getKeyStringPool() {
- Chunk chunk = Preconditions.checkNotNull(getChunks().get(keyStringsOffset + offset));
- Preconditions.checkState(chunk instanceof StringPoolChunk, "Key string pool not found.");
- return (StringPoolChunk) chunk;
- }
-
- /**
- * Get the type string for a specific id, e.g., (e.g. string, attr, id).
- *
- * @param id The id to get the type for.
- * @return The type string.
- */
- public String getTypeString(int id) {
- StringPoolChunk typePool = getTypeStringPool();
- Preconditions.checkNotNull(typePool, "Package has no type pool.");
- Preconditions.checkState(typePool.getStyleCount() >= id, "No type for id: " + id);
- return typePool.getString(id - 1); // - 1 here to convert to 0-based index
- }
-
- /**
- * Returns the string pool that contains the type strings for this package, such as "layout",
- * "string", "color".
- */
- public StringPoolChunk getTypeStringPool() {
- Chunk chunk = Preconditions.checkNotNull(getChunks().get(typeStringsOffset + offset));
- Preconditions.checkState(chunk instanceof StringPoolChunk, "Type string pool not found.");
- return (StringPoolChunk) chunk;
- }
-
- /** Returns all {@link TypeChunk} in this package. */
- public Collection getTypeChunks() {
- return types.values();
- }
-
- /**
- * For a given type id, returns the {@link TypeChunk} objects that match that id. The type id is
- * the 1-based index of the type in the type string pool (returned by {@link #getTypeStringPool}).
- *
- * @param id The 1-based type id to return {@link TypeChunk} objects for.
- * @return The matching {@link TypeChunk} objects, or an empty collection if there are none.
- */
- public Collection getTypeChunks(int id) {
- return types.get(id);
- }
-
- /**
- * For a given type, returns the {@link TypeChunk} objects that match that type
- * (e.g. "attr", "id", "string", ...).
- *
- * @param type The type to return {@link TypeChunk} objects for.
- * @return The matching {@link TypeChunk} objects, or an empty collection if there are none.
- */
- public Collection getTypeChunks(String type) {
- StringPoolChunk typeStringPool = Preconditions.checkNotNull(getTypeStringPool());
- return getTypeChunks(typeStringPool.indexOf(type) + 1); // Convert 0-based index to 1-based
- }
-
- /** Returns all {@link TypeSpecChunk} in this package. */
- public Collection getTypeSpecChunks() {
- return typeSpecs.values();
- }
-
- /** For a given (1-based) type id, returns the {@link TypeSpecChunk} matching it. */
- public TypeSpecChunk getTypeSpecChunk(int id) {
- return Preconditions.checkNotNull(typeSpecs.get(id));
- }
-
- /**
- * For a given {@code type}, returns the {@link TypeSpecChunk} that matches it
- * (e.g. "attr", "id", "string", ...).
- */
- public TypeSpecChunk getTypeSpecChunk(String type) {
- StringPoolChunk typeStringPool = Preconditions.checkNotNull(getTypeStringPool());
- return getTypeSpecChunk(typeStringPool.indexOf(type) + 1); // Convert 0-based index to 1-based
- }
-
- /** Returns the name of this package. */
- public String getPackageName() {
- return packageName;
- }
-
- /** Set the package name */
- public void setPackageName(String packageName) {
- this.packageName = packageName;
- }
-
- @Override
- protected Type getType() {
- return Type.TABLE_PACKAGE;
- }
-
- @Override
- protected void writeHeader(ByteBuffer output) {
- output.putInt(id);
- PackageUtils.writePackageName(output, packageName);
- output.putInt(0); // typeStringsOffset. This value can't be computed here.
- output.putInt(lastPublicType);
- output.putInt(0); // keyStringsOffset. This value can't be computed here.
- output.putInt(lastPublicKey);
- output.putInt(typeIdOffset);
- }
-
- @Override
- protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
- throws IOException {
- int typeOffset = typeStringsOffset;
- int keyOffset = keyStringsOffset;
- int payloadOffset = 0;
- for (Chunk chunk : getChunks().values()) {
- if (chunk == getTypeStringPool()) {
- typeOffset = payloadOffset + getHeaderSize();
- } else if (chunk == getKeyStringPool()) {
- keyOffset = payloadOffset + getHeaderSize();
- }
- byte[] chunkBytes = chunk.toByteArray(shrink);
- output.write(chunkBytes);
- payloadOffset = writePad(output, chunkBytes.length);
+ @Override
+ protected void init(ByteBuffer buffer) {
+ super.init(buffer);
+ for (Chunk chunk : getChunks().values()) {
+ if (chunk instanceof TypeChunk) {
+ TypeChunk typeChunk = (TypeChunk) chunk;
+ types.put(typeChunk.getId(), typeChunk);
+ } else if (chunk instanceof TypeSpecChunk) {
+ TypeSpecChunk typeSpecChunk = (TypeSpecChunk) chunk;
+ typeSpecs.put(typeSpecChunk.getId(), typeSpecChunk);
+ } else if (chunk instanceof LibraryChunk) {
+ if (libraryChunk.isPresent()) {
+ throw new IllegalStateException(
+ "Multiple library chunks present in package chunk.");
+ }
+ libraryChunk = Optional.of((LibraryChunk) chunk);
+ } else if (!(chunk instanceof StringPoolChunk)) {
+ throw new IllegalStateException(
+ String.format("PackageChunk contains an unexpected chunk: %s", chunk.getClass()));
+ }
+ }
+ }
+
+ /**
+ * Returns the package id if this is a base package, or 0 if not a base package.
+ */
+ public int getId() {
+ return id;
+ }
+
+ /**
+ * Sets the package id
+ */
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ /**
+ * Returns the string pool that contains the names of the resources in this package.
+ */
+ public StringPoolChunk getKeyStringPool() {
+ Chunk chunk = Preconditions.checkNotNull(getChunks().get(keyStringsOffset + offset));
+ Preconditions.checkState(chunk instanceof StringPoolChunk, "Key string pool not found.");
+ return (StringPoolChunk) chunk;
+ }
+
+ /**
+ * Get the type string for a specific id, e.g., (e.g. string, attr, id).
+ *
+ * @param id The id to get the type for.
+ * @return The type string.
+ */
+ public String getTypeString(int id) {
+ StringPoolChunk typePool = getTypeStringPool();
+ Preconditions.checkNotNull(typePool, "Package has no type pool.");
+ Preconditions.checkState(typePool.getStyleCount() >= id, "No type for id: " + id);
+ return typePool.getString(id - 1); // - 1 here to convert to 0-based index
+ }
+
+ /**
+ * Returns the string pool that contains the type strings for this package, such as "layout",
+ * "string", "color".
+ */
+ public StringPoolChunk getTypeStringPool() {
+ Chunk chunk = Preconditions.checkNotNull(getChunks().get(typeStringsOffset + offset));
+ Preconditions.checkState(chunk instanceof StringPoolChunk, "Type string pool not found.");
+ return (StringPoolChunk) chunk;
+ }
+
+ /**
+ * Returns all {@link TypeChunk} in this package.
+ */
+ public Collection getTypeChunks() {
+ return types.values();
+ }
+
+ /**
+ * For a given type id, returns the {@link TypeChunk} objects that match that id. The type id is
+ * the 1-based index of the type in the type string pool (returned by {@link #getTypeStringPool}).
+ *
+ * @param id The 1-based type id to return {@link TypeChunk} objects for.
+ * @return The matching {@link TypeChunk} objects, or an empty collection if there are none.
+ */
+ public Collection getTypeChunks(int id) {
+ return types.get(id);
+ }
+
+ /**
+ * For a given type, returns the {@link TypeChunk} objects that match that type
+ * (e.g. "attr", "id", "string", ...).
+ *
+ * @param type The type to return {@link TypeChunk} objects for.
+ * @return The matching {@link TypeChunk} objects, or an empty collection if there are none.
+ */
+ public Collection getTypeChunks(String type) {
+ StringPoolChunk typeStringPool = Preconditions.checkNotNull(getTypeStringPool());
+ return getTypeChunks(typeStringPool.indexOf(type) + 1); // Convert 0-based index to 1-based
+ }
+
+ /**
+ * Returns all {@link TypeSpecChunk} in this package.
+ */
+ public Collection getTypeSpecChunks() {
+ return typeSpecs.values();
+ }
+
+ /**
+ * For a given (1-based) type id, returns the {@link TypeSpecChunk} matching it.
+ */
+ public TypeSpecChunk getTypeSpecChunk(int id) {
+ return Preconditions.checkNotNull(typeSpecs.get(id));
+ }
+
+ /**
+ * For a given {@code type}, returns the {@link TypeSpecChunk} that matches it
+ * (e.g. "attr", "id", "string", ...).
+ */
+ public TypeSpecChunk getTypeSpecChunk(String type) {
+ StringPoolChunk typeStringPool = Preconditions.checkNotNull(getTypeStringPool());
+ return getTypeSpecChunk(typeStringPool.indexOf(type) + 1); // Convert 0-based index to 1-based
+ }
+
+ /**
+ * Returns the name of this package.
+ */
+ public String getPackageName() {
+ return packageName;
+ }
+
+ /**
+ * Set the package name
+ */
+ public void setPackageName(String packageName) {
+ this.packageName = packageName;
+ }
+
+ @Override
+ protected Type getType() {
+ return Type.TABLE_PACKAGE;
+ }
+
+ @Override
+ protected void writeHeader(ByteBuffer output) {
+ output.putInt(id);
+ PackageUtils.writePackageName(output, packageName);
+ output.putInt(0); // typeStringsOffset. This value can't be computed here.
+ output.putInt(lastPublicType);
+ output.putInt(0); // keyStringsOffset. This value can't be computed here.
+ output.putInt(lastPublicKey);
+ output.putInt(typeIdOffset);
+ }
+
+ @Override
+ protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
+ throws IOException {
+ int typeOffset = typeStringsOffset;
+ int keyOffset = keyStringsOffset;
+ int payloadOffset = 0;
+ for (Chunk chunk : getChunks().values()) {
+ if (chunk == getTypeStringPool()) {
+ typeOffset = payloadOffset + getHeaderSize();
+ } else if (chunk == getKeyStringPool()) {
+ keyOffset = payloadOffset + getHeaderSize();
+ }
+ byte[] chunkBytes = chunk.toByteArray(shrink);
+ output.write(chunkBytes);
+ payloadOffset = writePad(output, chunkBytes.length);
+ }
+ header.putInt(TYPE_OFFSET_OFFSET, typeOffset);
+ header.putInt(KEY_OFFSET_OFFSET, keyOffset);
}
- header.putInt(TYPE_OFFSET_OFFSET, typeOffset);
- header.putInt(KEY_OFFSET_OFFSET, keyOffset);
- }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/PackageUtils.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/PackageUtils.java
index 5077ca6f0..6c5215762 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/PackageUtils.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/PackageUtils.java
@@ -20,48 +20,53 @@ import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
-/** Provides utility methods for package names. */
+/**
+ * Provides utility methods for package names.
+ */
public final class PackageUtils {
- public static final int PACKAGE_NAME_SIZE = 256;
+ public static final int PACKAGE_NAME_SIZE = 256;
- private PackageUtils() {} // Prevent instantiation
+ private PackageUtils() {
+ } // Prevent instantiation
- /**
- * Reads the package name from the buffer and repositions the buffer to point directly after
- * the package name.
- * @param buffer The buffer containing the package name.
- * @param offset The offset in the buffer to read from.
- * @return The package name.
- */
- public static String readPackageName(ByteBuffer buffer, int offset) {
- byte[] data = buffer.array();
- int length = 0;
- // Look for the null terminator for the string instead of using the entire buffer.
- // It's UTF-16 so check 2 bytes at a time to see if its double 0.
- for (int i = offset; i < data.length && i < PACKAGE_NAME_SIZE + offset; i += 2) {
- if (data[i] == 0 && data[i + 1] == 0) {
- length = i - offset;
- break;
- }
+ /**
+ * Reads the package name from the buffer and repositions the buffer to point directly after
+ * the package name.
+ *
+ * @param buffer The buffer containing the package name.
+ * @param offset The offset in the buffer to read from.
+ * @return The package name.
+ */
+ public static String readPackageName(ByteBuffer buffer, int offset) {
+ byte[] data = buffer.array();
+ int length = 0;
+ // Look for the null terminator for the string instead of using the entire buffer.
+ // It's UTF-16 so check 2 bytes at a time to see if its double 0.
+ for (int i = offset; i < data.length && i < PACKAGE_NAME_SIZE + offset; i += 2) {
+ if (data[i] == 0 && data[i + 1] == 0) {
+ length = i - offset;
+ break;
+ }
+ }
+ Charset utf16 = StandardCharsets.UTF_16LE;
+ String str = new String(data, offset, length, utf16);
+ buffer.position(offset + PACKAGE_NAME_SIZE);
+ return str;
}
- Charset utf16 = StandardCharsets.UTF_16LE;
- String str = new String(data, offset, length, utf16);
- buffer.position(offset + PACKAGE_NAME_SIZE);
- return str;
- }
- /**
- * Writes the provided package name to the buffer in UTF-16.
- * @param buffer The buffer that will be written to.
- * @param packageName The package name that will be written to the buffer.
- */
- public static void writePackageName(ByteBuffer buffer, String packageName) {
- byte[] nameBytes = packageName.getBytes(StandardCharsets.UTF_16LE);
- buffer.put(nameBytes, 0, Math.min(nameBytes.length, PACKAGE_NAME_SIZE));
- if (nameBytes.length < PACKAGE_NAME_SIZE) {
- // pad out the remaining space with an empty array.
- buffer.put(new byte[PACKAGE_NAME_SIZE - nameBytes.length]);
+ /**
+ * Writes the provided package name to the buffer in UTF-16.
+ *
+ * @param buffer The buffer that will be written to.
+ * @param packageName The package name that will be written to the buffer.
+ */
+ public static void writePackageName(ByteBuffer buffer, String packageName) {
+ byte[] nameBytes = packageName.getBytes(StandardCharsets.UTF_16LE);
+ buffer.put(nameBytes, 0, Math.min(nameBytes.length, PACKAGE_NAME_SIZE));
+ if (nameBytes.length < PACKAGE_NAME_SIZE) {
+ // pad out the remaining space with an empty array.
+ buffer.put(new byte[PACKAGE_NAME_SIZE - nameBytes.length]);
+ }
}
- }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/ResourceEntryStatsCollector.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/ResourceEntryStatsCollector.java
index 5891f0567..e7d3895ef 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/ResourceEntryStatsCollector.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/ResourceEntryStatsCollector.java
@@ -26,255 +26,276 @@ import java.util.Map.Entry;
/**
* Calculates extra information about an {@link ResourceEntry}, such as the total
* APK size the entry is responsible for.
- *
+ *
* This class is not thread-safe.
*/
public class ResourceEntryStatsCollector {
- /** The size in bytes of an offset in a chunk. */
- private static final int OFFSET_SIZE = 4;
+ /**
+ * The size in bytes of an offset in a chunk.
+ */
+ private static final int OFFSET_SIZE = 4;
- /** The size in bytes of overhead for styles, if present, in {@link StringPoolChunk}. */
- private static final int STYLE_OVERHEAD = 8;
+ /**
+ * The size in bytes of overhead for styles, if present, in {@link StringPoolChunk}.
+ */
+ private static final int STYLE_OVERHEAD = 8;
- /**
- * The number of bytes, in addition to the header, that the {@link PackageChunk} has in overhead
- * excluding the chunks it contains.
- */
- private static final int PACKAGE_CHUNK_OVERHEAD = 8;
+ /**
+ * The number of bytes, in addition to the header, that the {@link PackageChunk} has in overhead
+ * excluding the chunks it contains.
+ */
+ private static final int PACKAGE_CHUNK_OVERHEAD = 8;
- private final Map stats = new HashMap<>();
+ private final Map stats = new HashMap<>();
- private final ArscBlamer blamer;
+ private final ArscBlamer blamer;
- private final ResourceTableChunk resourceTable;
+ private final ResourceTableChunk resourceTable;
- /**
- * Creates a new {@link ResourceEntryStatsCollector}.
- *
- * @param blamer The blamer that maps resource entries to what they use.
- * @param resourceTable The resource table that {@code blamer} is blamed on.
- */
- public ResourceEntryStatsCollector(ArscBlamer blamer, ResourceTableChunk resourceTable) {
- this.resourceTable = resourceTable;
- this.blamer = blamer;
- }
-
- public void compute() throws IOException {
- Preconditions.checkState(stats.isEmpty(), "Must only call #compute once.");
- blamer.blame();
- computeStringPoolSizes();
- computePackageSizes();
- }
-
- /** Returns entries for which there are computed stats. Must first call {@link #compute}. */
- public Map getStats() {
- Preconditions.checkState(!stats.isEmpty(), "Must call #compute() first.");
- return Collections.unmodifiableMap(stats);
- }
-
- /** Returns computed stats for a given entry. Must first call {@link #compute}. */
- public ResourceStatistics getStats(ResourceEntry entry) {
- Preconditions.checkState(!stats.isEmpty(), "Must call #compute() first.");
- return stats.containsKey(entry) ? stats.get(entry) : ResourceStatistics.EMPTY;
- }
-
- private void computeStringPoolSizes() throws IOException {
- computePoolSizes(resourceTable.getStringPool(), blamer.getStringToBlamedResources());
- }
-
- private void computePackageSizes() throws IOException {
- computeTypePoolSizes();
- computeKeyPoolSizes();
- computeTypeSpecSizes();
- computeTypeChunkSizes();
- computePackageChunkSizes();
- }
-
- private void computeTypePoolSizes() throws IOException {
- for (Entry[]> entry
- : blamer.getTypeToBlamedResources().entrySet()) {
- computePoolSizes(entry.getKey().getTypeStringPool(), entry.getValue());
- }
- }
-
- private void computeKeyPoolSizes() throws IOException {
- for (Entry[]> entry
- : blamer.getKeyToBlamedResources().entrySet()) {
- computePoolSizes(entry.getKey().getKeyStringPool(), entry.getValue());
- }
- }
-
- private void computeTypeSpecSizes() {
- for (Entry[]> entry
- : blamer.getTypeToBlamedResources().entrySet()) {
- computeTypeSpecSizes(entry.getKey(), entry.getValue());
- }
- }
-
- private void computeTypeChunkSizes() {
- for (Entry> entry
- : blamer.getTypeEntryToBlamedResources().asMap().entrySet()) {
- TypeChunk.Entry chunkEntry = entry.getKey();
- TypeChunk typeChunk = chunkEntry.parent();
- int size = chunkEntry.size() + OFFSET_SIZE;
- int count = typeChunk.getEntries().size();
- int nullEntries = typeChunk.getTotalEntryCount() - typeChunk.getEntries().size();
- int overhead = typeChunk.getHeaderSize() + nullEntries * OFFSET_SIZE;
- addSizes(entry.getValue(), overhead, size, count);
- }
- }
-
- private void computePackageChunkSizes() {
- for (Entry> entry
- : blamer.getPackageToBlamedResources().asMap().entrySet()) {
- int overhead = entry.getKey().getHeaderSize() + PACKAGE_CHUNK_OVERHEAD;
- addSizes(entry.getValue(), overhead, 0, 1);
- }
- }
-
- private void computePoolSizes(StringPoolChunk stringPool,
- List[] usages) throws IOException {
- int overhead = stringPool.getHeaderSize();
- if (stringPool.getStyleCount() > 0) {
- overhead += STYLE_OVERHEAD;
+ /**
+ * Creates a new {@link ResourceEntryStatsCollector}.
+ *
+ * @param blamer The blamer that maps resource entries to what they use.
+ * @param resourceTable The resource table that {@code blamer} is blamed on.
+ */
+ public ResourceEntryStatsCollector(ArscBlamer blamer, ResourceTableChunk resourceTable) {
+ this.resourceTable = resourceTable;
+ this.blamer = blamer;
}
- // We have to iterate over the indices of the string pool, because it is possible that there are
- // indices which have *no* associated resource entry (i.e. references from XML files without an
- // entry in R).
- int count = 0;
- for (int i = 0; i < usages.length; ++i) {
- if (usages[i].isEmpty()) {
- overhead += computeStringAndStyleSize(stringPool, i);
- } else {
- ++count;
- }
+ public void compute() throws IOException {
+ Preconditions.checkState(stats.isEmpty(), "Must only call #compute once.");
+ blamer.blame();
+ computeStringPoolSizes();
+ computePackageSizes();
}
- // Now that we know the number of actual entries, we can compute the size.
- for (int i = 0; i < usages.length; ++i) {
- if (usages[i].isEmpty()) {
- continue;
- }
- int size = computeStringAndStyleSize(stringPool, i);
- addSizes(usages[i], overhead, size, count);
- }
- }
-
- private void computeTypeSpecSizes(PackageChunk packageChunk,
- List[] usages) {
- for (int i = 0; i < usages.length; ++i) {
- // The 1 here is to convert back to a 1-based index.
- TypeSpecChunk typeSpec = packageChunk.getTypeSpecChunk(i + 1);
- // TypeSpecChunk entries share everything equally.
- addSizes(usages[i], typeSpec.getOriginalChunkSize(), 0, 1);
- }
- }
-
- /**
- * Given an {@code index} into a {@code stringPool}, return string's total size in bytes plus its
- * style, if any.
- *
- * @param stringPool The string pool containing the {@code index}.
- * @param index The (0-based) index of the string and (optional) style.
- * @throws IOException Thrown if the style's length could not be computed.
- */
- private int computeStringAndStyleSize(StringPoolChunk stringPool, int index)
- throws IOException {
- return computeStringSize(stringPool, index) + computeStyleSize(stringPool, index);
- }
-
- /** Given an {@code index} into a {@code stringPool}, return string's total size in bytes. */
- private int computeStringSize(StringPoolChunk stringPool, int index) {
- String string = stringPool.getString(index);
- int result = BinaryResourceString.encodeString(string, stringPool.getStringType()).length;
- result += OFFSET_SIZE;
- return result;
- }
-
- /**
- * Given an {@code index} into a {@code stringPool}, return style's total size in bytes or 0 if
- * there's no style at that index.
- *
- * @throws IOException Thrown if the style's length could not be computed.
- */
- private int computeStyleSize(StringPoolChunk stringPool, int index) throws IOException {
- if (index >= stringPool.getStyleCount()) { // No style at index
- return 0;
- }
- return stringPool.getStyle(index).toByteArray().length + OFFSET_SIZE;
- }
-
- /**
- * Adds to the {@link #stats} of {@code entries} that reference a value in a chunk the bytes it's
- * responsible for. This should only be called once per chunk-value pair.
- *
- * @param entries The resource entries referencing a single value in a chunk.
- * @param overhead The number of bytes of overhead of a chunk. Typically the header size.
- * @param size The size in bytes of a value in a chunk that {@code entries} reference.
- * @param count The total number of values in the chunk.
- */
- private void addSizes(Collection entries, int overhead, int size, int count) {
- int usageCount = entries.size();
- for (ResourceEntry resourceEntry : entries) {
- // TODO(acornwall): Replace with Java 8's #getOrDefault when possible.
- if (!stats.containsKey(resourceEntry)) {
- stats.put(resourceEntry, new ResourceStatistics());
- }
- ResourceStatistics resourceStats = stats.get(resourceEntry);
- if (usageCount == 1) {
- resourceStats.addPrivateSize(size);
- } else {
- resourceStats.addSharedSize(size);
- }
- // Special case: If the chunk only has one relevant value, removing this entry will remove the
- // entire chunk.
- if (usageCount == 1 && count == 1) {
- resourceStats.addPrivateSize(overhead);
- }
- resourceStats.addProportionalSize(size, usageCount);
- resourceStats.addProportionalSize(overhead, usageCount * count);
- }
- }
-
- /** Stats for an individual {@link ResourceEntry}. */
- public static class ResourceStatistics {
-
- /** The empty, immutable instance of ResourceStatistics which contains 0 for all values. */
- public static final ResourceStatistics EMPTY = new ResourceStatistics();
-
- private int privateSize = 0;
- private int sharedSize = 0;
- private double proportionalSize = 0;
-
- private ResourceStatistics() {}
-
- /** The number of bytes that would be freed if this resource was removed. */
- public int getPrivateSize() {
- return privateSize;
+ /**
+ * Returns entries for which there are computed stats. Must first call {@link #compute}.
+ */
+ public Map getStats() {
+ Preconditions.checkState(!stats.isEmpty(), "Must call #compute() first.");
+ return Collections.unmodifiableMap(stats);
}
- /** The number of bytes taken up by this resource that are also shared with other resources. */
- public int getSharedSize() {
- return sharedSize;
+ /**
+ * Returns computed stats for a given entry. Must first call {@link #compute}.
+ */
+ public ResourceStatistics getStats(ResourceEntry entry) {
+ Preconditions.checkState(!stats.isEmpty(), "Must call #compute() first.");
+ return stats.containsKey(entry) ? stats.get(entry) : ResourceStatistics.EMPTY;
}
- /** The total size this resource is responsible for. */
- public double getProportionalSize() {
- return proportionalSize;
+ private void computeStringPoolSizes() throws IOException {
+ computePoolSizes(resourceTable.getStringPool(), blamer.getStringToBlamedResources());
}
- private void addPrivateSize(int privateSize) {
- this.privateSize += privateSize;
+ private void computePackageSizes() throws IOException {
+ computeTypePoolSizes();
+ computeKeyPoolSizes();
+ computeTypeSpecSizes();
+ computeTypeChunkSizes();
+ computePackageChunkSizes();
}
- private void addSharedSize(int sharedSize) {
- this.sharedSize += sharedSize;
+ private void computeTypePoolSizes() throws IOException {
+ for (Entry[]> entry
+ : blamer.getTypeToBlamedResources().entrySet()) {
+ computePoolSizes(entry.getKey().getTypeStringPool(), entry.getValue());
+ }
}
- private void addProportionalSize(int numerator, int denominator) {
- this.proportionalSize += 1.0 * numerator / denominator;
+ private void computeKeyPoolSizes() throws IOException {
+ for (Entry[]> entry
+ : blamer.getKeyToBlamedResources().entrySet()) {
+ computePoolSizes(entry.getKey().getKeyStringPool(), entry.getValue());
+ }
+ }
+
+ private void computeTypeSpecSizes() {
+ for (Entry[]> entry
+ : blamer.getTypeToBlamedResources().entrySet()) {
+ computeTypeSpecSizes(entry.getKey(), entry.getValue());
+ }
+ }
+
+ private void computeTypeChunkSizes() {
+ for (Entry> entry
+ : blamer.getTypeEntryToBlamedResources().asMap().entrySet()) {
+ TypeChunk.Entry chunkEntry = entry.getKey();
+ TypeChunk typeChunk = chunkEntry.parent();
+ int size = chunkEntry.size() + OFFSET_SIZE;
+ int count = typeChunk.getEntries().size();
+ int nullEntries = typeChunk.getTotalEntryCount() - typeChunk.getEntries().size();
+ int overhead = typeChunk.getHeaderSize() + nullEntries * OFFSET_SIZE;
+ addSizes(entry.getValue(), overhead, size, count);
+ }
+ }
+
+ private void computePackageChunkSizes() {
+ for (Entry> entry
+ : blamer.getPackageToBlamedResources().asMap().entrySet()) {
+ int overhead = entry.getKey().getHeaderSize() + PACKAGE_CHUNK_OVERHEAD;
+ addSizes(entry.getValue(), overhead, 0, 1);
+ }
+ }
+
+ private void computePoolSizes(StringPoolChunk stringPool,
+ List[] usages) throws IOException {
+ int overhead = stringPool.getHeaderSize();
+ if (stringPool.getStyleCount() > 0) {
+ overhead += STYLE_OVERHEAD;
+ }
+
+ // We have to iterate over the indices of the string pool, because it is possible that there are
+ // indices which have *no* associated resource entry (i.e. references from XML files without an
+ // entry in R).
+ int count = 0;
+ for (int i = 0; i < usages.length; ++i) {
+ if (usages[i].isEmpty()) {
+ overhead += computeStringAndStyleSize(stringPool, i);
+ } else {
+ ++count;
+ }
+ }
+
+ // Now that we know the number of actual entries, we can compute the size.
+ for (int i = 0; i < usages.length; ++i) {
+ if (usages[i].isEmpty()) {
+ continue;
+ }
+ int size = computeStringAndStyleSize(stringPool, i);
+ addSizes(usages[i], overhead, size, count);
+ }
+ }
+
+ private void computeTypeSpecSizes(PackageChunk packageChunk,
+ List[] usages) {
+ for (int i = 0; i < usages.length; ++i) {
+ // The 1 here is to convert back to a 1-based index.
+ TypeSpecChunk typeSpec = packageChunk.getTypeSpecChunk(i + 1);
+ // TypeSpecChunk entries share everything equally.
+ addSizes(usages[i], typeSpec.getOriginalChunkSize(), 0, 1);
+ }
+ }
+
+ /**
+ * Given an {@code index} into a {@code stringPool}, return string's total size in bytes plus its
+ * style, if any.
+ *
+ * @param stringPool The string pool containing the {@code index}.
+ * @param index The (0-based) index of the string and (optional) style.
+ * @throws IOException Thrown if the style's length could not be computed.
+ */
+ private int computeStringAndStyleSize(StringPoolChunk stringPool, int index)
+ throws IOException {
+ return computeStringSize(stringPool, index) + computeStyleSize(stringPool, index);
+ }
+
+ /**
+ * Given an {@code index} into a {@code stringPool}, return string's total size in bytes.
+ */
+ private int computeStringSize(StringPoolChunk stringPool, int index) {
+ String string = stringPool.getString(index);
+ int result = BinaryResourceString.encodeString(string, stringPool.getStringType()).length;
+ result += OFFSET_SIZE;
+ return result;
+ }
+
+ /**
+ * Given an {@code index} into a {@code stringPool}, return style's total size in bytes or 0 if
+ * there's no style at that index.
+ *
+ * @throws IOException Thrown if the style's length could not be computed.
+ */
+ private int computeStyleSize(StringPoolChunk stringPool, int index) throws IOException {
+ if (index >= stringPool.getStyleCount()) { // No style at index
+ return 0;
+ }
+ return stringPool.getStyle(index).toByteArray().length + OFFSET_SIZE;
+ }
+
+ /**
+ * Adds to the {@link #stats} of {@code entries} that reference a value in a chunk the bytes it's
+ * responsible for. This should only be called once per chunk-value pair.
+ *
+ * @param entries The resource entries referencing a single value in a chunk.
+ * @param overhead The number of bytes of overhead of a chunk. Typically the header size.
+ * @param size The size in bytes of a value in a chunk that {@code entries} reference.
+ * @param count The total number of values in the chunk.
+ */
+ private void addSizes(Collection entries, int overhead, int size, int count) {
+ int usageCount = entries.size();
+ for (ResourceEntry resourceEntry : entries) {
+ // TODO(acornwall): Replace with Java 8's #getOrDefault when possible.
+ if (!stats.containsKey(resourceEntry)) {
+ stats.put(resourceEntry, new ResourceStatistics());
+ }
+ ResourceStatistics resourceStats = stats.get(resourceEntry);
+ if (usageCount == 1) {
+ resourceStats.addPrivateSize(size);
+ } else {
+ resourceStats.addSharedSize(size);
+ }
+ // Special case: If the chunk only has one relevant value, removing this entry will remove the
+ // entire chunk.
+ if (usageCount == 1 && count == 1) {
+ resourceStats.addPrivateSize(overhead);
+ }
+ resourceStats.addProportionalSize(size, usageCount);
+ resourceStats.addProportionalSize(overhead, usageCount * count);
+ }
+ }
+
+ /**
+ * Stats for an individual {@link ResourceEntry}.
+ */
+ public static class ResourceStatistics {
+
+ /**
+ * The empty, immutable instance of ResourceStatistics which contains 0 for all values.
+ */
+ public static final ResourceStatistics EMPTY = new ResourceStatistics();
+
+ private int privateSize = 0;
+ private int sharedSize = 0;
+ private double proportionalSize = 0;
+
+ private ResourceStatistics() {
+ }
+
+ /**
+ * The number of bytes that would be freed if this resource was removed.
+ */
+ public int getPrivateSize() {
+ return privateSize;
+ }
+
+ /**
+ * The number of bytes taken up by this resource that are also shared with other resources.
+ */
+ public int getSharedSize() {
+ return sharedSize;
+ }
+
+ /**
+ * The total size this resource is responsible for.
+ */
+ public double getProportionalSize() {
+ return proportionalSize;
+ }
+
+ private void addPrivateSize(int privateSize) {
+ this.privateSize += privateSize;
+ }
+
+ private void addSharedSize(int sharedSize) {
+ this.sharedSize += sharedSize;
+ }
+
+ private void addProportionalSize(int numerator, int denominator) {
+ this.proportionalSize += 1.0 * numerator / denominator;
+ }
}
- }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/ResourceTableChunk.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/ResourceTableChunk.java
index 8752b9054..b6dffd660 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/ResourceTableChunk.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/ResourceTableChunk.java
@@ -16,9 +16,9 @@
package com.google.devrel.gmscore.tools.apk.arsc;
+import androidx.annotation.Nullable;
import com.google.common.base.Preconditions;
-import androidx.annotation.Nullable;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Collections;
@@ -36,57 +36,67 @@ import java.util.Map;
*/
public final class ResourceTableChunk extends ChunkWithChunks {
- /** A string pool containing all string resource values in the entire resource table. */
- private StringPoolChunk stringPool;
+ /**
+ * A string pool containing all string resource values in the entire resource table.
+ */
+ private StringPoolChunk stringPool;
- /** The packages contained in this resource table. */
- private final Map packages = new HashMap<>();
+ /**
+ * The packages contained in this resource table.
+ */
+ private final Map packages = new HashMap<>();
- protected ResourceTableChunk(ByteBuffer buffer, @Nullable Chunk parent) {
- super(buffer, parent);
- // packageCount. We ignore this, because we already know how many chunks we have.
- Preconditions.checkState(buffer.getInt() >= 1, "ResourceTableChunk package count was < 1.");
- }
-
- @Override
- protected void init(ByteBuffer buffer) {
- super.init(buffer);
- packages.clear();
- for (Chunk chunk : getChunks().values()) {
- if (chunk instanceof PackageChunk) {
- PackageChunk packageChunk = (PackageChunk) chunk;
- packages.put(packageChunk.getPackageName(), packageChunk);
- } else if (chunk instanceof StringPoolChunk) {
- stringPool = (StringPoolChunk) chunk;
- }
+ protected ResourceTableChunk(ByteBuffer buffer, @Nullable Chunk parent) {
+ super(buffer, parent);
+ // packageCount. We ignore this, because we already know how many chunks we have.
+ Preconditions.checkState(buffer.getInt() >= 1, "ResourceTableChunk package count was < 1.");
}
- Preconditions.checkNotNull(stringPool, "ResourceTableChunk must have a string pool.");
- }
- /** Returns the string pool containing all string resource values in the resource table. */
- public StringPoolChunk getStringPool() {
- return stringPool;
- }
+ @Override
+ protected void init(ByteBuffer buffer) {
+ super.init(buffer);
+ packages.clear();
+ for (Chunk chunk : getChunks().values()) {
+ if (chunk instanceof PackageChunk) {
+ PackageChunk packageChunk = (PackageChunk) chunk;
+ packages.put(packageChunk.getPackageName(), packageChunk);
+ } else if (chunk instanceof StringPoolChunk) {
+ stringPool = (StringPoolChunk) chunk;
+ }
+ }
+ Preconditions.checkNotNull(stringPool, "ResourceTableChunk must have a string pool.");
+ }
- /** Returns the package with the given {@code packageName}. Else, returns null. */
- @Nullable
- public PackageChunk getPackage(String packageName) {
- return packages.get(packageName);
- }
+ /**
+ * Returns the string pool containing all string resource values in the resource table.
+ */
+ public StringPoolChunk getStringPool() {
+ return stringPool;
+ }
- /** Returns the packages contained in this resource table. */
- public Collection getPackages() {
- return Collections.unmodifiableCollection(packages.values());
- }
+ /**
+ * Returns the package with the given {@code packageName}. Else, returns null.
+ */
+ @Nullable
+ public PackageChunk getPackage(String packageName) {
+ return packages.get(packageName);
+ }
- @Override
- protected Type getType() {
- return Type.TABLE;
- }
+ /**
+ * Returns the packages contained in this resource table.
+ */
+ public Collection getPackages() {
+ return Collections.unmodifiableCollection(packages.values());
+ }
- @Override
- protected void writeHeader(ByteBuffer output) {
- super.writeHeader(output);
- output.putInt(packages.size());
- }
+ @Override
+ protected Type getType() {
+ return Type.TABLE;
+ }
+
+ @Override
+ protected void writeHeader(ByteBuffer output) {
+ super.writeHeader(output);
+ output.putInt(packages.size());
+ }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/SerializableResource.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/SerializableResource.java
index b9c62a1d5..d099defe4 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/SerializableResource.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/SerializableResource.java
@@ -22,20 +22,22 @@ import java.io.IOException;
* A resource, typically a @{link Chunk}, that can be converted to an array of bytes.
*/
public interface SerializableResource {
-
- /**
- * Converts this resource into an array of bytes representation.
- * @return An array of bytes representing this resource.
- * @throws IOException
- */
- byte[] toByteArray() throws IOException;
- /**
- * Converts this resource into an array of bytes representation.
- * @param shrink True if, when converting to a byte array, this resource can modify the returned
- * bytes in an effort to reduce the size.
- * @return An array of bytes representing this resource.
- * @throws IOException
- */
- byte[] toByteArray(boolean shrink) throws IOException;
+ /**
+ * Converts this resource into an array of bytes representation.
+ *
+ * @return An array of bytes representing this resource.
+ * @throws IOException
+ */
+ byte[] toByteArray() throws IOException;
+
+ /**
+ * Converts this resource into an array of bytes representation.
+ *
+ * @param shrink True if, when converting to a byte array, this resource can modify the returned
+ * bytes in an effort to reduce the size.
+ * @return An array of bytes representing this resource.
+ * @throws IOException
+ */
+ byte[] toByteArray(boolean shrink) throws IOException;
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/StringPoolChunk.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/StringPoolChunk.java
index c0c73efaa..28b7f5907 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/StringPoolChunk.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/StringPoolChunk.java
@@ -16,11 +16,11 @@
package com.google.devrel.gmscore.tools.apk.arsc;
+import androidx.annotation.Nullable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.io.LittleEndianDataOutputStream;
-import androidx.annotation.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
import java.io.IOException;
@@ -28,396 +28,416 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.*;
-/** Represents a string pool structure. */
+/**
+ * Represents a string pool structure.
+ */
public final class StringPoolChunk extends Chunk {
- // These are the defined flags for the "flags" field of ResourceStringPoolHeader
- private static final int SORTED_FLAG = 1 << 0;
- private static final int UTF8_FLAG = 1 << 8;
+ // These are the defined flags for the "flags" field of ResourceStringPoolHeader
+ private static final int SORTED_FLAG = 1 << 0;
+ private static final int UTF8_FLAG = 1 << 8;
- /** The offset from the start of the header that the stylesStart field is at. */
- private static final int STYLE_START_OFFSET = 24;
+ /**
+ * The offset from the start of the header that the stylesStart field is at.
+ */
+ private static final int STYLE_START_OFFSET = 24;
- /** Flags. */
- private final int flags;
+ /**
+ * Flags.
+ */
+ private final int flags;
- /** Index from header of the string data. */
- private final int stringsStart;
+ /**
+ * Index from header of the string data.
+ */
+ private final int stringsStart;
- /** Index from header of the style data. */
- private final int stylesStart;
+ /**
+ * Index from header of the style data.
+ */
+ private final int stylesStart;
- /**
- * Number of strings in the original buffer. This is not necessarily the number of strings in
- * {@code strings}.
- */
- private final int stringCount;
+ /**
+ * Number of strings in the original buffer. This is not necessarily the number of strings in
+ * {@code strings}.
+ */
+ private final int stringCount;
- /**
- * Number of styles in the original buffer. This is not necessarily the number of styles in
- * {@code styles}.
- */
- private final int styleCount;
+ /**
+ * Number of styles in the original buffer. This is not necessarily the number of styles in
+ * {@code styles}.
+ */
+ private final int styleCount;
- /**
- * The strings ordered as they appear in the arsc file. e.g. strings.get(1234) gets the 1235th
- * string in the arsc file.
- */
- private final List strings = new ArrayList<>();
+ /**
+ * The strings ordered as they appear in the arsc file. e.g. strings.get(1234) gets the 1235th
+ * string in the arsc file.
+ */
+ private final List strings = new ArrayList<>();
- /**
- * These styles have a 1:1 relationship with the strings. For example, styles.get(3) refers to
- * the string at location strings.get(3). There are never more styles than strings (though there
- * may be less). Inside of that are all of the styles referenced by that string.
- */
- private final List styles = new ArrayList<>();
+ /**
+ * These styles have a 1:1 relationship with the strings. For example, styles.get(3) refers to
+ * the string at location strings.get(3). There are never more styles than strings (though there
+ * may be less). Inside of that are all of the styles referenced by that string.
+ */
+ private final List styles = new ArrayList<>();
- /**
- * True if the original {@link StringPoolChunk} shows signs of being deduped. Specifically, this
- * is set to true if there exists a string whose offset is <= the previous offset. This is used to
- * preserve the deduping of strings for pools that have been deduped.
- */
- private boolean isOriginalDeduped = false;
+ /**
+ * True if the original {@link StringPoolChunk} shows signs of being deduped. Specifically, this
+ * is set to true if there exists a string whose offset is <= the previous offset. This is used to
+ * preserve the deduping of strings for pools that have been deduped.
+ */
+ private boolean isOriginalDeduped = false;
- protected StringPoolChunk(ByteBuffer buffer, @Nullable Chunk parent) {
- super(buffer, parent);
- stringCount = buffer.getInt();
- styleCount = buffer.getInt();
- flags = buffer.getInt();
- stringsStart = buffer.getInt();
- stylesStart = buffer.getInt();
- }
-
- @Override
- protected void init(ByteBuffer buffer) {
- super.init(buffer);
- strings.addAll(readStrings(buffer, offset + stringsStart, stringCount));
- styles.addAll(readStyles(buffer, offset + stylesStart, styleCount));
- }
-
- /**
- * Returns the 0-based index of the first occurrence of the given string, or -1 if the string is
- * not in the pool. This runs in O(n) time.
- *
- * @param string The string to check the pool for.
- * @return Index of the string, or -1 if not found.
- */
- public int indexOf(String string) {
- return strings.indexOf(string);
- }
-
- /**
- * Returns a string at the given (0-based) index.
- *
- * @param index The (0-based) index of the string to return.
- * @throws IndexOutOfBoundsException If the index is out of range (index < 0 || index >= size()).
- */
- public String getString(int index) {
- return strings.get(index);
- }
-
- public void updateString(int index, String value){
- strings.set(index, value);
- }
-
- /** Returns the number of strings in this pool. */
- public int getStringCount() {
- return strings.size();
- }
-
- /**
- * Returns a style at the given (0-based) index.
- *
- * @param index The (0-based) index of the style to return.
- * @throws IndexOutOfBoundsException If the index is out of range (index < 0 || index >= size()).
- */
- public StringPoolStyle getStyle(int index) {
- return styles.get(index);
- }
-
- /** Returns the number of styles in this pool. */
- public int getStyleCount() {
- return styles.size();
- }
-
- /** Returns the type of strings in this pool. */
- public BinaryResourceString.Type getStringType() {
- return isUTF8() ? BinaryResourceString.Type.UTF8 : BinaryResourceString.Type.UTF16;
- }
-
- @Override
- protected Type getType() {
- return Type.STRING_POOL;
- }
-
- /** Returns the number of bytes needed for offsets based on {@code strings} and {@code styles}. */
- private int getOffsetSize() {
- return (strings.size() + styles.size()) * 4;
- }
-
- /**
- * True if this string pool contains strings in UTF-8 format. Otherwise, strings are in UTF-16.
- *
- * @return true if @{code strings} are in UTF-8; false if they're in UTF-16.
- */
- public boolean isUTF8() {
- return (flags & UTF8_FLAG) != 0;
- }
-
- /**
- * True if this string pool contains already-sorted strings.
- *
- * @return true if @{code strings} are sorted.
- */
- public boolean isSorted() {
- return (flags & SORTED_FLAG) != 0;
- }
-
- private List readStrings(ByteBuffer buffer, int offset, int count) {
- List result = new ArrayList<>();
- int previousOffset = -1;
- // After the header, we now have an array of offsets for the strings in this pool.
- for (int i = 0; i < count; ++i) {
- int stringOffset = offset + buffer.getInt();
- result.add(BinaryResourceString.decodeString(buffer, stringOffset, getStringType()));
- if (stringOffset <= previousOffset) {
- isOriginalDeduped = true;
- }
- previousOffset = stringOffset;
- }
- return result;
- }
-
- private List readStyles(ByteBuffer buffer, int offset, int count) {
- List result = new ArrayList<>();
- // After the array of offsets for the strings in the pool, we have an offset for the styles
- // in this pool.
- for (int i = 0; i < count; ++i) {
- int styleOffset = offset + buffer.getInt();
- result.add(StringPoolStyle.create(buffer, styleOffset, this));
- }
- return result;
- }
-
- private int writeStrings(DataOutput payload, ByteBuffer offsets, boolean shrink)
- throws IOException {
- int stringOffset = 0;
- Map used = new HashMap<>(); // Keeps track of strings already written
- for (String string : strings) {
- // Dedupe everything except stylized strings, unless shrink is true (then dedupe everything)
- if (used.containsKey(string) && (shrink || isOriginalDeduped)) {
- Integer offset = used.get(string);
- offsets.putInt(offset == null ? 0 : offset);
- } else {
- byte[] encodedString = BinaryResourceString.encodeString(string, getStringType());
- payload.write(encodedString);
- used.put(string, stringOffset);
- offsets.putInt(stringOffset);
- stringOffset += encodedString.length;
- }
+ protected StringPoolChunk(ByteBuffer buffer, @Nullable Chunk parent) {
+ super(buffer, parent);
+ stringCount = buffer.getInt();
+ styleCount = buffer.getInt();
+ flags = buffer.getInt();
+ stringsStart = buffer.getInt();
+ stylesStart = buffer.getInt();
}
- // ARSC files pad to a 4-byte boundary. We should do so too.
- stringOffset = writePad(payload, stringOffset);
- return stringOffset;
- }
+ @Override
+ protected void init(ByteBuffer buffer) {
+ super.init(buffer);
+ strings.addAll(readStrings(buffer, offset + stringsStart, stringCount));
+ styles.addAll(readStyles(buffer, offset + stylesStart, styleCount));
+ }
- private int writeStyles(DataOutput payload, ByteBuffer offsets, boolean shrink)
- throws IOException {
- int styleOffset = 0;
+ /**
+ * Returns the 0-based index of the first occurrence of the given string, or -1 if the string is
+ * not in the pool. This runs in O(n) time.
+ *
+ * @param string The string to check the pool for.
+ * @return Index of the string, or -1 if not found.
+ */
+ public int indexOf(String string) {
+ return strings.indexOf(string);
+ }
+
+ /**
+ * Returns a string at the given (0-based) index.
+ *
+ * @param index The (0-based) index of the string to return.
+ * @throws IndexOutOfBoundsException If the index is out of range (index < 0 || index >= size()).
+ */
+ public String getString(int index) {
+ return strings.get(index);
+ }
+
+ public void updateString(int index, String value) {
+ strings.set(index, value);
+ }
+
+ /**
+ * Returns the number of strings in this pool.
+ */
+ public int getStringCount() {
+ return strings.size();
+ }
+
+ /**
+ * Returns a style at the given (0-based) index.
+ *
+ * @param index The (0-based) index of the style to return.
+ * @throws IndexOutOfBoundsException If the index is out of range (index < 0 || index >= size()).
+ */
+ public StringPoolStyle getStyle(int index) {
+ return styles.get(index);
+ }
+
+ /**
+ * Returns the number of styles in this pool.
+ */
+ public int getStyleCount() {
+ return styles.size();
+ }
+
+ /**
+ * Returns the type of strings in this pool.
+ */
+ public BinaryResourceString.Type getStringType() {
+ return isUTF8() ? BinaryResourceString.Type.UTF8 : BinaryResourceString.Type.UTF16;
+ }
+
+ @Override
+ protected Type getType() {
+ return Type.STRING_POOL;
+ }
+
+ /**
+ * Returns the number of bytes needed for offsets based on {@code strings} and {@code styles}.
+ */
+ private int getOffsetSize() {
+ return (strings.size() + styles.size()) * 4;
+ }
+
+ /**
+ * True if this string pool contains strings in UTF-8 format. Otherwise, strings are in UTF-16.
+ *
+ * @return true if @{code strings} are in UTF-8; false if they're in UTF-16.
+ */
+ public boolean isUTF8() {
+ return (flags & UTF8_FLAG) != 0;
+ }
+
+ /**
+ * True if this string pool contains already-sorted strings.
+ *
+ * @return true if @{code strings} are sorted.
+ */
+ public boolean isSorted() {
+ return (flags & SORTED_FLAG) != 0;
+ }
+
+ private List readStrings(ByteBuffer buffer, int offset, int count) {
+ List result = new ArrayList<>();
+ int previousOffset = -1;
+ // After the header, we now have an array of offsets for the strings in this pool.
+ for (int i = 0; i < count; ++i) {
+ int stringOffset = offset + buffer.getInt();
+ result.add(BinaryResourceString.decodeString(buffer, stringOffset, getStringType()));
+ if (stringOffset <= previousOffset) {
+ isOriginalDeduped = true;
+ }
+ previousOffset = stringOffset;
+ }
+ return result;
+ }
+
+ private List readStyles(ByteBuffer buffer, int offset, int count) {
+ List result = new ArrayList<>();
+ // After the array of offsets for the strings in the pool, we have an offset for the styles
+ // in this pool.
+ for (int i = 0; i < count; ++i) {
+ int styleOffset = offset + buffer.getInt();
+ result.add(StringPoolStyle.create(buffer, styleOffset, this));
+ }
+ return result;
+ }
+
+ private int writeStrings(DataOutput payload, ByteBuffer offsets, boolean shrink)
+ throws IOException {
+ int stringOffset = 0;
+ Map used = new HashMap<>(); // Keeps track of strings already written
+ for (String string : strings) {
+ // Dedupe everything except stylized strings, unless shrink is true (then dedupe everything)
+ if (used.containsKey(string) && (shrink || isOriginalDeduped)) {
+ Integer offset = used.get(string);
+ offsets.putInt(offset == null ? 0 : offset);
+ } else {
+ byte[] encodedString = BinaryResourceString.encodeString(string, getStringType());
+ payload.write(encodedString);
+ used.put(string, stringOffset);
+ offsets.putInt(stringOffset);
+ stringOffset += encodedString.length;
+ }
+ }
+
+ // ARSC files pad to a 4-byte boundary. We should do so too.
+ stringOffset = writePad(payload, stringOffset);
+ return stringOffset;
+ }
+
+ private int writeStyles(DataOutput payload, ByteBuffer offsets, boolean shrink)
+ throws IOException {
+ int styleOffset = 0;
if (!styles.isEmpty()) {
- Map used = new HashMap<>(); // Keeps track of bytes already written
- for (StringPoolStyle style : styles) {
- if (!used.containsKey(style) || !shrink) {
- byte[] encodedStyle = style.toByteArray(shrink);
- payload.write(encodedStyle);
- used.put(style, styleOffset);
- offsets.putInt(styleOffset);
- styleOffset += encodedStyle.length;
- } else { // contains key and shrink is true
- Integer offset = used.get(style);
- offsets.putInt(offset == null ? 0 : offset);
+ Map used = new HashMap<>(); // Keeps track of bytes already written
+ for (StringPoolStyle style : styles) {
+ if (!used.containsKey(style) || !shrink) {
+ byte[] encodedStyle = style.toByteArray(shrink);
+ payload.write(encodedStyle);
+ used.put(style, styleOffset);
+ offsets.putInt(styleOffset);
+ styleOffset += encodedStyle.length;
+ } else { // contains key and shrink is true
+ Integer offset = used.get(style);
+ offsets.putInt(offset == null ? 0 : offset);
+ }
+ }
+ // The end of the spans are terminated with another sentinel value
+ payload.writeInt(StringPoolStyle.RES_STRING_POOL_SPAN_END);
+ styleOffset += 4;
+ // TODO(acornwall): There appears to be an extra SPAN_END here... why?
+ payload.writeInt(StringPoolStyle.RES_STRING_POOL_SPAN_END);
+ styleOffset += 4;
+
+ styleOffset = writePad(payload, styleOffset);
}
- }
- // The end of the spans are terminated with another sentinel value
- payload.writeInt(StringPoolStyle.RES_STRING_POOL_SPAN_END);
- styleOffset += 4;
- // TODO(acornwall): There appears to be an extra SPAN_END here... why?
- payload.writeInt(StringPoolStyle.RES_STRING_POOL_SPAN_END);
- styleOffset += 4;
-
- styleOffset = writePad(payload, styleOffset);
- }
- return styleOffset;
- }
-
- @Override
- protected void writeHeader(ByteBuffer output) {
- int stringsStart = getHeaderSize() + getOffsetSize();
- output.putInt(strings.size());
- output.putInt(styles.size());
- output.putInt(flags);
- output.putInt(strings.isEmpty() ? 0 : stringsStart);
- output.putInt(0); // Placeholder. The styles starting offset cannot be computed at this point.
- }
-
- @Override
- protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
- throws IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- int stringOffset = 0;
- ByteBuffer offsets = ByteBuffer.allocate(getOffsetSize());
- offsets.order(ByteOrder.LITTLE_ENDIAN);
-
- // Write to a temporary payload so we can rearrange this and put the offsets first
- try (LittleEndianDataOutputStream payload = new LittleEndianDataOutputStream(baos)) {
- stringOffset = writeStrings(payload, offsets, shrink);
- writeStyles(payload, offsets, shrink);
- }
-
- output.write(offsets.array());
- output.write(baos.toByteArray());
- if (!styles.isEmpty()) {
- header.putInt(STYLE_START_OFFSET, getHeaderSize() + getOffsetSize() + stringOffset);
- }
- }
-
- /**
- * Represents all of the styles for a particular string. The string is determined by its index
- * in {@link StringPoolChunk}.
- */
- public static class StringPoolStyle implements SerializableResource {
-
- // Styles are a list of integers with 0xFFFFFFFF serving as a sentinel value.
- static final int RES_STRING_POOL_SPAN_END = 0xFFFFFFFF;
- private final List spans;
-
- static StringPoolStyle create(ByteBuffer buffer, int offset, StringPoolChunk parent) {
- Builder spans = ImmutableList.builder();
- int nameIndex = buffer.getInt(offset);
- while (nameIndex != RES_STRING_POOL_SPAN_END) {
- spans.add(StringPoolSpan.create(buffer, offset, parent));
- offset += StringPoolSpan.SPAN_LENGTH;
- nameIndex = buffer.getInt(offset);
- }
- return new StringPoolStyle(spans.build());
- }
-
- private StringPoolStyle(List spans) {
- this.spans = spans;
+ return styleOffset;
}
@Override
- public byte[] toByteArray() throws IOException {
- return toByteArray(false);
+ protected void writeHeader(ByteBuffer output) {
+ int stringsStart = getHeaderSize() + getOffsetSize();
+ output.putInt(strings.size());
+ output.putInt(styles.size());
+ output.putInt(flags);
+ output.putInt(strings.isEmpty() ? 0 : stringsStart);
+ output.putInt(0); // Placeholder. The styles starting offset cannot be computed at this point.
}
@Override
- public byte[] toByteArray(boolean shrink) throws IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
+ throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int stringOffset = 0;
+ ByteBuffer offsets = ByteBuffer.allocate(getOffsetSize());
+ offsets.order(ByteOrder.LITTLE_ENDIAN);
- try (LittleEndianDataOutputStream payload = new LittleEndianDataOutputStream(baos)) {
- for (StringPoolSpan span : spans) {
- byte[] encodedSpan = span.toByteArray(shrink);
- if (encodedSpan.length != StringPoolSpan.SPAN_LENGTH) {
- throw new IllegalStateException("Encountered a span of invalid length.");
- }
- payload.write(encodedSpan);
+ // Write to a temporary payload so we can rearrange this and put the offsets first
+ try (LittleEndianDataOutputStream payload = new LittleEndianDataOutputStream(baos)) {
+ stringOffset = writeStrings(payload, offsets, shrink);
+ writeStyles(payload, offsets, shrink);
}
- payload.writeInt(RES_STRING_POOL_SPAN_END);
- }
- return baos.toByteArray();
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- StringPoolStyle that = (StringPoolStyle)o;
- return Objects.equals(spans, that.spans);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(spans);
+ output.write(offsets.array());
+ output.write(baos.toByteArray());
+ if (!styles.isEmpty()) {
+ header.putInt(STYLE_START_OFFSET, getHeaderSize() + getOffsetSize() + stringOffset);
+ }
}
/**
- * Returns a brief description of the contents of this style. The representation of this
- * information is subject to change, but below is a typical example:
- *
- * "StringPoolStyle{spans=[StringPoolSpan{foo, start=0, stop=5}, ...]}"
+ * Represents all of the styles for a particular string. The string is determined by its index
+ * in {@link StringPoolChunk}.
*/
- @Override
- public String toString() {
- return String.format("StringPoolStyle{spans=%s}", spans);
- }
- }
+ public static class StringPoolStyle implements SerializableResource {
- /** Represents a styled span associated with a specific string. */
- private static class StringPoolSpan implements SerializableResource {
- static final int SPAN_LENGTH = 12;
+ // Styles are a list of integers with 0xFFFFFFFF serving as a sentinel value.
+ static final int RES_STRING_POOL_SPAN_END = 0xFFFFFFFF;
+ private final List spans;
- private final int nameIndex;
- private final int start;
- private final int stop;
- private final StringPoolChunk parent;
+ static StringPoolStyle create(ByteBuffer buffer, int offset, StringPoolChunk parent) {
+ Builder spans = ImmutableList.builder();
+ int nameIndex = buffer.getInt(offset);
+ while (nameIndex != RES_STRING_POOL_SPAN_END) {
+ spans.add(StringPoolSpan.create(buffer, offset, parent));
+ offset += StringPoolSpan.SPAN_LENGTH;
+ nameIndex = buffer.getInt(offset);
+ }
+ return new StringPoolStyle(spans.build());
+ }
- static StringPoolSpan create(ByteBuffer buffer, int offset, StringPoolChunk parent) {
- int nameIndex = buffer.getInt(offset);
- int start = buffer.getInt(offset + 4);
- int stop = buffer.getInt(offset + 8);
- return new StringPoolSpan(nameIndex, start, stop, parent);
- }
+ private StringPoolStyle(List spans) {
+ this.spans = spans;
+ }
- private StringPoolSpan(int nameIndex, int start, int stop, StringPoolChunk parent) {
- this.nameIndex = nameIndex;
- this.start = start;
- this.stop = stop;
- this.parent = parent;
- }
+ @Override
+ public byte[] toByteArray() throws IOException {
+ return toByteArray(false);
+ }
- @Override
- public final byte[] toByteArray() {
- return toByteArray(false);
- }
+ @Override
+ public byte[] toByteArray(boolean shrink) throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
- @Override
- public final byte[] toByteArray(boolean shrink) {
- ByteBuffer buffer = ByteBuffer.allocate(SPAN_LENGTH).order(ByteOrder.LITTLE_ENDIAN);
- buffer.putInt(nameIndex);
- buffer.putInt(start);
- buffer.putInt(stop);
- return buffer.array();
- }
+ try (LittleEndianDataOutputStream payload = new LittleEndianDataOutputStream(baos)) {
+ for (StringPoolSpan span : spans) {
+ byte[] encodedSpan = span.toByteArray(shrink);
+ if (encodedSpan.length != StringPoolSpan.SPAN_LENGTH) {
+ throw new IllegalStateException("Encountered a span of invalid length.");
+ }
+ payload.write(encodedSpan);
+ }
+ payload.writeInt(RES_STRING_POOL_SPAN_END);
+ }
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- StringPoolSpan that = (StringPoolSpan)o;
- return nameIndex == that.nameIndex &&
- start == that.start &&
- stop == that.stop &&
- Objects.equals(parent, that.parent);
- }
+ return baos.toByteArray();
+ }
- @Override
- public int hashCode() {
- return Objects.hash(nameIndex, start, stop, parent);
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ StringPoolStyle that = (StringPoolStyle) o;
+ return Objects.equals(spans, that.spans);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(spans);
+ }
+
+ /**
+ * Returns a brief description of the contents of this style. The representation of this
+ * information is subject to change, but below is a typical example:
+ *
+ * "StringPoolStyle{spans=[StringPoolSpan{foo, start=0, stop=5}, ...]}"
+ */
+ @Override
+ public String toString() {
+ return String.format("StringPoolStyle{spans=%s}", spans);
+ }
}
/**
- * Returns a brief description of this span. The representation of this information is subject
- * to change, but below is a typical example:
- *
- * "StringPoolSpan{foo, start=0, stop=5}"
+ * Represents a styled span associated with a specific string.
*/
- @Override
- public String toString() {
- return String.format("StringPoolSpan{%s, start=%d, stop=%d}",
- parent.getString(nameIndex), start, stop);
+ private static class StringPoolSpan implements SerializableResource {
+ static final int SPAN_LENGTH = 12;
+
+ private final int nameIndex;
+ private final int start;
+ private final int stop;
+ private final StringPoolChunk parent;
+
+ static StringPoolSpan create(ByteBuffer buffer, int offset, StringPoolChunk parent) {
+ int nameIndex = buffer.getInt(offset);
+ int start = buffer.getInt(offset + 4);
+ int stop = buffer.getInt(offset + 8);
+ return new StringPoolSpan(nameIndex, start, stop, parent);
+ }
+
+ private StringPoolSpan(int nameIndex, int start, int stop, StringPoolChunk parent) {
+ this.nameIndex = nameIndex;
+ this.start = start;
+ this.stop = stop;
+ this.parent = parent;
+ }
+
+ @Override
+ public final byte[] toByteArray() {
+ return toByteArray(false);
+ }
+
+ @Override
+ public final byte[] toByteArray(boolean shrink) {
+ ByteBuffer buffer = ByteBuffer.allocate(SPAN_LENGTH).order(ByteOrder.LITTLE_ENDIAN);
+ buffer.putInt(nameIndex);
+ buffer.putInt(start);
+ buffer.putInt(stop);
+ return buffer.array();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ StringPoolSpan that = (StringPoolSpan) o;
+ return nameIndex == that.nameIndex &&
+ start == that.start &&
+ stop == that.stop &&
+ Objects.equals(parent, that.parent);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(nameIndex, start, stop, parent);
+ }
+
+ /**
+ * Returns a brief description of this span. The representation of this information is subject
+ * to change, but below is a typical example:
+ *
+ * "StringPoolSpan{foo, start=0, stop=5}"
+ */
+ @Override
+ public String toString() {
+ return String.format("StringPoolSpan{%s, start=%d, stop=%d}",
+ parent.getString(nameIndex), start, stop);
+ }
}
- }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/TypeChunk.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/TypeChunk.java
index 9a98ed54c..1030df262 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/TypeChunk.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/TypeChunk.java
@@ -16,11 +16,11 @@
package com.google.devrel.gmscore.tools.apk.arsc;
+import androidx.annotation.Nullable;
import com.google.common.base.Preconditions;
import com.google.common.io.LittleEndianDataOutputStream;
import com.google.common.primitives.UnsignedBytes;
-import androidx.annotation.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
import java.io.IOException;
@@ -38,412 +38,485 @@ import java.util.*;
*/
public final class TypeChunk extends Chunk {
- /** The type identifier of the resource type this chunk is holding. */
- private final int id;
-
- /** The number of resources of this type at creation time. */
- private final int entryCount;
-
- /** The offset (from {@code offset}) in the original buffer where {@code entries} start. */
- private final int entriesStart;
-
- /** The resource configuration that these resource entries correspond to. */
- private BinaryResourceConfiguration configuration;
-
- /** A sparse list of resource entries defined by this chunk. */
- private final Map entries = new TreeMap<>();
-
- protected TypeChunk(ByteBuffer buffer, @Nullable Chunk parent) {
- super(buffer, parent);
- id = UnsignedBytes.toInt(buffer.get());
- buffer.position(buffer.position() + 3); // Skip 3 bytes for packing
- entryCount = buffer.getInt();
- entriesStart = buffer.getInt();
- configuration = BinaryResourceConfiguration.create(buffer);
- }
-
- @Override
- protected void init(ByteBuffer buffer) {
- int offset = this.offset + entriesStart;
- for (int i = 0; i < entryCount; ++i) {
- Entry entry = Entry.create(buffer, offset, this);
- if (entry != null) {
- entries.put(i, entry);
- }
- }
- }
-
- /** Returns the (1-based) type id of the resource types that this {@link TypeChunk} is holding. */
- public int getId() {
- return id;
- }
-
- /** Returns the name of the type this chunk represents (e.g. string, attr, id). */
- public String getTypeName() {
- PackageChunk packageChunk = getPackageChunk();
- Preconditions.checkNotNull(packageChunk, "%s has no parent package.", getClass());
- StringPoolChunk typePool = packageChunk.getTypeStringPool();
- Preconditions.checkNotNull(typePool, "%s's parent package has no type pool.", getClass());
- return typePool.getString(getId() - 1); // - 1 here to convert to 0-based index
- }
-
- /** Returns the resource configuration that these resource entries correspond to. */
- public BinaryResourceConfiguration getConfiguration() {
- return configuration;
- }
-
- /**
- * Sets the resource configuration that this chunk's entries correspond to.
- *
- * @param configuration The new configuration.
- */
- public void setConfiguration(BinaryResourceConfiguration configuration) {
- this.configuration = configuration;
- }
-
- /** Returns the total number of entries for this type + configuration, including null entries. */
- public int getTotalEntryCount() {
- return entryCount;
- }
-
- /** Returns a sparse list of 0-based indices to resource entries defined by this chunk. */
- public Map getEntries() {
- return Collections.unmodifiableMap(entries);
- }
-
- /** Returns true if this chunk contains an entry for {@code resourceId}. */
- public boolean containsResource(BinaryResourceIdentifier resourceId) {
- PackageChunk packageChunk = Preconditions.checkNotNull(getPackageChunk());
- int packageId = packageChunk.getId();
- int typeId = getId();
- return resourceId.packageId() == packageId
- && resourceId.typeId() == typeId
- && entries.containsKey(resourceId.entryId());
- }
-
- /**
- * Overrides the entries in this chunk at the given index:entry pairs in {@code entries}.
- * For example, if the current list of entries is {0: foo, 1: bar, 2: baz}, and {@code entries}
- * is {1: qux, 3: quux}, then the entries will be changed to {0: foo, 1: qux, 2: baz}. If an entry
- * has an index that does not exist in the dense entry list, then it is considered a no-op for
- * that single entry.
- *
- * @param entries A sparse list containing index:entry pairs to override.
- */
- public void overrideEntries(Map entries) {
- for (Map.Entry entry : entries.entrySet()) {
- int index = entry.getKey() != null ? entry.getKey() : -1;
- overrideEntry(index, entry.getValue());
- }
- }
-
- /**
- * Overrides an entry at the given index. Passing null for the {@code entry} will remove that
- * entry from {@code entries}. Indices < 0 or >= {@link #getTotalEntryCount()} are a no-op.
- *
- * @param index The 0-based index for the entry to override.
- * @param entry The entry to override, or null if the entry should be removed at this location.
- */
- public void overrideEntry(int index, @Nullable Entry entry) {
- if (index >= 0 && index < entryCount) {
- if (entry != null) {
- entries.put(index, entry);
- } else {
- entries.remove(index);
- }
- }
- }
-
- protected String getString(int index) {
- ResourceTableChunk resourceTable = getResourceTableChunk();
- Preconditions.checkNotNull(resourceTable, "%s has no resource table.", getClass());
- return resourceTable.getStringPool().getString(index);
- }
-
- protected String getKeyName(int index) {
- PackageChunk packageChunk = getPackageChunk();
- Preconditions.checkNotNull(packageChunk, "%s has no parent package.", getClass());
- StringPoolChunk keyPool = packageChunk.getKeyStringPool();
- Preconditions.checkNotNull(keyPool, "%s's parent package has no key pool.", getClass());
- return keyPool.getString(index);
- }
-
- protected void updateKey(int index, String value) {
- PackageChunk packageChunk = getPackageChunk();
- Preconditions.checkNotNull(packageChunk, "%s has no parent package.", getClass());
- StringPoolChunk keyPool = packageChunk.getKeyStringPool();
- Preconditions.checkNotNull(keyPool, "%s's parent package has no key pool.", getClass());
- keyPool.updateString(index, value);
- }
-
- @Nullable
- private ResourceTableChunk getResourceTableChunk() {
- Chunk chunk = getParent();
- while (chunk != null && !(chunk instanceof ResourceTableChunk)) {
- chunk = chunk.getParent();
- }
- return chunk != null ? (ResourceTableChunk) chunk : null;
- }
-
- /** Returns the package enclosing this chunk, if any. Else, returns null. */
- @Nullable
- public PackageChunk getPackageChunk() {
- Chunk chunk = getParent();
- while (chunk != null && !(chunk instanceof PackageChunk)) {
- chunk = chunk.getParent();
- }
- return chunk != null ? (PackageChunk) chunk : null;
- }
-
- @Override
- protected Type getType() {
- return Type.TABLE_TYPE;
- }
-
- /** Returns the number of bytes needed for offsets based on {@code entries}. */
- private int getOffsetSize() {
- return entryCount * 4;
- }
-
- private int writeEntries(DataOutput payload, ByteBuffer offsets, boolean shrink)
- throws IOException {
- int entryOffset = 0;
- for (int i = 0; i < entryCount; ++i) {
- Entry entry = entries.get(i);
- if (entry == null) {
- offsets.putInt(Entry.NO_ENTRY);
- } else {
- byte[] encodedEntry = entry.toByteArray(shrink);
- payload.write(encodedEntry);
- offsets.putInt(entryOffset);
- entryOffset += encodedEntry.length;
- }
- }
- entryOffset = writePad(payload, entryOffset);
- return entryOffset;
- }
-
- @Override
- protected void writeHeader(ByteBuffer output) {
- int entriesStart = getHeaderSize() + getOffsetSize();
- output.putInt(id); // Write an unsigned byte with 3 bytes padding
- output.putInt(entryCount);
- output.putInt(entriesStart);
- output.put(configuration.toByteArray(false));
- }
-
- @Override
- protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
- throws IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ByteBuffer offsets = ByteBuffer.allocate(getOffsetSize()).order(ByteOrder.LITTLE_ENDIAN);
- try (LittleEndianDataOutputStream payload = new LittleEndianDataOutputStream(baos)) {
- writeEntries(payload, offsets, shrink);
- }
- output.write(offsets.array());
- output.write(baos.toByteArray());
- }
-
- /** An {@link Entry} in a {@link TypeChunk}. Contains one or more {@link BinaryResourceValue}. */
- public static class Entry implements SerializableResource {
-
- /** An entry offset that indicates that a given resource is not present. */
- public static final int NO_ENTRY = 0xFFFFFFFF;
-
- /** Set if this is a complex resource. Otherwise, it's a simple resource. */
- private static final int FLAG_COMPLEX = 0x0001;
-
- /** Size of a single resource id + value mapping entry. */
- private static final int MAPPING_SIZE = 4 + BinaryResourceValue.SIZE;
-
- private final int headerSize;
- private final int flags;
- private final int keyIndex;
- private BinaryResourceValue value;
- private Map values;
- private final int parentEntry;
- private final TypeChunk parent;
-
- private Entry(int headerSize,
- int flags,
- int keyIndex,
- BinaryResourceValue value,
- Map values,
- int parentEntry,
- TypeChunk parent) {
- this.headerSize = headerSize;
- this.flags = flags;
- this.keyIndex = keyIndex;
- this.value = value;
- this.values = values;
- this.parentEntry = parentEntry;
- this.parent = parent;
- }
-
- /** Number of bytes in the header of the {@link Entry}. */
- public int headerSize() { return headerSize; }
-
- /** Resource entry flags. */
- public int flags() { return flags; }
-
- /** Index into {@link PackageChunk#getKeyStringPool} identifying this entry. */
- public int keyIndex() { return keyIndex; }
-
- /** The value of this resource entry, if this is not a complex entry. Else, null. */
- @Nullable
- public BinaryResourceValue value() { return value; }
+ /**
+ * The type identifier of the resource type this chunk is holding.
+ */
+ private final int id;
/**
- * Update the value of this resource entry
- * @param value
+ * The number of resources of this type at creation time.
*/
- public void updateValue(@Nullable BinaryResourceValue value){
- this.value = value;
+ private final int entryCount;
+
+ /**
+ * The offset (from {@code offset}) in the original buffer where {@code entries} start.
+ */
+ private final int entriesStart;
+
+ /**
+ * The resource configuration that these resource entries correspond to.
+ */
+ private BinaryResourceConfiguration configuration;
+
+ /**
+ * A sparse list of resource entries defined by this chunk.
+ */
+ private final Map entries = new TreeMap<>();
+
+ protected TypeChunk(ByteBuffer buffer, @Nullable Chunk parent) {
+ super(buffer, parent);
+ id = UnsignedBytes.toInt(buffer.get());
+ buffer.position(buffer.position() + 3); // Skip 3 bytes for packing
+ entryCount = buffer.getInt();
+ entriesStart = buffer.getInt();
+ configuration = BinaryResourceConfiguration.create(buffer);
}
- /** The extra values in this resource entry if this {@link #isComplex}. */
- public Map values() { return values; }
-
- /** Update the value in this resource entry if this {@link #isComplex}. */
- public void updateValue(int index, BinaryResourceValue value){
- if (index >= 0) {
- if (value != null) {
- values.put(index, value);
- } else {
- values.remove(index);
+ @Override
+ protected void init(ByteBuffer buffer) {
+ int offset = this.offset + entriesStart;
+ for (int i = 0; i < entryCount; ++i) {
+ Entry entry = Entry.create(buffer, offset, this);
+ if (entry != null) {
+ entries.put(i, entry);
+ }
}
- }
}
/**
- * Entry into {@link PackageChunk} that is the parent {@link Entry} to this entry.
- * This value only makes sense when this is complex ({@link #isComplex} returns true).
+ * Returns the (1-based) type id of the resource types that this {@link TypeChunk} is holding.
*/
- public int parentEntry() { return parentEntry; }
-
- /** The {@link TypeChunk} that this resource entry belongs to. */
- public TypeChunk parent() { return parent; }
-
- /** Returns the name of the type this chunk represents (e.g. string, attr, id). */
- public final String typeName() {
- return parent().getTypeName();
- }
-
- /** The total number of bytes that this {@link Entry} takes up. */
- public final int size() {
- return headerSize() + (isComplex() ? values().size() * MAPPING_SIZE : BinaryResourceValue.SIZE);
- }
-
- /** Returns the key name identifying this resource entry. */
- public final String key() {
- return parent().getKeyName(keyIndex());
- }
-
- /** Update the key name in this resource entry. */
- public final void updateKey(int keyIndex, String value) {
- parent().updateKey(keyIndex, value);
- }
-
- /** Returns true if this is a complex resource. */
- public final boolean isComplex() {
- return (flags() & FLAG_COMPLEX) != 0;
+ public int getId() {
+ return id;
}
/**
- * Creates a new {@link Entry} whose contents start at the 0-based position in
- * {@code buffer} given by a 4-byte value read from {@code buffer} and then added to
- * {@code baseOffset}. If the value read from {@code buffer} is equal to {@link #NO_ENTRY}, then
- * null is returned as there is no resource at that position.
+ * Returns the name of the type this chunk represents (e.g. string, attr, id).
+ */
+ public String getTypeName() {
+ PackageChunk packageChunk = getPackageChunk();
+ Preconditions.checkNotNull(packageChunk, "%s has no parent package.", getClass());
+ StringPoolChunk typePool = packageChunk.getTypeStringPool();
+ Preconditions.checkNotNull(typePool, "%s's parent package has no type pool.", getClass());
+ return typePool.getString(getId() - 1); // - 1 here to convert to 0-based index
+ }
+
+ /**
+ * Returns the resource configuration that these resource entries correspond to.
+ */
+ public BinaryResourceConfiguration getConfiguration() {
+ return configuration;
+ }
+
+ /**
+ * Sets the resource configuration that this chunk's entries correspond to.
*
- * Otherwise, this position is parsed and returned as an {@link Entry}.
+ * @param configuration The new configuration.
+ */
+ public void setConfiguration(BinaryResourceConfiguration configuration) {
+ this.configuration = configuration;
+ }
+
+ /**
+ * Returns the total number of entries for this type + configuration, including null entries.
+ */
+ public int getTotalEntryCount() {
+ return entryCount;
+ }
+
+ /**
+ * Returns a sparse list of 0-based indices to resource entries defined by this chunk.
+ */
+ public Map getEntries() {
+ return Collections.unmodifiableMap(entries);
+ }
+
+ /**
+ * Returns true if this chunk contains an entry for {@code resourceId}.
+ */
+ public boolean containsResource(BinaryResourceIdentifier resourceId) {
+ PackageChunk packageChunk = Preconditions.checkNotNull(getPackageChunk());
+ int packageId = packageChunk.getId();
+ int typeId = getId();
+ return resourceId.packageId() == packageId
+ && resourceId.typeId() == typeId
+ && entries.containsKey(resourceId.entryId());
+ }
+
+ /**
+ * Overrides the entries in this chunk at the given index:entry pairs in {@code entries}.
+ * For example, if the current list of entries is {0: foo, 1: bar, 2: baz}, and {@code entries}
+ * is {1: qux, 3: quux}, then the entries will be changed to {0: foo, 1: qux, 2: baz}. If an entry
+ * has an index that does not exist in the dense entry list, then it is considered a no-op for
+ * that single entry.
*
- * @param buffer A buffer positioned at an offset to an {@link Entry}.
- * @param baseOffset Offset that must be added to the value at {@code buffer}'s position.
- * @param parent The {@link TypeChunk} that this resource entry belongs to.
- * @return New {@link Entry} or null if there is no resource at this location.
+ * @param entries A sparse list containing index:entry pairs to override.
+ */
+ public void overrideEntries(Map entries) {
+ for (Map.Entry entry : entries.entrySet()) {
+ int index = entry.getKey() != null ? entry.getKey() : -1;
+ overrideEntry(index, entry.getValue());
+ }
+ }
+
+ /**
+ * Overrides an entry at the given index. Passing null for the {@code entry} will remove that
+ * entry from {@code entries}. Indices < 0 or >= {@link #getTotalEntryCount()} are a no-op.
+ *
+ * @param index The 0-based index for the entry to override.
+ * @param entry The entry to override, or null if the entry should be removed at this location.
+ */
+ public void overrideEntry(int index, @Nullable Entry entry) {
+ if (index >= 0 && index < entryCount) {
+ if (entry != null) {
+ entries.put(index, entry);
+ } else {
+ entries.remove(index);
+ }
+ }
+ }
+
+ protected String getString(int index) {
+ ResourceTableChunk resourceTable = getResourceTableChunk();
+ Preconditions.checkNotNull(resourceTable, "%s has no resource table.", getClass());
+ return resourceTable.getStringPool().getString(index);
+ }
+
+ protected String getKeyName(int index) {
+ PackageChunk packageChunk = getPackageChunk();
+ Preconditions.checkNotNull(packageChunk, "%s has no parent package.", getClass());
+ StringPoolChunk keyPool = packageChunk.getKeyStringPool();
+ Preconditions.checkNotNull(keyPool, "%s's parent package has no key pool.", getClass());
+ return keyPool.getString(index);
+ }
+
+ protected void updateKey(int index, String value) {
+ PackageChunk packageChunk = getPackageChunk();
+ Preconditions.checkNotNull(packageChunk, "%s has no parent package.", getClass());
+ StringPoolChunk keyPool = packageChunk.getKeyStringPool();
+ Preconditions.checkNotNull(keyPool, "%s's parent package has no key pool.", getClass());
+ keyPool.updateString(index, value);
+ }
+
+ @Nullable
+ private ResourceTableChunk getResourceTableChunk() {
+ Chunk chunk = getParent();
+ while (chunk != null && !(chunk instanceof ResourceTableChunk)) {
+ chunk = chunk.getParent();
+ }
+ return chunk != null ? (ResourceTableChunk) chunk : null;
+ }
+
+ /**
+ * Returns the package enclosing this chunk, if any. Else, returns null.
*/
@Nullable
- public static Entry create(ByteBuffer buffer, int baseOffset, TypeChunk parent) {
- int offset = buffer.getInt();
- if (offset == NO_ENTRY) {
- return null;
- }
- int position = buffer.position();
- buffer.position(baseOffset + offset); // Set buffer position to resource entry start
- Entry result = newInstance(buffer, parent);
- buffer.position(position); // Restore buffer position
- return result;
- }
-
- @Nullable
- private static Entry newInstance(ByteBuffer buffer, TypeChunk parent) {
- int headerSize = buffer.getShort() & 0xFFFF;
- int flags = buffer.getShort() & 0xFFFF;
- int keyIndex = buffer.getInt();
- BinaryResourceValue value = null;
- Map values = new LinkedHashMap<>();
- int parentEntry = 0;
- if ((flags & FLAG_COMPLEX) != 0) {
- parentEntry = buffer.getInt();
- int valueCount = buffer.getInt();
- for (int i = 0; i < valueCount; ++i) {
- values.put(buffer.getInt(), BinaryResourceValue.create(buffer));
+ public PackageChunk getPackageChunk() {
+ Chunk chunk = getParent();
+ while (chunk != null && !(chunk instanceof PackageChunk)) {
+ chunk = chunk.getParent();
}
- } else {
- value = BinaryResourceValue.create(buffer);
- }
- return new Entry(headerSize, flags, keyIndex, value, values, parentEntry, parent);
+ return chunk != null ? (PackageChunk) chunk : null;
}
@Override
- public final byte[] toByteArray() {
- return toByteArray(false);
+ protected Type getType() {
+ return Type.TABLE_TYPE;
}
- @Override
- public final byte[] toByteArray(boolean shrink) {
- ByteBuffer buffer = ByteBuffer.allocate(size());
- buffer.order(ByteOrder.LITTLE_ENDIAN);
- buffer.putShort((short) headerSize());
- buffer.putShort((short) flags());
- buffer.putInt(keyIndex());
- if (isComplex()) {
- buffer.putInt(parentEntry());
- buffer.putInt(values().size());
- for (Map.Entry entry : values().entrySet()) {
- buffer.putInt(entry.getKey());
- buffer.put(entry.getValue().toByteArray(shrink));
+ /**
+ * Returns the number of bytes needed for offsets based on {@code entries}.
+ */
+ private int getOffsetSize() {
+ return entryCount * 4;
+ }
+
+ private int writeEntries(DataOutput payload, ByteBuffer offsets, boolean shrink)
+ throws IOException {
+ int entryOffset = 0;
+ for (int i = 0; i < entryCount; ++i) {
+ Entry entry = entries.get(i);
+ if (entry == null) {
+ offsets.putInt(Entry.NO_ENTRY);
+ } else {
+ byte[] encodedEntry = entry.toByteArray(shrink);
+ payload.write(encodedEntry);
+ offsets.putInt(entryOffset);
+ entryOffset += encodedEntry.length;
+ }
}
- } else {
- BinaryResourceValue value = value();
- Preconditions.checkNotNull(value, "A non-complex TypeChunk entry must have a value.");
- buffer.put(value.toByteArray());
- }
- return buffer.array();
+ entryOffset = writePad(payload, entryOffset);
+ return entryOffset;
}
@Override
- public final String toString() {
- return String.format("Entry{key=%s}", key());
+ protected void writeHeader(ByteBuffer output) {
+ int entriesStart = getHeaderSize() + getOffsetSize();
+ output.putInt(id); // Write an unsigned byte with 3 bytes padding
+ output.putInt(entryCount);
+ output.putInt(entriesStart);
+ output.put(configuration.toByteArray(false));
}
@Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Entry entry = (Entry)o;
- return headerSize == entry.headerSize &&
- flags == entry.flags &&
- keyIndex == entry.keyIndex &&
- parentEntry == entry.parentEntry &&
- Objects.equals(value, entry.value) &&
- Objects.equals(values, entry.values) &&
- Objects.equals(parent, entry.parent);
+ protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
+ throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ByteBuffer offsets = ByteBuffer.allocate(getOffsetSize()).order(ByteOrder.LITTLE_ENDIAN);
+ try (LittleEndianDataOutputStream payload = new LittleEndianDataOutputStream(baos)) {
+ writeEntries(payload, offsets, shrink);
+ }
+ output.write(offsets.array());
+ output.write(baos.toByteArray());
}
- @Override
- public int hashCode() {
- return Objects.hash(headerSize, flags, keyIndex, value, values, parentEntry, parent);
+ /**
+ * An {@link Entry} in a {@link TypeChunk}. Contains one or more {@link BinaryResourceValue}.
+ */
+ public static class Entry implements SerializableResource {
+
+ /**
+ * An entry offset that indicates that a given resource is not present.
+ */
+ public static final int NO_ENTRY = 0xFFFFFFFF;
+
+ /**
+ * Set if this is a complex resource. Otherwise, it's a simple resource.
+ */
+ private static final int FLAG_COMPLEX = 0x0001;
+
+ /**
+ * Size of a single resource id + value mapping entry.
+ */
+ private static final int MAPPING_SIZE = 4 + BinaryResourceValue.SIZE;
+
+ private final int headerSize;
+ private final int flags;
+ private final int keyIndex;
+ private BinaryResourceValue value;
+ private Map values;
+ private final int parentEntry;
+ private final TypeChunk parent;
+
+ private Entry(int headerSize,
+ int flags,
+ int keyIndex,
+ BinaryResourceValue value,
+ Map values,
+ int parentEntry,
+ TypeChunk parent) {
+ this.headerSize = headerSize;
+ this.flags = flags;
+ this.keyIndex = keyIndex;
+ this.value = value;
+ this.values = values;
+ this.parentEntry = parentEntry;
+ this.parent = parent;
+ }
+
+ /**
+ * Number of bytes in the header of the {@link Entry}.
+ */
+ public int headerSize() {
+ return headerSize;
+ }
+
+ /**
+ * Resource entry flags.
+ */
+ public int flags() {
+ return flags;
+ }
+
+ /**
+ * Index into {@link PackageChunk#getKeyStringPool} identifying this entry.
+ */
+ public int keyIndex() {
+ return keyIndex;
+ }
+
+ /**
+ * The value of this resource entry, if this is not a complex entry. Else, null.
+ */
+ @Nullable
+ public BinaryResourceValue value() {
+ return value;
+ }
+
+ /**
+ * Update the value of this resource entry
+ *
+ * @param value
+ */
+ public void updateValue(@Nullable BinaryResourceValue value) {
+ this.value = value;
+ }
+
+ /**
+ * The extra values in this resource entry if this {@link #isComplex}.
+ */
+ public Map values() {
+ return values;
+ }
+
+ /**
+ * Update the value in this resource entry if this {@link #isComplex}.
+ */
+ public void updateValue(int index, BinaryResourceValue value) {
+ if (index >= 0) {
+ if (value != null) {
+ values.put(index, value);
+ } else {
+ values.remove(index);
+ }
+ }
+ }
+
+ /**
+ * Entry into {@link PackageChunk} that is the parent {@link Entry} to this entry.
+ * This value only makes sense when this is complex ({@link #isComplex} returns true).
+ */
+ public int parentEntry() {
+ return parentEntry;
+ }
+
+ /**
+ * The {@link TypeChunk} that this resource entry belongs to.
+ */
+ public TypeChunk parent() {
+ return parent;
+ }
+
+ /**
+ * Returns the name of the type this chunk represents (e.g. string, attr, id).
+ */
+ public final String typeName() {
+ return parent().getTypeName();
+ }
+
+ /**
+ * The total number of bytes that this {@link Entry} takes up.
+ */
+ public final int size() {
+ return headerSize() + (isComplex() ? values().size() * MAPPING_SIZE : BinaryResourceValue.SIZE);
+ }
+
+ /**
+ * Returns the key name identifying this resource entry.
+ */
+ public final String key() {
+ return parent().getKeyName(keyIndex());
+ }
+
+ /**
+ * Update the key name in this resource entry.
+ */
+ public final void updateKey(int keyIndex, String value) {
+ parent().updateKey(keyIndex, value);
+ }
+
+ /**
+ * Returns true if this is a complex resource.
+ */
+ public final boolean isComplex() {
+ return (flags() & FLAG_COMPLEX) != 0;
+ }
+
+ /**
+ * Creates a new {@link Entry} whose contents start at the 0-based position in
+ * {@code buffer} given by a 4-byte value read from {@code buffer} and then added to
+ * {@code baseOffset}. If the value read from {@code buffer} is equal to {@link #NO_ENTRY}, then
+ * null is returned as there is no resource at that position.
+ *
+ * Otherwise, this position is parsed and returned as an {@link Entry}.
+ *
+ * @param buffer A buffer positioned at an offset to an {@link Entry}.
+ * @param baseOffset Offset that must be added to the value at {@code buffer}'s position.
+ * @param parent The {@link TypeChunk} that this resource entry belongs to.
+ * @return New {@link Entry} or null if there is no resource at this location.
+ */
+ @Nullable
+ public static Entry create(ByteBuffer buffer, int baseOffset, TypeChunk parent) {
+ int offset = buffer.getInt();
+ if (offset == NO_ENTRY) {
+ return null;
+ }
+ int position = buffer.position();
+ buffer.position(baseOffset + offset); // Set buffer position to resource entry start
+ Entry result = newInstance(buffer, parent);
+ buffer.position(position); // Restore buffer position
+ return result;
+ }
+
+ @Nullable
+ private static Entry newInstance(ByteBuffer buffer, TypeChunk parent) {
+ int headerSize = buffer.getShort() & 0xFFFF;
+ int flags = buffer.getShort() & 0xFFFF;
+ int keyIndex = buffer.getInt();
+ BinaryResourceValue value = null;
+ Map values = new LinkedHashMap<>();
+ int parentEntry = 0;
+ if ((flags & FLAG_COMPLEX) != 0) {
+ parentEntry = buffer.getInt();
+ int valueCount = buffer.getInt();
+ for (int i = 0; i < valueCount; ++i) {
+ values.put(buffer.getInt(), BinaryResourceValue.create(buffer));
+ }
+ } else {
+ value = BinaryResourceValue.create(buffer);
+ }
+ return new Entry(headerSize, flags, keyIndex, value, values, parentEntry, parent);
+ }
+
+ @Override
+ public final byte[] toByteArray() {
+ return toByteArray(false);
+ }
+
+ @Override
+ public final byte[] toByteArray(boolean shrink) {
+ ByteBuffer buffer = ByteBuffer.allocate(size());
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ buffer.putShort((short) headerSize());
+ buffer.putShort((short) flags());
+ buffer.putInt(keyIndex());
+ if (isComplex()) {
+ buffer.putInt(parentEntry());
+ buffer.putInt(values().size());
+ for (Map.Entry entry : values().entrySet()) {
+ buffer.putInt(entry.getKey());
+ buffer.put(entry.getValue().toByteArray(shrink));
+ }
+ } else {
+ BinaryResourceValue value = value();
+ Preconditions.checkNotNull(value, "A non-complex TypeChunk entry must have a value.");
+ buffer.put(value.toByteArray());
+ }
+ return buffer.array();
+ }
+
+ @Override
+ public final String toString() {
+ return String.format("Entry{key=%s}", key());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Entry entry = (Entry) o;
+ return headerSize == entry.headerSize &&
+ flags == entry.flags &&
+ keyIndex == entry.keyIndex &&
+ parentEntry == entry.parentEntry &&
+ Objects.equals(value, entry.value) &&
+ Objects.equals(values, entry.values) &&
+ Objects.equals(parent, entry.parent);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(headerSize, flags, keyIndex, value, values, parentEntry, parent);
+ }
}
- }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/TypeSpecChunk.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/TypeSpecChunk.java
index 691d9a38a..c12922b8d 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/TypeSpecChunk.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/TypeSpecChunk.java
@@ -16,86 +16,98 @@
package com.google.devrel.gmscore.tools.apk.arsc;
+import androidx.annotation.Nullable;
import com.google.common.base.Preconditions;
import com.google.common.primitives.UnsignedBytes;
-import androidx.annotation.Nullable;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
-/** A chunk that contains a collection of resource entries for a particular resource data type. */
+/**
+ * A chunk that contains a collection of resource entries for a particular resource data type.
+ */
public final class TypeSpecChunk extends Chunk {
- /** The id of the resource type that this type spec refers to. */
- private final int id;
+ /**
+ * The id of the resource type that this type spec refers to.
+ */
+ private final int id;
- /** Resource configuration masks. */
- private final int[] resources;
+ /**
+ * Resource configuration masks.
+ */
+ private final int[] resources;
- protected TypeSpecChunk(ByteBuffer buffer, @Nullable Chunk parent) {
- super(buffer, parent);
- id = UnsignedBytes.toInt(buffer.get());
- buffer.position(buffer.position() + 3); // Skip 3 bytes for packing
- int resourceCount = buffer.getInt();
- resources = new int[resourceCount];
+ protected TypeSpecChunk(ByteBuffer buffer, @Nullable Chunk parent) {
+ super(buffer, parent);
+ id = UnsignedBytes.toInt(buffer.get());
+ buffer.position(buffer.position() + 3); // Skip 3 bytes for packing
+ int resourceCount = buffer.getInt();
+ resources = new int[resourceCount];
- for (int i = 0; i < resourceCount; ++i) {
- resources[i] = buffer.getInt();
+ for (int i = 0; i < resourceCount; ++i) {
+ resources[i] = buffer.getInt();
+ }
}
- }
- /**
- * Returns the (1-based) type id of the resources that this {@link TypeSpecChunk} has
- * configuration masks for.
- */
- public int getId() {
- return id;
- }
-
- /** Returns the number of resource entries that this chunk has configuration masks for. */
- public int getResourceCount() {
- return resources.length;
- }
-
- @Override
- protected Type getType() {
- return Type.TABLE_TYPE_SPEC;
- }
-
- /** Returns the name of the type this chunk represents (e.g. string, attr, id). */
- public String getTypeName() {
- PackageChunk packageChunk = getPackageChunk();
- Preconditions.checkNotNull(packageChunk, "%s has no parent package.", getClass());
- StringPoolChunk typePool = packageChunk.getTypeStringPool();
- Preconditions.checkNotNull(typePool, "%s's parent package has no type pool.", getClass());
- return typePool.getString(getId() - 1); // - 1 here to convert to 0-based index
- }
-
- /** Returns the package enclosing this chunk, if any. Else, returns null. */
- @Nullable
- private PackageChunk getPackageChunk() {
- Chunk chunk = getParent();
- while (chunk != null && !(chunk instanceof PackageChunk)) {
- chunk = chunk.getParent();
+ /**
+ * Returns the (1-based) type id of the resources that this {@link TypeSpecChunk} has
+ * configuration masks for.
+ */
+ public int getId() {
+ return id;
}
- return chunk != null ? (PackageChunk) chunk : null;
- }
- @Override
- protected void writeHeader(ByteBuffer output) {
- super.writeHeader(output);
- // id is an unsigned byte in the range [0-255]. It is guaranteed to be non-negative.
- // Because our output is in little-endian, we are making use of the 4 byte packing here
- output.putInt(id);
- output.putInt(resources.length);
- }
-
- @Override
- protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
- throws IOException {
- for (int resource : resources) {
- output.writeInt(resource);
+ /**
+ * Returns the number of resource entries that this chunk has configuration masks for.
+ */
+ public int getResourceCount() {
+ return resources.length;
+ }
+
+ @Override
+ protected Type getType() {
+ return Type.TABLE_TYPE_SPEC;
+ }
+
+ /**
+ * Returns the name of the type this chunk represents (e.g. string, attr, id).
+ */
+ public String getTypeName() {
+ PackageChunk packageChunk = getPackageChunk();
+ Preconditions.checkNotNull(packageChunk, "%s has no parent package.", getClass());
+ StringPoolChunk typePool = packageChunk.getTypeStringPool();
+ Preconditions.checkNotNull(typePool, "%s's parent package has no type pool.", getClass());
+ return typePool.getString(getId() - 1); // - 1 here to convert to 0-based index
+ }
+
+ /**
+ * Returns the package enclosing this chunk, if any. Else, returns null.
+ */
+ @Nullable
+ private PackageChunk getPackageChunk() {
+ Chunk chunk = getParent();
+ while (chunk != null && !(chunk instanceof PackageChunk)) {
+ chunk = chunk.getParent();
+ }
+ return chunk != null ? (PackageChunk) chunk : null;
+ }
+
+ @Override
+ protected void writeHeader(ByteBuffer output) {
+ super.writeHeader(output);
+ // id is an unsigned byte in the range [0-255]. It is guaranteed to be non-negative.
+ // Because our output is in little-endian, we are making use of the 4 byte packing here
+ output.putInt(id);
+ output.putInt(resources.length);
+ }
+
+ @Override
+ protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
+ throws IOException {
+ for (int resource : resources) {
+ output.writeInt(resource);
+ }
}
- }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/UnknownChunk.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/UnknownChunk.java
index 7117e63f7..0d07c3199 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/UnknownChunk.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/UnknownChunk.java
@@ -17,6 +17,7 @@
package com.google.devrel.gmscore.tools.apk.arsc;
import androidx.annotation.Nullable;
+
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -27,35 +28,35 @@ import java.nio.ByteBuffer;
*/
public final class UnknownChunk extends Chunk {
- private final Type type;
+ private final Type type;
- private final byte[] header;
+ private final byte[] header;
- private final byte[] payload;
+ private final byte[] payload;
- protected UnknownChunk(ByteBuffer buffer, @Nullable Chunk parent) {
- super(buffer, parent);
+ protected UnknownChunk(ByteBuffer buffer, @Nullable Chunk parent) {
+ super(buffer, parent);
- type = Type.fromCode(buffer.getShort(offset));
- header = new byte[headerSize - Chunk.METADATA_SIZE];
- payload = new byte[chunkSize - headerSize];
- buffer.get(header);
- buffer.get(payload);
- }
+ type = Type.fromCode(buffer.getShort(offset));
+ header = new byte[headerSize - Chunk.METADATA_SIZE];
+ payload = new byte[chunkSize - headerSize];
+ buffer.get(header);
+ buffer.get(payload);
+ }
- @Override
- protected void writeHeader(ByteBuffer output) {
- output.put(header);
- }
+ @Override
+ protected void writeHeader(ByteBuffer output) {
+ output.put(header);
+ }
- @Override
- protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
- throws IOException {
- output.write(payload);
- }
+ @Override
+ protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
+ throws IOException {
+ output.write(payload);
+ }
- @Override
- protected Type getType() {
- return type;
- }
+ @Override
+ protected Type getType() {
+ return type;
+ }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlAttribute.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlAttribute.java
index 1cb2977b3..f3d696834 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlAttribute.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlAttribute.java
@@ -20,129 +20,149 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Objects;
-/** Represents an XML attribute and value. */
+/**
+ * Represents an XML attribute and value.
+ */
public class XmlAttribute implements SerializableResource {
- /** The serialized size in bytes of an {@link XmlAttribute}. */
- public static final int SIZE = 12 + BinaryResourceValue.SIZE;
+ /**
+ * The serialized size in bytes of an {@link XmlAttribute}.
+ */
+ public static final int SIZE = 12 + BinaryResourceValue.SIZE;
- private final int namespaceIndex;
- private final int nameIndex;
- private final int rawValueIndex;
- private final BinaryResourceValue typedValue;
- private final XmlNodeChunk parent;
+ private final int namespaceIndex;
+ private final int nameIndex;
+ private final int rawValueIndex;
+ private final BinaryResourceValue typedValue;
+ private final XmlNodeChunk parent;
- /**
- * Creates a new {@link XmlAttribute} based on the bytes at the current {@code buffer} position.
- *
- * @param buffer A buffer whose position is at the start of a {@link XmlAttribute}.
- * @param parent The parent chunk that contains this attribute; used for string lookups.
- */
- public static XmlAttribute create(ByteBuffer buffer, XmlNodeChunk parent) {
- int namespace = buffer.getInt();
- int name = buffer.getInt();
- int rawValue = buffer.getInt();
- BinaryResourceValue typedValue = BinaryResourceValue.create(buffer);
- return new XmlAttribute(namespace, name, rawValue, typedValue, parent);
- }
+ /**
+ * Creates a new {@link XmlAttribute} based on the bytes at the current {@code buffer} position.
+ *
+ * @param buffer A buffer whose position is at the start of a {@link XmlAttribute}.
+ * @param parent The parent chunk that contains this attribute; used for string lookups.
+ */
+ public static XmlAttribute create(ByteBuffer buffer, XmlNodeChunk parent) {
+ int namespace = buffer.getInt();
+ int name = buffer.getInt();
+ int rawValue = buffer.getInt();
+ BinaryResourceValue typedValue = BinaryResourceValue.create(buffer);
+ return new XmlAttribute(namespace, name, rawValue, typedValue, parent);
+ }
- private XmlAttribute(int namespaceIndex,
- int nameIndex,
- int rawValueIndex,
- BinaryResourceValue typedValue,
- XmlNodeChunk parent) {
- this.namespaceIndex = namespaceIndex;
- this.nameIndex = nameIndex;
- this.rawValueIndex = rawValueIndex;
- this.typedValue = typedValue;
- this.parent = parent;
- }
+ private XmlAttribute(int namespaceIndex,
+ int nameIndex,
+ int rawValueIndex,
+ BinaryResourceValue typedValue,
+ XmlNodeChunk parent) {
+ this.namespaceIndex = namespaceIndex;
+ this.nameIndex = nameIndex;
+ this.rawValueIndex = rawValueIndex;
+ this.typedValue = typedValue;
+ this.parent = parent;
+ }
- /** A string reference to the namespace URI, or -1 if not present. */
- public int namespaceIndex() {
- return namespaceIndex;
- }
+ /**
+ * A string reference to the namespace URI, or -1 if not present.
+ */
+ public int namespaceIndex() {
+ return namespaceIndex;
+ }
- /** A string reference to the attribute name. */
- public int nameIndex() {
- return nameIndex;
- }
+ /**
+ * A string reference to the attribute name.
+ */
+ public int nameIndex() {
+ return nameIndex;
+ }
- /** A string reference to a string containing the character value. */
- public int rawValueIndex() {
- return rawValueIndex;
- }
+ /**
+ * A string reference to a string containing the character value.
+ */
+ public int rawValueIndex() {
+ return rawValueIndex;
+ }
- /** A {@link BinaryResourceValue} instance containing the parsed value. */
- public BinaryResourceValue typedValue() {
- return typedValue;
- }
+ /**
+ * A {@link BinaryResourceValue} instance containing the parsed value.
+ */
+ public BinaryResourceValue typedValue() {
+ return typedValue;
+ }
- /** The parent of this XML attribute; used for dereferencing the namespace and name. */
- public XmlNodeChunk parent() {
- return parent;
- }
+ /**
+ * The parent of this XML attribute; used for dereferencing the namespace and name.
+ */
+ public XmlNodeChunk parent() {
+ return parent;
+ }
- /** The namespace URI, or the empty string if not present. */
- public final String namespace() {
- return getString(namespaceIndex());
- }
+ /**
+ * The namespace URI, or the empty string if not present.
+ */
+ public final String namespace() {
+ return getString(namespaceIndex());
+ }
- /** The attribute name, or the empty string if not present. */
- public final String name() {
- return getString(nameIndex());
- }
+ /**
+ * The attribute name, or the empty string if not present.
+ */
+ public final String name() {
+ return getString(nameIndex());
+ }
- /** The raw character value. */
- public final String rawValue() {
- return getString(rawValueIndex());
- }
+ /**
+ * The raw character value.
+ */
+ public final String rawValue() {
+ return getString(rawValueIndex());
+ }
- private String getString(int index) {
- return parent().getString(index);
- }
+ private String getString(int index) {
+ return parent().getString(index);
+ }
- @Override
- public byte[] toByteArray() {
- return toByteArray(false);
- }
+ @Override
+ public byte[] toByteArray() {
+ return toByteArray(false);
+ }
- @Override
- public byte[] toByteArray(boolean shrink) {
- ByteBuffer buffer = ByteBuffer.allocate(SIZE).order(ByteOrder.LITTLE_ENDIAN);
- buffer.putInt(namespaceIndex());
- buffer.putInt(nameIndex());
- buffer.putInt(rawValueIndex());
- buffer.put(typedValue().toByteArray(shrink));
- return buffer.array();
- }
+ @Override
+ public byte[] toByteArray(boolean shrink) {
+ ByteBuffer buffer = ByteBuffer.allocate(SIZE).order(ByteOrder.LITTLE_ENDIAN);
+ buffer.putInt(namespaceIndex());
+ buffer.putInt(nameIndex());
+ buffer.putInt(rawValueIndex());
+ buffer.put(typedValue().toByteArray(shrink));
+ return buffer.array();
+ }
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- XmlAttribute that = (XmlAttribute)o;
- return namespaceIndex == that.namespaceIndex &&
- nameIndex == that.nameIndex &&
- rawValueIndex == that.rawValueIndex &&
- Objects.equals(typedValue, that.typedValue) &&
- Objects.equals(parent, that.parent);
- }
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ XmlAttribute that = (XmlAttribute) o;
+ return namespaceIndex == that.namespaceIndex &&
+ nameIndex == that.nameIndex &&
+ rawValueIndex == that.rawValueIndex &&
+ Objects.equals(typedValue, that.typedValue) &&
+ Objects.equals(parent, that.parent);
+ }
- @Override
- public int hashCode() {
- return Objects.hash(namespaceIndex, nameIndex, rawValueIndex, typedValue, parent);
- }
+ @Override
+ public int hashCode() {
+ return Objects.hash(namespaceIndex, nameIndex, rawValueIndex, typedValue, parent);
+ }
- /**
- * Returns a brief description of this XML attribute. The representation of this information is
- * subject to change, but below is a typical example:
- *
- * "XmlAttribute{namespace=foo, name=bar, value=1234}"
- */
- @Override
- public String toString() {
- return String.format("XmlAttribute{namespace=%s, name=%s, value=%s}",
- namespace(), name(), rawValue());
- }
+ /**
+ * Returns a brief description of this XML attribute. The representation of this information is
+ * subject to change, but below is a typical example:
+ *
+ * "XmlAttribute{namespace=foo, name=bar, value=1234}"
+ */
+ @Override
+ public String toString() {
+ return String.format("XmlAttribute{namespace=%s, name=%s, value=%s}",
+ namespace(), name(), rawValue());
+ }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlCdataChunk.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlCdataChunk.java
index 44bc66bef..2f961cb35 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlCdataChunk.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlCdataChunk.java
@@ -17,57 +17,68 @@
package com.google.devrel.gmscore.tools.apk.arsc;
import androidx.annotation.Nullable;
+
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
-/** Represents an XML cdata node. */
+/**
+ * Represents an XML cdata node.
+ */
public final class XmlCdataChunk extends XmlNodeChunk {
- /** A string reference to a string containing the raw character data. */
- private final int rawValue;
+ /**
+ * A string reference to a string containing the raw character data.
+ */
+ private final int rawValue;
- /** A {@link BinaryResourceValue} instance containing the parsed value. */
- private final BinaryResourceValue binaryResourceValue;
+ /**
+ * A {@link BinaryResourceValue} instance containing the parsed value.
+ */
+ private final BinaryResourceValue binaryResourceValue;
- protected XmlCdataChunk(ByteBuffer buffer, @Nullable Chunk parent) {
- super(buffer, parent);
- rawValue = buffer.getInt();
- binaryResourceValue = BinaryResourceValue.create(buffer);
- }
+ protected XmlCdataChunk(ByteBuffer buffer, @Nullable Chunk parent) {
+ super(buffer, parent);
+ rawValue = buffer.getInt();
+ binaryResourceValue = BinaryResourceValue.create(buffer);
+ }
- /** Returns a string containing the raw character data of this chunk. */
- public String getRawValue() {
- return getString(rawValue);
- }
+ /**
+ * Returns a string containing the raw character data of this chunk.
+ */
+ public String getRawValue() {
+ return getString(rawValue);
+ }
- /** Returns a {@link BinaryResourceValue} instance containing the parsed cdata value. */
- public BinaryResourceValue getResourceValue() {
- return binaryResourceValue;
- }
+ /**
+ * Returns a {@link BinaryResourceValue} instance containing the parsed cdata value.
+ */
+ public BinaryResourceValue getResourceValue() {
+ return binaryResourceValue;
+ }
- @Override
- protected Type getType() {
- return Type.XML_CDATA;
- }
+ @Override
+ protected Type getType() {
+ return Type.XML_CDATA;
+ }
- @Override
- protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
- throws IOException {
- super.writePayload(output, header, shrink);
- output.writeInt(rawValue);
- output.write(binaryResourceValue.toByteArray());
- }
+ @Override
+ protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
+ throws IOException {
+ super.writePayload(output, header, shrink);
+ output.writeInt(rawValue);
+ output.write(binaryResourceValue.toByteArray());
+ }
- /**
- * Returns a brief description of this XML node. The representation of this information is
- * subject to change, but below is a typical example:
- *
- * "XmlCdataChunk{line=1234, comment=My awesome comment., value=1234}"
- */
- @Override
- public String toString() {
- return String.format("XmlCdataChunk{line=%d, comment=%s, value=%s}",
- getLineNumber(), getComment(), getRawValue());
- }
+ /**
+ * Returns a brief description of this XML node. The representation of this information is
+ * subject to change, but below is a typical example:
+ *
+ * "XmlCdataChunk{line=1234, comment=My awesome comment., value=1234}"
+ */
+ @Override
+ public String toString() {
+ return String.format("XmlCdataChunk{line=%d, comment=%s, value=%s}",
+ getLineNumber(), getComment(), getRawValue());
+ }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlChunk.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlChunk.java
index 08aba6d3a..9c55bd42a 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlChunk.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlChunk.java
@@ -17,6 +17,7 @@
package com.google.devrel.gmscore.tools.apk.arsc;
import androidx.annotation.Nullable;
+
import java.nio.ByteBuffer;
/**
@@ -27,22 +28,24 @@ import java.nio.ByteBuffer;
*/
public final class XmlChunk extends ChunkWithChunks {
- protected XmlChunk(ByteBuffer buffer, @Nullable Chunk parent) {
- super(buffer, parent);
- }
+ protected XmlChunk(ByteBuffer buffer, @Nullable Chunk parent) {
+ super(buffer, parent);
+ }
- @Override
- protected Type getType() {
- return Type.XML;
- }
+ @Override
+ protected Type getType() {
+ return Type.XML;
+ }
- /** Returns a string at the provided (0-based) index if the index exists in the string pool. */
- public String getString(int index) {
- for (Chunk chunk : getChunks().values()) {
- if (chunk instanceof StringPoolChunk) {
- return ((StringPoolChunk) chunk).getString(index);
- }
+ /**
+ * Returns a string at the provided (0-based) index if the index exists in the string pool.
+ */
+ public String getString(int index) {
+ for (Chunk chunk : getChunks().values()) {
+ if (chunk instanceof StringPoolChunk) {
+ return ((StringPoolChunk) chunk).getString(index);
+ }
+ }
+ throw new IllegalStateException("XmlChunk did not contain a string pool.");
}
- throw new IllegalStateException("XmlChunk did not contain a string pool.");
- }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlEndElementChunk.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlEndElementChunk.java
index c336c68b9..f3020eeb8 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlEndElementChunk.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlEndElementChunk.java
@@ -17,59 +17,70 @@
package com.google.devrel.gmscore.tools.apk.arsc;
import androidx.annotation.Nullable;
+
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
-/** Represents the end of an XML node. */
+/**
+ * Represents the end of an XML node.
+ */
public final class XmlEndElementChunk extends XmlNodeChunk {
- /** A string reference to the namespace URI, or -1 if not present. */
- private final int namespace;
+ /**
+ * A string reference to the namespace URI, or -1 if not present.
+ */
+ private final int namespace;
- /** A string reference to the attribute name. */
- private final int name;
+ /**
+ * A string reference to the attribute name.
+ */
+ private final int name;
- protected XmlEndElementChunk(ByteBuffer buffer, @Nullable Chunk parent) {
- super(buffer, parent);
- namespace = buffer.getInt();
- name = buffer.getInt();
- }
+ protected XmlEndElementChunk(ByteBuffer buffer, @Nullable Chunk parent) {
+ super(buffer, parent);
+ namespace = buffer.getInt();
+ name = buffer.getInt();
+ }
- /** Returns the namespace URI, or the empty string if no namespace is present. */
- public String getNamespace() {
- return getString(namespace);
- }
+ /**
+ * Returns the namespace URI, or the empty string if no namespace is present.
+ */
+ public String getNamespace() {
+ return getString(namespace);
+ }
- /** Returns the attribute name. */
- public String getName() {
- return getString(name);
- }
+ /**
+ * Returns the attribute name.
+ */
+ public String getName() {
+ return getString(name);
+ }
- @Override
- protected Type getType() {
- return Type.XML_END_ELEMENT;
- }
+ @Override
+ protected Type getType() {
+ return Type.XML_END_ELEMENT;
+ }
- @Override
- protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
- throws IOException {
- super.writePayload(output, header, shrink);
- output.writeInt(namespace);
- output.writeInt(name);
- }
+ @Override
+ protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
+ throws IOException {
+ super.writePayload(output, header, shrink);
+ output.writeInt(namespace);
+ output.writeInt(name);
+ }
- /**
- * Returns a brief description of this XML node. The representation of this information is
- * subject to change, but below is a typical example:
- *
- *
- * "XmlEndElementChunk{line=1234, comment=My awesome comment., namespace=foo, name=bar}"
- *
- */
- @Override
- public String toString() {
- return String.format("XmlEndElementChunk{line=%d, comment=%s, namespace=%s, name=%s}",
- getLineNumber(), getComment(), getNamespace(), getName());
- }
+ /**
+ * Returns a brief description of this XML node. The representation of this information is
+ * subject to change, but below is a typical example:
+ *
+ *
+ * "XmlEndElementChunk{line=1234, comment=My awesome comment., namespace=foo, name=bar}"
+ *
+ */
+ @Override
+ public String toString() {
+ return String.format("XmlEndElementChunk{line=%d, comment=%s, namespace=%s, name=%s}",
+ getLineNumber(), getComment(), getNamespace(), getName());
+ }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlNamespaceChunk.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlNamespaceChunk.java
index dce571456..d90955eb1 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlNamespaceChunk.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlNamespaceChunk.java
@@ -17,54 +17,65 @@
package com.google.devrel.gmscore.tools.apk.arsc;
import androidx.annotation.Nullable;
+
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
-/** Represents the start/end of a namespace in an XML document. */
+/**
+ * Represents the start/end of a namespace in an XML document.
+ */
public abstract class XmlNamespaceChunk extends XmlNodeChunk {
- /** A string reference to the namespace prefix. */
- private final int prefix;
+ /**
+ * A string reference to the namespace prefix.
+ */
+ private final int prefix;
- /** A string reference to the namespace URI. */
- private final int uri;
+ /**
+ * A string reference to the namespace URI.
+ */
+ private final int uri;
- protected XmlNamespaceChunk(ByteBuffer buffer, @Nullable Chunk parent) {
- super(buffer, parent);
- prefix = buffer.getInt();
- uri = buffer.getInt();
- }
+ protected XmlNamespaceChunk(ByteBuffer buffer, @Nullable Chunk parent) {
+ super(buffer, parent);
+ prefix = buffer.getInt();
+ uri = buffer.getInt();
+ }
- /** Returns the namespace prefix. */
- public String getPrefix() {
- return getString(prefix);
- }
+ /**
+ * Returns the namespace prefix.
+ */
+ public String getPrefix() {
+ return getString(prefix);
+ }
- /** Returns the namespace URI. */
- public String getUri() {
- return getString(uri);
- }
+ /**
+ * Returns the namespace URI.
+ */
+ public String getUri() {
+ return getString(uri);
+ }
- @Override
- protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
- throws IOException {
- super.writePayload(output, header, shrink);
- output.writeInt(prefix);
- output.writeInt(uri);
- }
+ @Override
+ protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
+ throws IOException {
+ super.writePayload(output, header, shrink);
+ output.writeInt(prefix);
+ output.writeInt(uri);
+ }
- /**
- * Returns a brief description of this namespace chunk. The representation of this information is
- * subject to change, but below is a typical example:
- *
- *
- * "XmlNamespaceChunk{line=1234, comment=My awesome comment., prefix=foo, uri=com.google.foo}"
- *
- */
- @Override
- public String toString() {
- return String.format("XmlNamespaceChunk{line=%d, comment=%s, prefix=%s, uri=%s}",
- getLineNumber(), getComment(), getPrefix(), getUri());
- }
+ /**
+ * Returns a brief description of this namespace chunk. The representation of this information is
+ * subject to change, but below is a typical example:
+ *
+ *
+ * "XmlNamespaceChunk{line=1234, comment=My awesome comment., prefix=foo, uri=com.google.foo}"
+ *
+ */
+ @Override
+ public String toString() {
+ return String.format("XmlNamespaceChunk{line=%d, comment=%s, prefix=%s, uri=%s}",
+ getLineNumber(), getComment(), getPrefix(), getUri());
+ }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlNamespaceEndChunk.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlNamespaceEndChunk.java
index 6cce6e9fd..49f70957d 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlNamespaceEndChunk.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlNamespaceEndChunk.java
@@ -17,17 +17,20 @@
package com.google.devrel.gmscore.tools.apk.arsc;
import androidx.annotation.Nullable;
+
import java.nio.ByteBuffer;
-/** Represents the ending tag of a namespace in an XML document. */
+/**
+ * Represents the ending tag of a namespace in an XML document.
+ */
public final class XmlNamespaceEndChunk extends XmlNamespaceChunk {
- protected XmlNamespaceEndChunk(ByteBuffer buffer, @Nullable Chunk parent) {
- super(buffer, parent);
- }
+ protected XmlNamespaceEndChunk(ByteBuffer buffer, @Nullable Chunk parent) {
+ super(buffer, parent);
+ }
- @Override
- protected Type getType() {
- return Type.XML_END_NAMESPACE;
- }
+ @Override
+ protected Type getType() {
+ return Type.XML_END_NAMESPACE;
+ }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlNamespaceStartChunk.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlNamespaceStartChunk.java
index 3cb3daff9..1fbd23383 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlNamespaceStartChunk.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlNamespaceStartChunk.java
@@ -17,17 +17,20 @@
package com.google.devrel.gmscore.tools.apk.arsc;
import androidx.annotation.Nullable;
+
import java.nio.ByteBuffer;
-/** Represents the starting tag of a namespace in an XML document. */
+/**
+ * Represents the starting tag of a namespace in an XML document.
+ */
public final class XmlNamespaceStartChunk extends XmlNamespaceChunk {
- protected XmlNamespaceStartChunk(ByteBuffer buffer, @Nullable Chunk parent) {
- super(buffer, parent);
- }
+ protected XmlNamespaceStartChunk(ByteBuffer buffer, @Nullable Chunk parent) {
+ super(buffer, parent);
+ }
- @Override
- protected Type getType() {
- return Type.XML_START_NAMESPACE;
- }
+ @Override
+ protected Type getType() {
+ return Type.XML_START_NAMESPACE;
+ }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlNodeChunk.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlNodeChunk.java
index 3f293c0a9..2dddc3578 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlNodeChunk.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlNodeChunk.java
@@ -17,80 +17,93 @@
package com.google.devrel.gmscore.tools.apk.arsc;
import androidx.annotation.Nullable;
+
import java.nio.ByteBuffer;
-/** The common superclass for the various types of XML nodes. */
+/**
+ * The common superclass for the various types of XML nodes.
+ */
public abstract class XmlNodeChunk extends Chunk {
- /** The line number in the original source at which this node appeared. */
- private final int lineNumber;
+ /**
+ * The line number in the original source at which this node appeared.
+ */
+ private final int lineNumber;
- /** A string reference of this node's comment. If this is -1, then there is no comment. */
- private final int comment;
+ /**
+ * A string reference of this node's comment. If this is -1, then there is no comment.
+ */
+ private final int comment;
- protected XmlNodeChunk(ByteBuffer buffer, @Nullable Chunk parent) {
- super(buffer, parent);
- lineNumber = buffer.getInt();
- comment = buffer.getInt();
- }
-
- /** Returns true if this XML node contains a comment. Else, returns false. */
- public boolean hasComment() {
- return comment != -1;
- }
-
- /** Returns the line number in the original source at which this node appeared. */
- public int getLineNumber() {
- return lineNumber;
- }
-
- /** Returns the comment associated with this node, if any. Else, returns the empty string. */
- public String getComment() {
- return getString(comment);
- }
-
- /**
- * An {@link XmlNodeChunk} does not know by itself what strings its indices reference. In order
- * to get the actual string, the first {@link XmlChunk} ancestor is found. The
- * {@link XmlChunk} ancestor should have a string pool which {@code index} references.
- *
- * @param index The index of the string.
- * @return String that the given {@code index} references, or empty string if {@code index} is -1.
- */
- protected String getString(int index) {
- if (index == -1) { // Special case. Packed XML files use -1 for "no string entry"
- return "";
+ protected XmlNodeChunk(ByteBuffer buffer, @Nullable Chunk parent) {
+ super(buffer, parent);
+ lineNumber = buffer.getInt();
+ comment = buffer.getInt();
}
- Chunk parent = getParent();
- while (parent != null) {
- if (parent instanceof XmlChunk) {
- return ((XmlChunk) parent).getString(index);
- }
- parent = parent.getParent();
+
+ /**
+ * Returns true if this XML node contains a comment. Else, returns false.
+ */
+ public boolean hasComment() {
+ return comment != -1;
}
- throw new IllegalStateException("XmlNodeChunk did not have an XmlChunk parent.");
- }
- /**
- * An {@link XmlNodeChunk} and anything that is itself an {@link XmlNodeChunk} has a header size
- * of 16. Anything else is, interestingly, considered to be a payload. For that reason, this
- * method is final.
- */
- @Override
- protected final void writeHeader(ByteBuffer output) {
- super.writeHeader(output);
- output.putInt(lineNumber);
- output.putInt(comment);
- }
+ /**
+ * Returns the line number in the original source at which this node appeared.
+ */
+ public int getLineNumber() {
+ return lineNumber;
+ }
- /**
- * Returns a brief description of this XML node. The representation of this information is
- * subject to change, but below is a typical example:
- *
- * "XmlNodeChunk{line=1234, comment=My awesome comment.}"
- */
- @Override
- public String toString() {
- return String.format("XmlNodeChunk{line=%d, comment=%s}", getLineNumber(), getComment());
- }
+ /**
+ * Returns the comment associated with this node, if any. Else, returns the empty string.
+ */
+ public String getComment() {
+ return getString(comment);
+ }
+
+ /**
+ * An {@link XmlNodeChunk} does not know by itself what strings its indices reference. In order
+ * to get the actual string, the first {@link XmlChunk} ancestor is found. The
+ * {@link XmlChunk} ancestor should have a string pool which {@code index} references.
+ *
+ * @param index The index of the string.
+ * @return String that the given {@code index} references, or empty string if {@code index} is -1.
+ */
+ protected String getString(int index) {
+ if (index == -1) { // Special case. Packed XML files use -1 for "no string entry"
+ return "";
+ }
+ Chunk parent = getParent();
+ while (parent != null) {
+ if (parent instanceof XmlChunk) {
+ return ((XmlChunk) parent).getString(index);
+ }
+ parent = parent.getParent();
+ }
+ throw new IllegalStateException("XmlNodeChunk did not have an XmlChunk parent.");
+ }
+
+ /**
+ * An {@link XmlNodeChunk} and anything that is itself an {@link XmlNodeChunk} has a header size
+ * of 16. Anything else is, interestingly, considered to be a payload. For that reason, this
+ * method is final.
+ */
+ @Override
+ protected final void writeHeader(ByteBuffer output) {
+ super.writeHeader(output);
+ output.putInt(lineNumber);
+ output.putInt(comment);
+ }
+
+ /**
+ * Returns a brief description of this XML node. The representation of this information is
+ * subject to change, but below is a typical example:
+ *
+ * "XmlNodeChunk{line=1234, comment=My awesome comment.}"
+ */
+ @Override
+ public String toString() {
+ return String.format("XmlNodeChunk{line=%d, comment=%s}", getLineNumber(), getComment());
+ }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlResourceMapChunk.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlResourceMapChunk.java
index 85d389f0e..4c81d5c59 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlResourceMapChunk.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlResourceMapChunk.java
@@ -17,6 +17,7 @@
package com.google.devrel.gmscore.tools.apk.arsc;
import androidx.annotation.Nullable;
+
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -31,56 +32,60 @@ import java.util.List;
*/
public class XmlResourceMapChunk extends Chunk {
- /** The size of a resource reference for {@code resources} in bytes. */
- private static final int RESOURCE_SIZE = 4;
+ /**
+ * The size of a resource reference for {@code resources} in bytes.
+ */
+ private static final int RESOURCE_SIZE = 4;
- /**
- * Contains a mapping of attributeID to resourceID. For example, the attributeID 2 refers to the
- * resourceID returned by {@code resources.get(2)}.
- */
- private final List resources = new ArrayList<>();
+ /**
+ * Contains a mapping of attributeID to resourceID. For example, the attributeID 2 refers to the
+ * resourceID returned by {@code resources.get(2)}.
+ */
+ private final List resources = new ArrayList<>();
- protected XmlResourceMapChunk(ByteBuffer buffer, @Nullable Chunk parent) {
- super(buffer, parent);
- }
-
- @Override
- protected void init(ByteBuffer buffer) {
- super.init(buffer);
- resources.addAll(enumerateResources(buffer));
- }
-
- private List enumerateResources(ByteBuffer buffer) {
- int resourceCount = (getOriginalChunkSize() - getHeaderSize()) / RESOURCE_SIZE;
- List result = new ArrayList<>(resourceCount);
- int offset = this.offset + getHeaderSize();
- buffer.mark();
- buffer.position(offset);
-
- for (int i = 0; i < resourceCount; ++i) {
- result.add(buffer.getInt());
+ protected XmlResourceMapChunk(ByteBuffer buffer, @Nullable Chunk parent) {
+ super(buffer, parent);
}
- buffer.reset();
- return result;
- }
+ @Override
+ protected void init(ByteBuffer buffer) {
+ super.init(buffer);
+ resources.addAll(enumerateResources(buffer));
+ }
- /** Returns the resource ID that this {@code attributeId} maps to. */
- public BinaryResourceIdentifier getResourceId(int attributeId) {
- return BinaryResourceIdentifier.create(resources.get(attributeId));
- }
+ private List enumerateResources(ByteBuffer buffer) {
+ int resourceCount = (getOriginalChunkSize() - getHeaderSize()) / RESOURCE_SIZE;
+ List result = new ArrayList<>(resourceCount);
+ int offset = this.offset + getHeaderSize();
+ buffer.mark();
+ buffer.position(offset);
- @Override
- protected Type getType() {
- return Type.XML_RESOURCE_MAP;
- }
+ for (int i = 0; i < resourceCount; ++i) {
+ result.add(buffer.getInt());
+ }
- @Override
- protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
- throws IOException {
- super.writePayload(output, header, shrink);
- for (Integer resource : resources) {
- output.writeInt(resource);
+ buffer.reset();
+ return result;
+ }
+
+ /**
+ * Returns the resource ID that this {@code attributeId} maps to.
+ */
+ public BinaryResourceIdentifier getResourceId(int attributeId) {
+ return BinaryResourceIdentifier.create(resources.get(attributeId));
+ }
+
+ @Override
+ protected Type getType() {
+ return Type.XML_RESOURCE_MAP;
+ }
+
+ @Override
+ protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
+ throws IOException {
+ super.writePayload(output, header, shrink);
+ for (Integer resource : resources) {
+ output.writeInt(resource);
+ }
}
- }
}
diff --git a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlStartElementChunk.java b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlStartElementChunk.java
index 523dcaf1a..d219dec23 100644
--- a/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlStartElementChunk.java
+++ b/app/src/main/java/com/google/devrel/gmscore/tools/apk/arsc/XmlStartElementChunk.java
@@ -16,9 +16,9 @@
package com.google.devrel.gmscore.tools.apk.arsc;
+import androidx.annotation.Nullable;
import com.google.common.base.Preconditions;
-import androidx.annotation.Nullable;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -26,120 +26,144 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-/** Represents the beginning of an XML node. */
+/**
+ * Represents the beginning of an XML node.
+ */
public final class XmlStartElementChunk extends XmlNodeChunk {
- /** A string reference to the namespace URI, or -1 if not present. */
- private final int namespace;
+ /**
+ * A string reference to the namespace URI, or -1 if not present.
+ */
+ private final int namespace;
- /** A string reference to the element name that this chunk represents. */
- private final int name;
+ /**
+ * A string reference to the element name that this chunk represents.
+ */
+ private final int name;
- /** The offset to the start of the attributes payload. */
- private final int attributeStart;
+ /**
+ * The offset to the start of the attributes payload.
+ */
+ private final int attributeStart;
- /** The number of attributes in the original buffer. */
- private final int attributeCount;
+ /**
+ * The number of attributes in the original buffer.
+ */
+ private final int attributeCount;
- /** The (0-based) index of the id attribute, or -1 if not present. */
- private final int idIndex;
+ /**
+ * The (0-based) index of the id attribute, or -1 if not present.
+ */
+ private final int idIndex;
- /** The (0-based) index of the class attribute, or -1 if not present. */
- private final int classIndex;
+ /**
+ * The (0-based) index of the class attribute, or -1 if not present.
+ */
+ private final int classIndex;
- /** The (0-based) index of the style attribute, or -1 if not present. */
- private final int styleIndex;
+ /**
+ * The (0-based) index of the style attribute, or -1 if not present.
+ */
+ private final int styleIndex;
- /** The XML attributes associated with this element. */
- private final List attributes = new ArrayList<>();
+ /**
+ * The XML attributes associated with this element.
+ */
+ private final List attributes = new ArrayList<>();
- protected XmlStartElementChunk(ByteBuffer buffer, @Nullable Chunk parent) {
- super(buffer, parent);
- namespace = buffer.getInt();
- name = buffer.getInt();
- attributeStart = (buffer.getShort() & 0xFFFF);
- int attributeSize = (buffer.getShort() & 0xFFFF);
- Preconditions.checkState(attributeSize == XmlAttribute.SIZE,
- "attributeSize is wrong size. Got %s, want %s", attributeSize, XmlAttribute.SIZE);
- attributeCount = (buffer.getShort() & 0xFFFF);
+ protected XmlStartElementChunk(ByteBuffer buffer, @Nullable Chunk parent) {
+ super(buffer, parent);
+ namespace = buffer.getInt();
+ name = buffer.getInt();
+ attributeStart = (buffer.getShort() & 0xFFFF);
+ int attributeSize = (buffer.getShort() & 0xFFFF);
+ Preconditions.checkState(attributeSize == XmlAttribute.SIZE,
+ "attributeSize is wrong size. Got %s, want %s", attributeSize, XmlAttribute.SIZE);
+ attributeCount = (buffer.getShort() & 0xFFFF);
- // The following indices are 1-based and need to be adjusted.
- idIndex = (buffer.getShort() & 0xFFFF) - 1;
- classIndex = (buffer.getShort() & 0xFFFF) - 1;
- styleIndex = (buffer.getShort() & 0xFFFF) - 1;
- }
-
- @Override
- protected void init(ByteBuffer buffer) {
- super.init(buffer);
- attributes.addAll(enumerateAttributes(buffer));
- }
-
- private List enumerateAttributes(ByteBuffer buffer) {
- List result = new ArrayList<>(attributeCount);
- int offset = this.offset + getHeaderSize() + attributeStart;
- int endOffset = offset + XmlAttribute.SIZE * attributeCount;
- buffer.mark();
- buffer.position(offset);
-
- while (offset < endOffset) {
- result.add(XmlAttribute.create(buffer, this));
- offset += XmlAttribute.SIZE;
+ // The following indices are 1-based and need to be adjusted.
+ idIndex = (buffer.getShort() & 0xFFFF) - 1;
+ classIndex = (buffer.getShort() & 0xFFFF) - 1;
+ styleIndex = (buffer.getShort() & 0xFFFF) - 1;
}
- buffer.reset();
- return result;
- }
-
- /** Returns the namespace URI, or the empty string if not present. */
- public String getNamespace() {
- return getString(namespace);
- }
-
- /** Returns the element name that this chunk represents. */
- public String getName() {
- return getString(name);
- }
-
- /** Returns an unmodifiable list of this XML element's attributes. */
- public List getAttributes() {
- return Collections.unmodifiableList(attributes);
- }
-
- @Override
- protected Type getType() {
- return Type.XML_START_ELEMENT;
- }
-
- @Override
- protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
- throws IOException {
- super.writePayload(output, header, shrink);
- output.writeInt(namespace);
- output.writeInt(name);
- output.writeShort((short) XmlAttribute.SIZE); // attribute start
- output.writeShort((short) XmlAttribute.SIZE);
- output.writeShort((short) attributes.size());
- output.writeShort((short) (idIndex + 1));
- output.writeShort((short) (classIndex + 1));
- output.writeShort((short) (styleIndex + 1));
- for (XmlAttribute attribute : attributes) {
- output.write(attribute.toByteArray(shrink));
+ @Override
+ protected void init(ByteBuffer buffer) {
+ super.init(buffer);
+ attributes.addAll(enumerateAttributes(buffer));
}
- }
- /**
- * Returns a brief description of this XML node. The representation of this information is
- * subject to change, but below is a typical example:
- *
- *
- * "XmlStartElementChunk{line=1234, comment=My awesome comment., namespace=foo, name=bar, ...}"
- *
- */
- @Override
- public String toString() {
- return String.format(
- "XmlStartElementChunk{line=%d, comment=%s, namespace=%s, name=%s, attributes=%s}",
- getLineNumber(), getComment(), getNamespace(), getName(), attributes.toString());
- }
+ private List enumerateAttributes(ByteBuffer buffer) {
+ List result = new ArrayList<>(attributeCount);
+ int offset = this.offset + getHeaderSize() + attributeStart;
+ int endOffset = offset + XmlAttribute.SIZE * attributeCount;
+ buffer.mark();
+ buffer.position(offset);
+
+ while (offset < endOffset) {
+ result.add(XmlAttribute.create(buffer, this));
+ offset += XmlAttribute.SIZE;
+ }
+
+ buffer.reset();
+ return result;
+ }
+
+ /**
+ * Returns the namespace URI, or the empty string if not present.
+ */
+ public String getNamespace() {
+ return getString(namespace);
+ }
+
+ /**
+ * Returns the element name that this chunk represents.
+ */
+ public String getName() {
+ return getString(name);
+ }
+
+ /**
+ * Returns an unmodifiable list of this XML element's attributes.
+ */
+ public List getAttributes() {
+ return Collections.unmodifiableList(attributes);
+ }
+
+ @Override
+ protected Type getType() {
+ return Type.XML_START_ELEMENT;
+ }
+
+ @Override
+ protected void writePayload(DataOutput output, ByteBuffer header, boolean shrink)
+ throws IOException {
+ super.writePayload(output, header, shrink);
+ output.writeInt(namespace);
+ output.writeInt(name);
+ output.writeShort((short) XmlAttribute.SIZE); // attribute start
+ output.writeShort((short) XmlAttribute.SIZE);
+ output.writeShort((short) attributes.size());
+ output.writeShort((short) (idIndex + 1));
+ output.writeShort((short) (classIndex + 1));
+ output.writeShort((short) (styleIndex + 1));
+ for (XmlAttribute attribute : attributes) {
+ output.write(attribute.toByteArray(shrink));
+ }
+ }
+
+ /**
+ * Returns a brief description of this XML node. The representation of this information is
+ * subject to change, but below is a typical example:
+ *
+ *
+ * "XmlStartElementChunk{line=1234, comment=My awesome comment., namespace=foo, name=bar, ...}"
+ *
+ */
+ @Override
+ public String toString() {
+ return String.format(
+ "XmlStartElementChunk{line=%d, comment=%s, namespace=%s, name=%s, attributes=%s}",
+ getLineNumber(), getComment(), getNamespace(), getName(), attributes.toString());
+ }
}
diff --git a/app/src/main/java/com/vk/api/audio/AudioGetLyrics.java b/app/src/main/java/com/vk/api/audio/AudioGetLyrics.java
index a023f48e1..6e2df114e 100644
--- a/app/src/main/java/com/vk/api/audio/AudioGetLyrics.java
+++ b/app/src/main/java/com/vk/api/audio/AudioGetLyrics.java
@@ -1,21 +1,17 @@
package com.vk.api.audio;
import android.text.TextUtils;
-
import androidx.annotation.NonNull;
-
import com.vk.api.base.ApiRequest;
import com.vk.dto.music.MusicTrack;
import com.vk.log.L;
-
import org.json.JSONObject;
-
-import java.util.ArrayList;
-
import ru.vtosters.lite.music.Genius;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.Preferences;
+import java.util.ArrayList;
+
public class AudioGetLyrics extends ApiRequest {
private final MusicTrack musicTrack;
diff --git a/app/src/main/java/com/vk/api/gifts/GiftGetByStickerId.java b/app/src/main/java/com/vk/api/gifts/GiftGetByStickerId.java
index 0111560cf..dfa535f88 100644
--- a/app/src/main/java/com/vk/api/gifts/GiftGetByStickerId.java
+++ b/app/src/main/java/com/vk/api/gifts/GiftGetByStickerId.java
@@ -1,10 +1,8 @@
package com.vk.api.gifts;
import android.content.Context;
-
import com.vk.api.base.ApiRequest;
import com.vk.dto.gift.CatalogedGift;
-
import org.json.JSONException;
import org.json.JSONObject;
@@ -29,7 +27,7 @@ public class GiftGetByStickerId extends ApiRequest {
@Override // com.vk.api.sdk.o.VKRequest
public GiftGetByStickerId.a a(JSONObject jSONObject) throws JSONException {
JSONObject jSONObject2 = jSONObject.getJSONObject("response");
- return new a(jSONObject2.optInt("balance"),
+ return new a(jSONObject2.optInt("balance"),
new CatalogedGift(jSONObject2.getJSONObject("gift")));
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/vk/core/util/DownloadUtils.java b/app/src/main/java/com/vk/core/util/DownloadUtils.java
index 23ba63975..8ddceb31e 100644
--- a/app/src/main/java/com/vk/core/util/DownloadUtils.java
+++ b/app/src/main/java/com/vk/core/util/DownloadUtils.java
@@ -11,13 +11,15 @@ import android.net.Uri;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
-
+import b.h.g.m.CameraUtils;
+import b.h.g.m.FileUtils;
import com.facebook.common.i.MediaUtils;
import com.facebook.x.g.EncodedImage;
import com.vk.imageloader.VKImageLoader;
import com.vk.log.L;
import com.vk.metrics.eventtracking.VkTracker;
import com.vtosters.lite.R;
+import ru.vtosters.lite.concurrent.VTExecutors;
import java.io.File;
import java.io.FileOutputStream;
@@ -26,10 +28,6 @@ import java.io.InputStream;
import java.net.URI;
import java.util.HashSet;
-import b.h.g.m.CameraUtils;
-import b.h.g.m.FileUtils;
-import ru.vtosters.lite.concurrent.VTExecutors;
-
public class DownloadUtils {
public static final HashSet a = new HashSet<>();
@@ -49,7 +47,7 @@ public class DownloadUtils {
ToastUtils.a(context.getString(R.string.error) + " [" + e2.getMessage() + "]");
}
}
-
+
private static void b(final Context context, String str, String uri, boolean z) {
if (TextUtils.isEmpty(uri)) {
return;
diff --git a/app/src/main/java/com/vk/im/engine/commands/dialogs/DialogGetMentionSuggestionCmd.java b/app/src/main/java/com/vk/im/engine/commands/dialogs/DialogGetMentionSuggestionCmd.java
index 251e4a757..644415d33 100644
--- a/app/src/main/java/com/vk/im/engine/commands/dialogs/DialogGetMentionSuggestionCmd.java
+++ b/app/src/main/java/com/vk/im/engine/commands/dialogs/DialogGetMentionSuggestionCmd.java
@@ -186,8 +186,7 @@ public final class DialogGetMentionSuggestionCmd extends BaseImEngineCmd this.D == this.b.c.getLocalId();
case VK_ID -> this.D == this.b.c.C1();
diff --git a/app/src/main/java/com/vk/im/ui/components/viewcontrollers/msg_list/adapter/msgparts/MsgPartTextHolder.java b/app/src/main/java/com/vk/im/ui/components/viewcontrollers/msg_list/adapter/msgparts/MsgPartTextHolder.java
index 6cda32bab..23d3e3bb4 100644
--- a/app/src/main/java/com/vk/im/ui/components/viewcontrollers/msg_list/adapter/msgparts/MsgPartTextHolder.java
+++ b/app/src/main/java/com/vk/im/ui/components/viewcontrollers/msg_list/adapter/msgparts/MsgPartTextHolder.java
@@ -23,7 +23,7 @@ public class MsgPartTextHolder extends MsgPartHolderBase {
MsgPartTextHolder.this.f.a(MsgPartTextHolder.this.g.getLocalId());
});
mTextPart.setOnLongClickListener(view -> {
- if(!mTextPart.isTextSelectable()) MsgPartTextHolder.this.f.b(MsgPartTextHolder.this.g.getLocalId());
+ if (!mTextPart.isTextSelectable()) MsgPartTextHolder.this.f.b(MsgPartTextHolder.this.g.getLocalId());
return false;
});
diff --git a/app/src/main/java/com/vk/im/ui/components/viewcontrollers/msg_list/adapter/vh/VhMsg.java b/app/src/main/java/com/vk/im/ui/components/viewcontrollers/msg_list/adapter/vh/VhMsg.java
index f372dd668..808a9d02d 100644
--- a/app/src/main/java/com/vk/im/ui/components/viewcontrollers/msg_list/adapter/vh/VhMsg.java
+++ b/app/src/main/java/com/vk/im/ui/components/viewcontrollers/msg_list/adapter/vh/VhMsg.java
@@ -28,7 +28,6 @@ import com.vk.im.engine.models.Profile;
import com.vk.im.engine.models.dialogs.BubbleColors;
import com.vk.im.engine.models.dialogs.Dialog;
import com.vk.im.engine.models.messages.Msg;
-import com.vk.im.engine.models.messages.MsgSyncState;
import com.vk.im.engine.utils.MsgPermissionHelper;
import com.vk.im.ui.components.viewcontrollers.msg_list.BombView;
import com.vk.im.ui.components.viewcontrollers.msg_list.SwipeToReplyItemTouchCallback;
@@ -144,7 +143,7 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
this.X = context.getString(m.vkim_accessibility_msg_unread);
MsgBubbleView var5 = this.e;
var5.setContentView(this.B.a(var4, var5));
- ViewGroup.MarginLayoutParams var7 = (ViewGroup.MarginLayoutParams)this.e.getLayoutParams();
+ ViewGroup.MarginLayoutParams var7 = (ViewGroup.MarginLayoutParams) this.e.getLayoutParams();
Rect var6 = this.G;
var6.left = var7.leftMargin;
var6.right = var7.rightMargin;
@@ -337,7 +336,8 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
if (args.w()) {
var12 = var11;
if (args.k()) {
- label30: {
+ label30:
+ {
if (args.G()) {
var12 = Screen.a(4);
} else {
@@ -671,7 +671,7 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
private void f(VhBindArgs args) {
Msg var2 = args.b.c;
if (var2 != null) {
- this.P.setStateListener((BombView.d)null);
+ this.P.setStateListener((BombView.d) null);
if (var2.M1() && !var2.E1()) {
this.P.setVisibility(View.GONE);
} else {
@@ -832,7 +832,8 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
MsgBubbleView var8 = this.e;
boolean var6 = var4;
if (var5) {
- label57: {
+ label57:
+ {
if (!args.C()) {
var6 = var4;
if (!args.z()) {
@@ -877,7 +878,7 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
if (args.s()) {
this.itemView.setBackground(this.H);
} else {
- this.itemView.setBackground((Drawable)null);
+ this.itemView.setBackground((Drawable) null);
}
}
@@ -985,7 +986,7 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
public void a(@NonNull Msg var1, int var2) {
if (this.f0()) {
- ((MsgPartCarouselHolder)this.B).a(var1, var2);
+ ((MsgPartCarouselHolder) this.B).a(var1, var2);
}
}
@@ -1110,7 +1111,7 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
public Unit invoke() {
if (VhMsg.this.e.getMeasuredWidth() < VhMsg.h0) {
- ((ViewGroup.MarginLayoutParams)VhMsg.this.e.getLayoutParams()).leftMargin = VhMsg.h0 - VhMsg.this.e.getMeasuredWidth() + this.a;
+ ((ViewGroup.MarginLayoutParams) VhMsg.this.e.getLayoutParams()).leftMargin = VhMsg.h0 - VhMsg.this.e.getMeasuredWidth() + this.a;
VhMsg.this.e.invalidate();
VhMsg.this.e.requestLayout();
} else {
diff --git a/app/src/main/java/com/vk/im/ui/q/h/e/MentionsController.java b/app/src/main/java/com/vk/im/ui/q/h/e/MentionsController.java
index 4ef6a7831..007fadc4c 100644
--- a/app/src/main/java/com/vk/im/ui/q/h/e/MentionsController.java
+++ b/app/src/main/java/com/vk/im/ui/q/h/e/MentionsController.java
@@ -6,7 +6,6 @@ import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
-import android.widget.TextView;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.recyclerview.widget.RecyclerView;
import com.vk.core.ui.VkBottomSheetBehavior;
@@ -55,7 +54,7 @@ public final class MentionsController {
@Override
public boolean a() {
- if(MentionsController.this.state)
+ if (MentionsController.this.state)
return ViewExtKt.i(MentionsController.this.mCoordinatorLayout);
return false;
}
@@ -105,7 +104,7 @@ public final class MentionsController {
View inflate = ((ViewStub) this.j.findViewById(R.id.mentions_container_stub)).inflate();
ViewGroup viewGroup = (ViewGroup) inflate.findViewById(R.id.mentions_container);
viewGroup.addView(this.mDialogMentionComponent.a(viewGroup, (Bundle) null));
- this.mCoordinatorLayout = inflate.findViewById(R.id.mentions_cl);;
+ this.mCoordinatorLayout = inflate.findViewById(R.id.mentions_cl);
this.mBottomSheetBehavior = (VkBottomSheetBehavior) ((CoordinatorLayout.LayoutParams) viewGroup.getLayoutParams()).getBehavior();
this.mBottomSheetBehavior.a(new VkBottomSheetBehavior.b() {
@Override
diff --git a/app/src/main/java/com/vk/im/ui/views/Corners.java b/app/src/main/java/com/vk/im/ui/views/Corners.java
index 6d09b5010..d6faee429 100644
--- a/app/src/main/java/com/vk/im/ui/views/Corners.java
+++ b/app/src/main/java/com/vk/im/ui/views/Corners.java
@@ -2,13 +2,13 @@ package com.vk.im.ui.views;
import androidx.annotation.NonNull;
-public class Corners{
+public class Corners {
private int topLeft;
private int topRight;
private int bottomLeft;
private int bottomRight;
- public Corners(int i, int i2, int i3, int i4){
+ public Corners(int i, int i2, int i3, int i4) {
this.topLeft = i;
this.topRight = i2;
this.bottomLeft = i3;
@@ -23,7 +23,7 @@ public class Corners{
this(0, 0, 0, 0, 15);
}
- public static Corners a(Corners corners, int i, int i2, int i3, int i4, int i5, Object obj){
+ public static Corners a(Corners corners, int i, int i2, int i3, int i4, int i5, Object obj) {
if ((i5 & 1) != 0) {
i = corners.topLeft;
}
@@ -40,30 +40,30 @@ public class Corners{
return corners;
}
- public int a(){
+ public int a() {
return this.bottomLeft;
}
- public int b(){
+ public int b() {
return this.bottomRight;
}
- public int c(){
+ public int c() {
return this.topLeft;
}
- public int d(){
+ public int d() {
return this.topRight;
}
- public boolean e(){
+ public boolean e() {
int i;
int i2 = this.topLeft;
int i3 = this.topRight;
return i2 == i3 && i3 == (i = this.bottomRight) && this.bottomLeft == i;
}
- public boolean equals(Object obj){
+ public boolean equals(Object obj) {
if (this != obj) {
if (!(obj instanceof Corners)) {
return false;
@@ -74,24 +74,24 @@ public class Corners{
return true;
}
- public boolean f(){
+ public boolean f() {
return this.topLeft == 0 && e();
}
- public void g(){
+ public void g() {
a(0, 0);
}
- public int hashCode(){
+ public int hashCode() {
return (((((this.topLeft * 31) + this.topRight) * 31) + this.bottomLeft) * 31) + this.bottomRight;
}
@NonNull
- public String toString(){
+ public String toString() {
return "Corners(topLeft=" + this.topLeft + ", topRight=" + this.topRight + ", bottomLeft=" + this.bottomLeft + ", bottomRight=" + this.bottomRight + ")";
}
- public Corners a(int i, int i2){
+ public Corners a(int i, int i2) {
if ((i2 & 1) > 0) {
this.topLeft = i;
}
@@ -113,7 +113,7 @@ public class Corners{
return this;
}
- public Corners a(int i, int i2, int i3, int i4){
+ public Corners a(int i, int i2, int i3, int i4) {
this.topLeft = i;
this.topRight = i2;
this.bottomLeft = i3;
@@ -121,7 +121,7 @@ public class Corners{
return this;
}
- public Corners a(Corners corners){
+ public Corners a(Corners corners) {
this.topLeft = corners.topLeft;
this.topRight = corners.topRight;
this.bottomLeft = corners.bottomLeft;
diff --git a/app/src/main/java/com/vk/im/ui/views/avatars/StoryBorderView.java b/app/src/main/java/com/vk/im/ui/views/avatars/StoryBorderView.java
index 5ada59705..27ceffa88 100644
--- a/app/src/main/java/com/vk/im/ui/views/avatars/StoryBorderView.java
+++ b/app/src/main/java/com/vk/im/ui/views/avatars/StoryBorderView.java
@@ -9,7 +9,6 @@ import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
-
import com.vk.im.ui.o;
public class StoryBorderView extends View {
diff --git a/app/src/main/java/com/vk/im/ui/views/dialogs/DialogItemView.java b/app/src/main/java/com/vk/im/ui/views/dialogs/DialogItemView.java
index fcea16294..3b5c2ec7c 100644
--- a/app/src/main/java/com/vk/im/ui/views/dialogs/DialogItemView.java
+++ b/app/src/main/java/com/vk/im/ui/views/dialogs/DialogItemView.java
@@ -1,8 +1,5 @@
package com.vk.im.ui.views.dialogs;
-import static com.vk.im.ui.d.vkim_administration_title;
-import static ru.vtosters.lite.utils.ThemesUtils.getAccentColor;
-
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -17,10 +14,9 @@ import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.widget.ImageView;
import android.widget.TextView;
-
import androidx.annotation.ColorInt;
import androidx.appcompat.widget.AppCompatImageView;
-
+import b.h.g.r.MathExt;
import com.vk.core.extensions.AnimationExtKt;
import com.vk.core.util.ContextExtKt;
import com.vk.core.util.Screen;
@@ -42,9 +38,10 @@ import com.vk.im.ui.views.FixTextView;
import com.vk.im.ui.views.avatars.AvatarView;
import com.vk.im.ui.views.avatars.StoryBorderView;
-import b.h.g.r.MathExt;
+import static com.vk.im.ui.d.vkim_administration_title;
+import static ru.vtosters.lite.utils.ThemesUtils.getAccentColor;
-public class DialogItemView extends ViewGroup{
+public class DialogItemView extends ViewGroup {
private final int B;
private final int C;
private final int D;
@@ -80,15 +77,15 @@ public class DialogItemView extends ViewGroup{
private boolean g0;
private boolean h0;
- public DialogItemView(Context context){
+ public DialogItemView(Context context) {
this(context, null, 0, 6);
}
- public DialogItemView(Context context, AttributeSet attributeSet){
+ public DialogItemView(Context context, AttributeSet attributeSet) {
this(context, attributeSet, 0, 4);
}
- public DialogItemView(Context context, AttributeSet attributeSet, int i){
+ public DialogItemView(Context context, AttributeSet attributeSet, int i) {
super(context, attributeSet, i);
Resources resources = getResources();
this.a = resources.getDisplayMetrics();
@@ -255,11 +252,11 @@ public class DialogItemView extends ViewGroup{
addView(this.f0);
}
- public DialogItemView(Context context, AttributeSet attributeSet, int i, int i2){
+ public DialogItemView(Context context, AttributeSet attributeSet, int i, int i2) {
this(context, (i2 & 2) != 0 ? null : attributeSet, (i2 & 4) != 0 ? 0 : i);
}
- public static ViewPropertyAnimator b(View view, float f, long j, long j2, Runnable runnable, int i, Object obj){
+ public static ViewPropertyAnimator b(View view, float f, long j, long j2, Runnable runnable, int i, Object obj) {
if ((i & 1) != 0) {
f = 0.0f;
}
@@ -279,7 +276,7 @@ public class DialogItemView extends ViewGroup{
return AnimationExtKt.b(view, f, j, j2, runnable);
}
- public static void a(View view, float f, float f2, int i, Object obj){
+ public static void a(View view, float f, float f2, int i, Object obj) {
if ((i & 1) != 0) {
f = 1.0f;
}
@@ -291,7 +288,7 @@ public class DialogItemView extends ViewGroup{
AnimationExtKt.a(view, f, f2);
}
- public static ViewPropertyAnimator a(View view, float f, long j, long j2, Runnable runnable, int i, Object obj){
+ public static ViewPropertyAnimator a(View view, float f, long j, long j2, Runnable runnable, int i, Object obj) {
if ((i & 1) != 0) {
f = 1.0f;
}
@@ -311,14 +308,14 @@ public class DialogItemView extends ViewGroup{
return AnimationExtKt.a(view, f, j, j2, runnable);
}
- static void a(DialogItemView dialogItemView, int i, int i2, int i3, int i4, Object obj){
+ static void a(DialogItemView dialogItemView, int i, int i2, int i3, int i4, Object obj) {
if ((i4 & 4) != 0) {
i3 = 0;
}
dialogItemView.a(i, i2, i3);
}
- static ViewGroup.MarginLayoutParams a(DialogItemView dialogItemView, int i, int i2, int i3, int i4, int i5, int i6, int i7, Object obj){
+ static ViewGroup.MarginLayoutParams a(DialogItemView dialogItemView, int i, int i2, int i3, int i4, int i5, int i6, int i7, Object obj) {
if ((i7 & 1) != 0) {
i = -2;
}
@@ -340,19 +337,19 @@ public class DialogItemView extends ViewGroup{
return dialogItemView.a(i, i2, i3, i4, i5, i6);
}
- private Drawable getDrOnlineMobile(){
+ private Drawable getDrOnlineMobile() {
return ContextExtKt.c(getContext(), f.ic_online_mobile_vkapp_composite_16);
}
- private Drawable getDrOnlineVkMe(){
+ private Drawable getDrOnlineVkMe() {
return ContextExtKt.c(getContext(), f.ic_online_mobile_vkme_composite_16);
}
- private Drawable getDrOnlineWeb(){
+ private Drawable getDrOnlineWeb() {
return ContextExtKt.c(getContext(), f.ic_online_web_composite_16);
}
- private int getTimeMargin(){
+ private int getTimeMargin() {
Layout layout = this.T.getLayout();
if (layout == null) {
@@ -374,11 +371,11 @@ public class DialogItemView extends ViewGroup{
return 0;
}
- private int getTitleColorHighlight(){
+ private int getTitleColorHighlight() {
return ContextExtKt.a(getContext(), vkim_administration_title);
}
- private void h(int i, int i2, int i3, int i4){
+ private void h(int i, int i2, int i3, int i4) {
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
int measuredWidth = getMeasuredWidth() - getPaddingRight();
@@ -412,51 +409,51 @@ public class DialogItemView extends ViewGroup{
j(measuredWidth - r, max6, measuredWidth, q + max6);
}
- private int i(){
+ private int i() {
return g(this.K);
}
- private int j(){
+ private int j() {
return f(this.T) + Math.max(f(this.V), f(this.U));
}
- private int k(){
+ private int k() {
return Math.max(g(this.T), g(this.V) + g(this.U));
}
- private int l(){
+ private int l() {
return Math.max(Math.max(f(this.N), f(this.R)), Math.max(f(this.O), f(this.P)));
}
- private int m(){
+ private int m() {
return View.MeasureSpec.makeMeasureSpec(0, 0);
}
- private boolean n(){
+ private boolean n() {
return e(this.S);
}
- private int o(){
+ private int o() {
return f(this.S);
}
- private int p(){
+ private int p() {
return g(this.S);
}
- private int q(){
+ private int q() {
return MathExt.a(f(this.b0), f(this.c0), f(this.d0), f(this.e0), f(this.f0));
}
- private int r(){
+ private int r() {
return g(this.b0) + MathExt.a(g(this.c0), g(this.d0), g(this.e0), g(this.f0));
}
- private int s(){
+ private int s() {
return Math.max(f(this.a0), f(this.W));
}
- private void setAttach(CharSequence charSequence){
+ private void setAttach(CharSequence charSequence) {
int i = VISIBLE;
if (charSequence == null || charSequence.length() == 0) {
@@ -467,57 +464,57 @@ public class DialogItemView extends ViewGroup{
this.U.setText(charSequence);
}
- private void setBodyLinesCount(int i){
+ private void setBodyLinesCount(int i) {
boolean z = i == 1;
this.T.setSingleLine(z);
this.T.setMaxLines(i);
}
- private int t(){
+ private int t() {
return g(this.a0) + g(this.W);
}
- private void u(){
+ private void u() {
ViewExtKt.b(this.b0, this.g0 | this.h0);
}
- public void a(Dialog dialog, ProfilesSimpleInfo profilesSimpleInfo){
+ public void a(Dialog dialog, ProfilesSimpleInfo profilesSimpleInfo) {
this.K.a(dialog, profilesSimpleInfo);
}
- public void b(){
+ public void b() {
a(null, (ComposingType) null);
}
- public void c(){
+ public void c() {
a(getDrOnlineMobile());
}
- public void d(){
+ public void d() {
a(getDrOnlineVkMe());
}
- public void e(){
+ public void e() {
a(getDrOnlineWeb());
}
- public void f(){
+ public void f() {
this.S.setVisibility(GONE);
this.S.e();
}
- public void g(){
+ public void g() {
b(this.L, 0.0f, 0L, 0L, null, 15, null);
}
@Override // android.view.ViewGroup, android.view.View
- protected void onDetachedFromWindow(){
+ protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
a(this.L, 0.0f, 0.0f, 3, null);
}
@Override // android.view.ViewGroup, android.view.View
- protected void onLayout(boolean z, int i, int i2, int i3, int i4){
+ protected void onLayout(boolean z, int i, int i2, int i3, int i4) {
if (this.E) {
g(i, i2, i3, i4);
} else {
@@ -526,7 +523,7 @@ public class DialogItemView extends ViewGroup{
}
@Override // android.view.View
- protected void onMeasure(int i, int i2){
+ protected void onMeasure(int i, int i2) {
if (this.E) {
c(i);
} else {
@@ -534,11 +531,11 @@ public class DialogItemView extends ViewGroup{
}
}
- public void setAvatar(Profile profile){
+ public void setAvatar(Profile profile) {
this.K.a(profile);
}
- public void setBombVisible(boolean z){
+ public void setBombVisible(boolean z) {
if (z) {
this.b0.setImageResource(f.ic_bomb_composite_24);
}
@@ -548,23 +545,23 @@ public class DialogItemView extends ViewGroup{
u();
}
- public void setCasperIconColor(@ColorInt int i){
+ public void setCasperIconColor(@ColorInt int i) {
ViewExtKt.a(this.Q, i);
}
- public void setCasperIconVisible(boolean z){
+ public void setCasperIconVisible(boolean z) {
this.Q.setVisibility(z ? VISIBLE : GONE);
}
- public void setErrorVisible(boolean z){
+ public void setErrorVisible(boolean z) {
this.f0.setVisibility(z ? VISIBLE : GONE);
}
- public void setGiftVisible(boolean z){
+ public void setGiftVisible(boolean z) {
this.V.setVisibility(z ? VISIBLE : GONE);
}
- public void setHasStories(boolean z){
+ public void setHasStories(boolean z) {
if (this.I == z) {
return;
}
@@ -590,7 +587,7 @@ public class DialogItemView extends ViewGroup{
setClipToPadding(!z);
}
- public void setMentionVisible(boolean z){
+ public void setMentionVisible(boolean z) {
if (z) {
this.b0.setImageResource(f.ic_mention_composite_24);
}
@@ -600,15 +597,15 @@ public class DialogItemView extends ViewGroup{
u();
}
- public void setMutedVisible(boolean z){
+ public void setMutedVisible(boolean z) {
this.P.setVisibility(z ? VISIBLE : GONE);
}
- public void setOnStoryClickListener(View.OnClickListener onClickListener){
+ public void setOnStoryClickListener(View.OnClickListener onClickListener) {
ViewExtKt.b(this.M, onClickListener);
}
- public void setSender(Profile profile){
+ public void setSender(Profile profile) {
if (profile == null) {
f();
return;
@@ -617,29 +614,29 @@ public class DialogItemView extends ViewGroup{
this.S.a(profile);
}
- public void setSendingVisible(boolean z){
+ public void setSendingVisible(boolean z) {
this.e0.setVisibility(z ? VISIBLE : GONE);
}
- public void setTime(CharSequence charSequence){
+ public void setTime(CharSequence charSequence) {
this.R.setText(charSequence);
}
- public void setUnreadOutVisible(boolean z){
+ public void setUnreadOutVisible(boolean z) {
this.d0.setVisibility(z ? VISIBLE : GONE);
}
- public void setVerifiedVisible(boolean z){
+ public void setVerifiedVisible(boolean z) {
this.O.setVisibility(z ? VISIBLE : GONE);
}
- private void a(Drawable drawable){
+ private void a(Drawable drawable) {
ViewExtKt.c(this.L, a(16), a(20));
this.L.setImageDrawable(drawable);
a(this.L, 0.0f, 0L, 0L, null, 15, null);
}
- private void c(int i){
+ private void c(int i) {
int size = (View.MeasureSpec.getSize(i) - getPaddingLeft()) - getPaddingRight();
d(b(size - this.K.getViewSize()), m());
int r = r();
@@ -655,7 +652,7 @@ public class DialogItemView extends ViewGroup{
setMeasuredDimension(View.MeasureSpec.getSize(i), getPaddingTop() + getPaddingBottom() + Math.max(f(this.K), l() + Math.max(Math.max(o(), j()), Math.max(s(), q()))));
}
- private void d(int i){
+ private void d(int i) {
int size = (View.MeasureSpec.getSize(i) - getPaddingLeft()) - getPaddingRight();
d(b(size - this.K.getViewSize()), m());
int r = r();
@@ -672,7 +669,7 @@ public class DialogItemView extends ViewGroup{
setMeasuredDimension(View.MeasureSpec.getSize(i), getPaddingTop() + getPaddingBottom() + Math.max(f(this.K), l() + Math.max(Math.max(o(), j()), Math.max(s(), q()))));
}
- private void e(int i, int i2, int i3, int i4){
+ private void e(int i, int i2, int i3, int i4) {
if (e(this.T) && e(this.U)) {
c(i, i2, i3, i4);
} else if (e(this.T)) {
@@ -683,7 +680,7 @@ public class DialogItemView extends ViewGroup{
}
}
- private void g(int i, int i2, int i3, int i4){
+ private void g(int i, int i2, int i3, int i4) {
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
int measuredWidth = getMeasuredWidth() - getPaddingRight();
@@ -706,12 +703,12 @@ public class DialogItemView extends ViewGroup{
j(measuredWidth - r, i8, measuredWidth, q + i8);
}
- private void i(int i, int i2, int i3, int i4){
+ private void i(int i, int i2, int i3, int i4) {
AvatarView avatarView = this.S;
b(avatarView, i + b(avatarView), ((i2 + ((i4 - i2) / 2)) - (this.S.getMeasuredHeight() / 2)) + d(this.S));
}
- private void j(int i, int i2, int i3, int i4){
+ private void j(int i, int i2, int i3, int i4) {
int i5 = i2 + ((i4 - i2) / 2);
AppCompatImageView appCompatImageView = this.b0;
b(appCompatImageView, b(appCompatImageView) + i, (i5 - (this.b0.getMeasuredHeight() / 2)) + d(this.b0));
@@ -728,7 +725,7 @@ public class DialogItemView extends ViewGroup{
b(appCompatImageView4, i + b(appCompatImageView4), (i5 - (this.f0.getMeasuredHeight() / 2)) + d(this.f0));
}
- private void k(int i, int i2, int i3, int i4){
+ private void k(int i, int i2, int i3, int i4) {
int i5 = i2 + ((i4 - i2) / 2);
TextView textView = this.W;
b(textView, b(textView) + i, (i5 - (this.W.getMeasuredHeight() / 2)) + d(this.W));
@@ -741,7 +738,7 @@ public class DialogItemView extends ViewGroup{
b(appCompatImageView, i + b(appCompatImageView), (i5 - (this.a0.getMeasuredHeight() / 2)) + d(this.a0));
}
- public void b(CharSequence charSequence, boolean z){
+ public void b(CharSequence charSequence, boolean z) {
int i = VISIBLE;
if (charSequence == null || charSequence.length() == 0) {
@@ -755,7 +752,7 @@ public class DialogItemView extends ViewGroup{
this.g.a(z ? this.B : this.h);
}
- private void f(int i, int i2, int i3, int i4){
+ private void f(int i, int i2, int i3, int i4) {
TextView textView = this.N;
b(textView, i + b(textView), d(this.N) + i2);
int right = this.N.getRight() + c(this.N);
@@ -783,7 +780,7 @@ public class DialogItemView extends ViewGroup{
}
}
- private void l(int i, int i2, int i3, int i4){
+ private void l(int i, int i2, int i3, int i4) {
TextView textView = this.W;
b(textView, i + b(textView), i2);
int right = this.W.getRight() + c(this.W);
@@ -791,33 +788,33 @@ public class DialogItemView extends ViewGroup{
b(appCompatImageView, right + b(appCompatImageView), (((this.W.getBottom() + this.W.getTop()) / 2) - (this.a0.getMeasuredHeight() / 2)) + d(this.a0));
}
- private void e(int i, int i2){
+ private void e(int i, int i2) {
b(this.a0, i, 0, i2, 0);
b(this.W, i, g(this.a0), i2, 0);
}
- public void a(CharSequence charSequence, boolean z){
+ public void a(CharSequence charSequence, boolean z) {
this.N.setText(charSequence);
this.N.setTextColor(z ? getTitleColorHighlight() : this.f15761e);
}
- private void b(int i, int i2){
+ private void b(int i, int i2) {
b(this.O, i, 0, i2, 0);
b(this.Q, i, 0, i2, 0);
b(this.P, i, 0, i2, 0);
b(this.R, i, 0, i2, 0);
}
- public void a(){
+ public void a() {
a(null, 1);
setAttach(null);
}
- private boolean e(View view){
+ private boolean e(View view) {
return view.getVisibility() == VISIBLE;
}
- public void a(CharSequence charSequence, int i, CharSequence charSequence2){
+ public void a(CharSequence charSequence, int i, CharSequence charSequence2) {
boolean z = false;
if (!(charSequence == null || charSequence.length() == 0)) {
@@ -840,11 +837,11 @@ public class DialogItemView extends ViewGroup{
}
}
- private void b(int i, int i2, int i3){
+ private void b(int i, int i2, int i3) {
a(this.N, i, i3, i2, 0);
}
- private void b(int i, int i2, int i3, int i4){
+ private void b(int i, int i2, int i3, int i4) {
int g = e(this.V) ? g(this.V) + i : i;
TextView textView = this.U;
a(textView, g + b(textView), i2 + d(this.U));
@@ -852,7 +849,7 @@ public class DialogItemView extends ViewGroup{
b(appCompatImageView, i + b(appCompatImageView), (this.U.getTop() + (this.U.getMeasuredHeight() / 2)) - (this.V.getMeasuredHeight() / 2));
}
- private void a(CharSequence charSequence, int i){
+ private void a(CharSequence charSequence, int i) {
int i2 = VISIBLE;
if (charSequence == null || charSequence.length() == 0) {
@@ -864,13 +861,13 @@ public class DialogItemView extends ViewGroup{
setBodyLinesCount(i);
}
- private void b(View view, int i, int i2, int i3, int i4){
+ private void b(View view, int i, int i2, int i3, int i4) {
if (view.getVisibility() != GONE) {
a(view, i, i2, i3, i4);
}
}
- public void a(CharSequence charSequence, ComposingType composingType){
+ public void a(CharSequence charSequence, ComposingType composingType) {
Drawable drawable = composingType == ComposingType.AUDIO ? this.H : this.G;
boolean z = false;
drawable.setVisible(!(charSequence == null || charSequence.length() == 0), false);
@@ -885,13 +882,13 @@ public class DialogItemView extends ViewGroup{
this.W.setText(charSequence);
}
- private void b(View view, int i, int i2){
+ private void b(View view, int i, int i2) {
if (view.getVisibility() == VISIBLE) {
a(view, i, i2);
}
}
- private int b(View view){
+ private int b(View view) {
ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
if (marginLayoutParams != null) {
@@ -901,11 +898,11 @@ public class DialogItemView extends ViewGroup{
return 0;
}
- private int b(int i){
+ private int b(int i) {
return View.MeasureSpec.makeMeasureSpec(i, MeasureSpec.AT_MOST);
}
- private int g(View view){
+ private int g(View view) {
if (view.getVisibility() == GONE) {
return 0;
}
@@ -915,13 +912,13 @@ public class DialogItemView extends ViewGroup{
return marginLayoutParams != null ? measuredWidth + marginLayoutParams.leftMargin + marginLayoutParams.rightMargin : measuredWidth;
}
- private void a(int i, int i2){
+ private void a(int i, int i2) {
a(this.K, i, 0, i2, 0);
b(this.M, i, 0, i2, 0);
b(this.L, i, 0, i2, 0);
}
- private int f(View view){
+ private int f(View view) {
if (view.getVisibility() == GONE) {
return 0;
}
@@ -934,32 +931,32 @@ public class DialogItemView extends ViewGroup{
return measuredHeight;
}
- private void a(int i, int i2, int i3, int i4){
+ private void a(int i, int i2, int i3, int i4) {
AvatarView avatarView = this.K;
a(avatarView, i + b(avatarView), i2 + d(this.K));
a(this.M, this.K.getLeft() - this.K.getPaddingLeft(), this.K.getTop() - this.K.getPaddingTop());
b(this.L, this.K.getRight() - this.L.getMeasuredWidth(), this.K.getBottom() - this.L.getMeasuredHeight());
}
- private void c(int i, int i2){
+ private void c(int i, int i2) {
b(this.S, i, 0, i2, 0);
}
- private void c(int i, int i2, int i3, int i4){
+ private void c(int i, int i2, int i3, int i4) {
d(i, i2, i3, f(this.T) + i2);
b(i, i2 + f(this.T), i3, i4);
}
- private int h(){
+ private int h() {
return f(this.K);
}
- private void d(int i, int i2, int i3, int i4){
+ private void d(int i, int i2, int i3, int i4) {
TextView textView = this.T;
a(textView, i + b(textView), i2 + d(this.T));
}
- private int c(View view){
+ private int c(View view) {
ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
if (marginLayoutParams != null) {
@@ -969,7 +966,7 @@ public class DialogItemView extends ViewGroup{
return 0;
}
- private void d(int i, int i2){
+ private void d(int i, int i2) {
b(this.b0, i, 0, i2, 0);
b(this.c0, i, 0, i2, 0);
b(this.d0, i, 0, i2, 0);
@@ -977,13 +974,13 @@ public class DialogItemView extends ViewGroup{
b(this.f0, i, 0, i2, 0);
}
- private void a(int i, int i2, int i3){
+ private void a(int i, int i2, int i3) {
b(this.T, i, i3, i2, 0);
b(this.V, i, i3, i2, 0);
b(this.U, i, i3 + g(this.V), i2, 0);
}
- private int d(View view){
+ private int d(View view) {
ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
if (marginLayoutParams != null) {
@@ -993,7 +990,7 @@ public class DialogItemView extends ViewGroup{
return 0;
}
- private ViewGroup.MarginLayoutParams a(int i, int i2, int i3, int i4, int i5, int i6){
+ private ViewGroup.MarginLayoutParams a(int i, int i2, int i3, int i4, int i5, int i6) {
ViewGroup.MarginLayoutParams marginLayoutParams = new ViewGroup.MarginLayoutParams(i, i2);
marginLayoutParams.leftMargin = i3;
marginLayoutParams.topMargin = i4;
@@ -1002,23 +999,23 @@ public class DialogItemView extends ViewGroup{
return marginLayoutParams;
}
- public int a(int i){
+ public int a(int i) {
return (int) (i * (this.a.densityDpi / 160.0f));
}
- public int a(float f2){
+ public int a(float f2) {
return (int) (f2 * (this.a.densityDpi / 160.0f));
}
- private void a(View view, int i, int i2, int i3, int i4){
+ private void a(View view, int i, int i2, int i3, int i4) {
view.measure(ViewGroup.getChildMeasureSpec(i, i2 + b(view) + c(view), view.getLayoutParams().width), ViewGroup.getChildMeasureSpec(i3, i4 + d(view) + a(view), view.getLayoutParams().height));
}
- private void a(View view, int i, int i2){
+ private void a(View view, int i, int i2) {
view.layout(i, i2, view.getMeasuredWidth() + i, view.getMeasuredHeight() + i2);
}
- private int a(View view){
+ private int a(View view) {
ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
if (marginLayoutParams != null) {
diff --git a/app/src/main/java/com/vk/profile/ui/community/CommunityBorderedImageView.java b/app/src/main/java/com/vk/profile/ui/community/CommunityBorderedImageView.java
index 0c8c8b19b..bfcd7f239 100644
--- a/app/src/main/java/com/vk/profile/ui/community/CommunityBorderedImageView.java
+++ b/app/src/main/java/com/vk/profile/ui/community/CommunityBorderedImageView.java
@@ -6,7 +6,6 @@ import android.graphics.Canvas;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.util.AttributeSet;
-
import com.vk.core.ui.themes.Themable;
import com.vk.core.ui.themes.VKThemeHelper;
import com.vk.core.util.ContextExtKt;
@@ -14,7 +13,7 @@ import com.vk.dto.stories.model.StoriesContainer;
import com.vk.stories.view.BorderedCircleImageView;
import com.vtosters.lite.R;
-public final class CommunityBorderedImageView extends BorderedCircleImageView implements Themable{
+public final class CommunityBorderedImageView extends BorderedCircleImageView implements Themable {
private boolean f0;
private int g0;
private int h0;
@@ -23,19 +22,19 @@ public final class CommunityBorderedImageView extends BorderedCircleImageView im
private int unread;
private int read;
- public CommunityBorderedImageView(Context context){
+ public CommunityBorderedImageView(Context context) {
this(context, null, 0, 6);
}
- public CommunityBorderedImageView(Context context, AttributeSet attributeSet){
+ public CommunityBorderedImageView(Context context, AttributeSet attributeSet) {
this(context, attributeSet, 0, 4);
}
- public CommunityBorderedImageView(Context context, AttributeSet attributeSet, int i, int i2){
+ public CommunityBorderedImageView(Context context, AttributeSet attributeSet, int i, int i2) {
this(context, (i2 & 2) != 0 ? null : attributeSet, (i2 & 4) != 0 ? 0 : i);
}
- public CommunityBorderedImageView(Context context, AttributeSet attributeSet, int i){
+ public CommunityBorderedImageView(Context context, AttributeSet attributeSet, int i) {
super(context, attributeSet, i);
this.g0 = VKThemeHelper.d((int) R.attr.accent);
this.h0 = ContextExtKt.a(context, (int) R.color.gray_200);
@@ -43,44 +42,44 @@ public final class CommunityBorderedImageView extends BorderedCircleImageView im
this.j0 = new PorterDuffColorFilter(this.h0, PorterDuff.Mode.SRC_IN);
}
- public final void c(int unread, int read){ // get unread and read resid
+ public final void c(int unread, int read) { // get unread and read resid
this.unread = unread;
this.read = read;
this.T = BitmapFactory.decodeResource(getResources(), this.unread);
}
- public final int getPrimaryColor(){
+ public final int getPrimaryColor() {
return this.g0;
}
- public final void setPrimaryColor(int i){
+ public final void setPrimaryColor(int i) {
this.g0 = i;
this.i0 = new PorterDuffColorFilter(i, PorterDuff.Mode.SRC_IN);
}
- public final int getWasViewedColor(){
+ public final int getWasViewedColor() {
return this.h0;
}
- public final void setWasViewedColor(int i){
+ public final void setWasViewedColor(int i) {
this.h0 = i;
this.j0 = new PorterDuffColorFilter(i, PorterDuff.Mode.SRC_IN);
}
- public final void o(){
+ public final void o() {
this.f0 = false;
l();
}
@Override // com.vk.stories.view.ClippedImageView, android.widget.ImageView, android.view.View
- public void onDraw(Canvas canvas){
+ public void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (this.f0) {
a(canvas);
}
}
- public final void setStoryContainer(StoriesContainer storiesContainer){
+ public final void setStoryContainer(StoriesContainer storiesContainer) {
if (storiesContainer.L1()) {
this.f0 = true;
m();
@@ -101,6 +100,6 @@ public final class CommunityBorderedImageView extends BorderedCircleImageView im
}
@Override // com.vk.core.ui.themes.Themable
- public void v(){
+ public void v() {
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/vk/sharing/view/TargetImageView.java b/app/src/main/java/com/vk/sharing/view/TargetImageView.java
index 7358ce917..dcec58e21 100644
--- a/app/src/main/java/com/vk/sharing/view/TargetImageView.java
+++ b/app/src/main/java/com/vk/sharing/view/TargetImageView.java
@@ -1,26 +1,13 @@
package com.vk.sharing.view;
-import static b.h.z.f.roboto_medium;
-import static ru.vtosters.lite.utils.ThemesUtils.getAccentColor;
-
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.LinearGradient;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.RectF;
-import android.graphics.Shader;
-import android.graphics.Typeface;
+import android.graphics.*;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.util.AttributeSet;
-
import androidx.annotation.UiThread;
import androidx.core.content.ContextCompat;
-
import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.generic.GenericDraweeHierarchy;
import com.facebook.drawee.view.DraweeHolder;
@@ -35,6 +22,9 @@ import ru.vtosters.lite.utils.Preferences;
import java.util.Arrays;
+import static b.h.z.f.roboto_medium;
+import static ru.vtosters.lite.utils.ThemesUtils.getAccentColor;
+
@UiThread
public class TargetImageView extends VKMultiImageView {
private static final int I = Screen.a(2);
diff --git a/app/src/main/java/com/vk/stickers/details/StickerDetailsAdapter.java b/app/src/main/java/com/vk/stickers/details/StickerDetailsAdapter.java
index a2f830125..3aacdbe50 100644
--- a/app/src/main/java/com/vk/stickers/details/StickerDetailsAdapter.java
+++ b/app/src/main/java/com/vk/stickers/details/StickerDetailsAdapter.java
@@ -129,23 +129,23 @@ public final class StickerDetailsAdapter extends RecyclerView.Adapter B0 = new HashSet<>();
private final PipelineDraweeControllerBuilder f0;
private int A0;
@@ -72,35 +70,35 @@ public class StoryCircleImageView extends BorderedCircleImageView implements The
private String y0;
private LottieDrawable z0;
- public StoryCircleImageView(Context context){
+ public StoryCircleImageView(Context context) {
this(context, null);
}
- public StoryCircleImageView(Context context, AttributeSet attributeSet){
+ public StoryCircleImageView(Context context, AttributeSet attributeSet) {
this(context, attributeSet, 0);
}
- public StoryCircleImageView(Context context, AttributeSet attributeSet, int i){
+ public StoryCircleImageView(Context context, AttributeSet attributeSet, int i) {
super(context, attributeSet, i);
this.f0 = FrescoWrapper.c.d();
a(context, attributeSet, i);
}
- private void b(Canvas canvas){
+ private void b(Canvas canvas) {
this.z0.draw(canvas);
}
- private boolean o(){
+ private boolean o() {
return !this.u0 && !this.t0 && this.z0 == null && !this.w0 && !this.v0;
}
- private void p(){
+ private void p() {
this.z0.stop();
this.z0 = null;
invalidate();
}
- private void q(){
+ private void q() {
int i = this.j0;
if (i != 0 && VKThemeHelper.k.a(i)) {
this.k0 = VKThemeHelper.d(this.j0);
@@ -132,19 +130,19 @@ public class StoryCircleImageView extends BorderedCircleImageView implements The
this.s0 = DrawableUtils.a(a4, i8, i8);
}
- private void r(){
+ private void r() {
this.c0 = Screen.a(o() ? 0 : 2);
m();
q();
}
- private void setUploadFailed(boolean z){
+ private void setUploadFailed(boolean z) {
this.u0 = z;
invalidate();
}
@Override // com.vk.stories.view.ClippedImageView, android.widget.ImageView, android.view.View
- public void onDraw(Canvas canvas){
+ public void onDraw(Canvas canvas) {
super.onDraw(canvas);
setBorderAlpha(255);
if (this.u0) {
@@ -170,13 +168,13 @@ public class StoryCircleImageView extends BorderedCircleImageView implements The
}
@Override // com.vk.stories.view.BorderedCircleImageView, android.view.View
- public void onSizeChanged(int i, int i2, int i3, int i4){
+ public void onSizeChanged(int i, int i2, int i3, int i4) {
super.onSizeChanged(i, i2, i3, i4);
this.i0.setBounds(0, 0, i, i2);
b(i);
}
- public void setStoryContainer(@Nullable StoriesContainer storiesContainer){
+ public void setStoryContainer(@Nullable StoriesContainer storiesContainer) {
boolean z = true;
boolean z2 = storiesContainer != null && storiesContainer.Q1();
this.v0 = storiesContainer != null && storiesContainer.S1() && !storiesContainer.L1();
@@ -246,12 +244,12 @@ public class StoryCircleImageView extends BorderedCircleImageView implements The
}
@Override // com.vk.core.ui.themes.Themable
- public void v(){
+ public void v() {
r();
invalidate();
}
- private void b(int i){
+ private void b(int i) {
LottieDrawable lottieDrawable = this.z0;
if (lottieDrawable == null || lottieDrawable.e() == null) {
return;
@@ -259,7 +257,7 @@ public class StoryCircleImageView extends BorderedCircleImageView implements The
this.z0.d(i / this.z0.e().a().width());
}
- protected void a(Context context, AttributeSet attributeSet, int i){
+ protected void a(Context context, AttributeSet attributeSet, int i) {
TypedArray obtainStyledAttributes = context.obtainStyledAttributes(attributeSet, com.vtosters.lite.a0.StoryCircleImageView, i, 0);
a(context, attributeSet, obtainStyledAttributes);
obtainStyledAttributes.recycle();
@@ -267,7 +265,7 @@ public class StoryCircleImageView extends BorderedCircleImageView implements The
this.T = this.p0;
}
- public void b(@Nullable List list){
+ public void b(@Nullable List list) {
PipelineDraweeControllerBuilder pipelineDraweeControllerBuilder = this.f0;
pipelineDraweeControllerBuilder.j();
pipelineDraweeControllerBuilder.a(getController());
@@ -277,7 +275,7 @@ public class StoryCircleImageView extends BorderedCircleImageView implements The
}
@SuppressLint("ResourceType")
- private void a(Context context, AttributeSet attributeSet, TypedArray typedArray){ // bullshit vk code is not working with custom bordered circle image
+ private void a(Context context, AttributeSet attributeSet, TypedArray typedArray) { // bullshit vk code is not working with custom bordered circle image
Drawable drawable;
this.A0 = typedArray.getDimensionPixelSize(0, Screen.a(64.0f));
if (typedArray.hasValue(2)) {
@@ -344,13 +342,13 @@ public class StoryCircleImageView extends BorderedCircleImageView implements The
this.s0 = DrawableUtils.a(drawable4, i4, i4);
}
- public void a(StoriesContainer storiesContainer){
+ public void a(StoriesContainer storiesContainer) {
if (this.z0 == null) {
LottieCompositionFactory.a(getContext(), this.y0).b(new a(storiesContainer));
}
}
- public void a(StoriesContainer storiesContainer, LottieComposition lottieComposition){
+ public void a(StoriesContainer storiesContainer, LottieComposition lottieComposition) {
this.z0 = new LottieDrawable();
this.z0.a(lottieComposition);
this.z0.d(-1);
@@ -363,7 +361,7 @@ public class StoryCircleImageView extends BorderedCircleImageView implements The
this.z0.stop();
}
- private void a(boolean z, boolean z2){
+ private void a(boolean z, boolean z2) {
k();
this.t0 = z;
if (!z2) {
@@ -373,7 +371,7 @@ public class StoryCircleImageView extends BorderedCircleImageView implements The
}
}
- private void a(@NonNull PipelineDraweeControllerBuilder pipelineDraweeControllerBuilder, @Nullable final List list){
+ private void a(@NonNull PipelineDraweeControllerBuilder pipelineDraweeControllerBuilder, @Nullable final List list) {
pipelineDraweeControllerBuilder.b(null);
pipelineDraweeControllerBuilder.a((Supplier>>) null);
if (list != null && !list.isEmpty()) {
@@ -390,7 +388,7 @@ public class StoryCircleImageView extends BorderedCircleImageView implements The
pipelineDraweeControllerBuilder.b(null);
}
- public DataSource> a(@Nullable List list){
+ public DataSource> a(@Nullable List list) {
ArrayList arrayList = new ArrayList<>();
for (String o : Objects.requireNonNull(list)) {
arrayList.add(new Image(-1, -1, o));
@@ -398,15 +396,15 @@ public class StoryCircleImageView extends BorderedCircleImageView implements The
return AvatarDataSource.a(arrayList, this.A0, 0);
}
- public class a implements LottieListener{
+ public class a implements LottieListener {
final StoriesContainer a;
- a(StoriesContainer storiesContainer){
+ a(StoriesContainer storiesContainer) {
this.a = storiesContainer;
}
@Override // com.airbnb.lottie.LottieListener
- public void a(LottieComposition lottieComposition){
+ public void a(LottieComposition lottieComposition) {
if (StoryCircleImageView.this.getWidth() > 0) {
StoryCircleImageView.this.a(this.a, lottieComposition);
} else {
@@ -414,15 +412,15 @@ public class StoryCircleImageView extends BorderedCircleImageView implements The
}
}
- public class ViewTreeObserver$OnGlobalLayoutListenerC0393a implements ViewTreeObserver.OnGlobalLayoutListener{
+ public class ViewTreeObserver$OnGlobalLayoutListenerC0393a implements ViewTreeObserver.OnGlobalLayoutListener {
final LottieComposition a;
- ViewTreeObserver$OnGlobalLayoutListenerC0393a(LottieComposition lottieComposition){
+ ViewTreeObserver$OnGlobalLayoutListenerC0393a(LottieComposition lottieComposition) {
this.a = lottieComposition;
}
@Override // android.view.ViewTreeObserver.OnGlobalLayoutListener
- public void onGlobalLayout(){
+ public void onGlobalLayout() {
StoryCircleImageView.this.getViewTreeObserver().removeOnGlobalLayoutListener(this);
a aVar = a.this;
StoryCircleImageView.this.a(aVar.a, this.a);
@@ -430,12 +428,12 @@ public class StoryCircleImageView extends BorderedCircleImageView implements The
}
}
- public class b implements ValueAnimator.AnimatorUpdateListener{
- b(){
+ public class b implements ValueAnimator.AnimatorUpdateListener {
+ b() {
}
@Override // android.animation.ValueAnimator.AnimatorUpdateListener
- public void onAnimationUpdate(ValueAnimator valueAnimator){
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
StoryCircleImageView.this.invalidate();
}
}
diff --git a/app/src/main/java/com/vtosters/lite/ui/bottomnavigation/BottomNavigationItemView.java b/app/src/main/java/com/vtosters/lite/ui/bottomnavigation/BottomNavigationItemView.java
index 137ac48d5..a800144ba 100644
--- a/app/src/main/java/com/vtosters/lite/ui/bottomnavigation/BottomNavigationItemView.java
+++ b/app/src/main/java/com/vtosters/lite/ui/bottomnavigation/BottomNavigationItemView.java
@@ -1,8 +1,5 @@
package com.vtosters.lite.ui.bottomnavigation;
-import static ru.vtosters.lite.utils.Preferences.vkme;
-import static ru.vtosters.lite.utils.ThemesUtils.getCSTDock;
-
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -18,7 +15,6 @@ import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.view.menu.MenuItemImpl;
@@ -26,12 +22,13 @@ import androidx.appcompat.view.menu.MenuView;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.core.view.ViewCompat;
-
import com.vk.core.ui.themes.VKThemeHelper;
import com.vtosters.lite.R;
-
import me.grishka.appkit.utils.V;
+import static ru.vtosters.lite.utils.Preferences.vkme;
+import static ru.vtosters.lite.utils.ThemesUtils.getCSTDock;
+
public class BottomNavigationItemView extends FrameLayout implements MenuView.ItemView {
private final TextView mSmallLabel;
private final TextView mLargeLabel;
@@ -224,7 +221,7 @@ public class BottomNavigationItemView extends FrameLayout implements MenuView.It
int textsize;
- if (vkme()){
+ if (vkme()) {
textsize = 11;
} else {
textsize = 10;
diff --git a/app/src/main/java/ru/vtosters/lite/deviceinfo/Device.java b/app/src/main/java/ru/vtosters/lite/deviceinfo/Device.java
index 61a849862..c1c9d3d3c 100644
--- a/app/src/main/java/ru/vtosters/lite/deviceinfo/Device.java
+++ b/app/src/main/java/ru/vtosters/lite/deviceinfo/Device.java
@@ -2,9 +2,9 @@ package ru.vtosters.lite.deviceinfo;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.ThemesUtils;
+import ru.vtosters.lite.utils.VersionReader;
import static ru.vtosters.lite.deviceinfo.OEMDetector.*;
-import static ru.vtosters.lite.utils.About.getBuildNumber;
public class Device {
private String boardName;
@@ -88,7 +88,8 @@ public class Device {
public String toDeviceName() {
return "Device information: "
- + "commit='" + getBuildNumber()
+ + "commit='" + VersionReader.getVersionBuild()
+ + "', fullVersionInfo='" + VersionReader.getVersionFull()
+ "', isMilkshake='" + ThemesUtils.isMilkshake()
+ "', sdkVersion='" + this.sdkVersion
+ "', productName='" + this.productName
@@ -105,7 +106,8 @@ public class Device {
public String forLogging() {
return "**" + AndroidUtils.getString("device_info") + ":** " + "\n\n"
- + "- Commit: " + getBuildNumber() + "\n"
+ + "- Commit: " + VersionReader.getVersionBuild() + "\n"
+ + "- Full Version Information: " + VersionReader.getVersionFull() + "\n"
+ "- Android SDK: " + this.sdkVersion + "\n"
+ "- Product: " + this.productName + "\n"
+ "- Device: " + this.deviceName + "\n"
diff --git a/app/src/main/java/ru/vtosters/lite/music/VKM3UParser.java b/app/src/main/java/ru/vtosters/lite/music/VKM3UParser.java
index 03b210c98..835a3e728 100644
--- a/app/src/main/java/ru/vtosters/lite/music/VKM3UParser.java
+++ b/app/src/main/java/ru/vtosters/lite/music/VKM3UParser.java
@@ -1,7 +1,6 @@
package ru.vtosters.lite.music;
import android.text.TextUtils;
-import android.util.Log;
import java.util.ArrayList;
import java.util.List;
@@ -65,7 +64,8 @@ public class VKM3UParser {
if (TextUtils.isEmpty(mData)) throw new NullPointerException("mData==null");
Scanner scanner = new Scanner(mData);
String line = scanner.nextLine();
- if (!"#EXTM3U".equals(line)) throw new IllegalStateException(String.format("Unknown initial M3U tag: %s", line));
+ if (!"#EXTM3U".equals(line))
+ throw new IllegalStateException(String.format("Unknown initial M3U tag: %s", line));
boolean aes128 = false;
String keyUri = "";
while (scanner.hasNextLine()) {
@@ -82,7 +82,7 @@ public class VKM3UParser {
} else {
throw new RuntimeException(String.format("Failed to parse: %s", line));
}
- mHeapSize += line.getBytes().length;;
+ mHeapSize += line.getBytes().length;
}
}
diff --git a/app/src/main/java/ru/vtosters/lite/music/cache/injectors/TracklistInjector.java b/app/src/main/java/ru/vtosters/lite/music/cache/injectors/TracklistInjector.java
index 53cda25b2..037986b71 100644
--- a/app/src/main/java/ru/vtosters/lite/music/cache/injectors/TracklistInjector.java
+++ b/app/src/main/java/ru/vtosters/lite/music/cache/injectors/TracklistInjector.java
@@ -18,7 +18,9 @@ import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.NetworkUtils;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
public class TracklistInjector {
@@ -224,6 +226,7 @@ public class TracklistInjector {
}
}
}
+
public static JSONObject createVirtualCatalog(List tracks) throws JSONException {
var audiosBlock = new JSONObject()
.put("id", getRandomId())
diff --git a/app/src/main/java/ru/vtosters/lite/music/catalog/inject/CatalogJsonInjector.java b/app/src/main/java/ru/vtosters/lite/music/catalog/inject/CatalogJsonInjector.java
index f0ab25622..adbb6637f 100644
--- a/app/src/main/java/ru/vtosters/lite/music/catalog/inject/CatalogJsonInjector.java
+++ b/app/src/main/java/ru/vtosters/lite/music/catalog/inject/CatalogJsonInjector.java
@@ -18,7 +18,7 @@ import ru.vtosters.lite.utils.Preferences;
import java.io.IOException;
-import static ru.vtosters.lite.feature.groupslist.GroupsCatalogInjector.*;
+import static ru.vtosters.lite.feature.groupslist.GroupsCatalogInjector.injectIntoCatalog;
import static ru.vtosters.lite.hooks.DateHook.getLocale;
import static ru.vtosters.lite.music.cache.helpers.PlaylistHelper.*;
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
@@ -152,7 +152,8 @@ public class CatalogJsonInjector {
var useOldAppVer = getBoolValue("useOldAppVer", false);
var isUsersCatalog = section.optString("url").equals("https://vk.com/audios" + getUserId() + "?section=" + (useOldAppVer ? "all" : "general"));
- if (!isUsersCatalog || !CacheDatabaseDelegate.hasTracks() || LibVKXClient.isIntegrationEnabled()) return; // early return if not users catalog or no tracks or integration enabled
+ if (!isUsersCatalog || !CacheDatabaseDelegate.hasTracks() || LibVKXClient.isIntegrationEnabled())
+ return; // early return if not users catalog or no tracks or integration enabled
var blocks = section.getJSONArray("blocks");
var noPlaylists = !json.has("playlists");
diff --git a/app/src/main/java/ru/vtosters/lite/ssfs/ImBanner.java b/app/src/main/java/ru/vtosters/lite/ssfs/ImBanner.java
index 43b802d81..6ee323557 100644
--- a/app/src/main/java/ru/vtosters/lite/ssfs/ImBanner.java
+++ b/app/src/main/java/ru/vtosters/lite/ssfs/ImBanner.java
@@ -10,8 +10,6 @@ import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.LifecycleUtils;
import ru.vtosters.lite.utils.Preferences;
-import static ru.vtosters.lite.utils.Preferences.getBoolValue;
-
public class ImBanner {
private static final String TAG = "ImBanner";
diff --git a/app/src/main/java/ru/vtosters/lite/ssfs/UsersList.java b/app/src/main/java/ru/vtosters/lite/ssfs/UsersList.java
index a734d5fc8..030d3b646 100644
--- a/app/src/main/java/ru/vtosters/lite/ssfs/UsersList.java
+++ b/app/src/main/java/ru/vtosters/lite/ssfs/UsersList.java
@@ -18,7 +18,6 @@ import java.util.ArrayList;
import java.util.List;
import static ru.vtosters.lite.utils.Preferences.dev;
-import static ru.vtosters.lite.utils.Preferences.getBoolValue;
public class UsersList {
private static final String TAG = "UsersList";
diff --git a/app/src/main/java/ru/vtosters/lite/ui/fragments/SystemInfo.java b/app/src/main/java/ru/vtosters/lite/ui/fragments/SystemInfo.java
index 80c34e99c..f664814d9 100644
--- a/app/src/main/java/ru/vtosters/lite/ui/fragments/SystemInfo.java
+++ b/app/src/main/java/ru/vtosters/lite/ui/fragments/SystemInfo.java
@@ -8,9 +8,9 @@ import android.os.Bundle;
import com.vtosters.lite.R;
import ru.vtosters.lite.deviceinfo.OEMDetector;
import ru.vtosters.lite.ui.PreferenceFragmentUtils;
-import ru.vtosters.lite.utils.About;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.Preferences;
+import ru.vtosters.lite.utils.VersionReader;
public class SystemInfo extends TrackedMaterialPreferenceToolbarFragment {
@@ -33,7 +33,9 @@ public class SystemInfo extends TrackedMaterialPreferenceToolbarFragment {
String miuiUiVersionCodeName = OEMDetector.getMiuiUiVersionCode();
String emuiVersionCode = OEMDetector.getEmuiVersionCode();
String packageName = requireContext().getPackageName();
- String commit = About.getBuildNumber();
+ String commit = VersionReader.getVersionCommit();
+ String branch = VersionReader.getVersionBranch();
+ String fullbuildinfo = VersionReader.getVersionFull();
boolean isMiui = OEMDetector.isMIUI();
boolean isFlyme = OEMDetector.isFlyme();
@@ -62,6 +64,18 @@ public class SystemInfo extends TrackedMaterialPreferenceToolbarFragment {
return false;
});
+ PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "Branch", branch, 0, preference -> {
+ ((ClipboardManager) requireActivity().getSystemService(Context.CLIPBOARD_SERVICE)).setPrimaryClip(ClipData.newPlainText("MBH-ST", branch));
+ AndroidUtils.sendToast(requireContext().getString(R.string.copied_to_clipboard));
+ return false;
+ });
+
+ PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "Build Info", fullbuildinfo, 0, preference -> {
+ ((ClipboardManager) requireActivity().getSystemService(Context.CLIPBOARD_SERVICE)).setPrimaryClip(ClipData.newPlainText("MBH-ST", fullbuildinfo));
+ AndroidUtils.sendToast(requireContext().getString(R.string.copied_to_clipboard));
+ return false;
+ });
+
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "Valid Signature", "Value: " + isValidSignature, 0, null);
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "isTablet", "Value: " + isTablet, 0, null);
diff --git a/app/src/main/java/ru/vtosters/lite/ui/fragments/VTSettings.java b/app/src/main/java/ru/vtosters/lite/ui/fragments/VTSettings.java
index 39b275902..bd5d81caa 100644
--- a/app/src/main/java/ru/vtosters/lite/ui/fragments/VTSettings.java
+++ b/app/src/main/java/ru/vtosters/lite/ui/fragments/VTSettings.java
@@ -520,7 +520,7 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment impleme
getPreferenceScreen(),
"",
requireContext().getString(R.string.menu_about),
- Preferences.getBuildName() + " | " + About.getBuildNumber(),
+ Preferences.getBuildName() + " | " + (Preferences.isValidSignature() ? VersionReader.getVersionBuild() : VersionReader.getVersionFull()),
R.drawable.ic_about_outline_28,
preference -> {
NavigatorUtils.switchFragment(requireContext(), AboutAppFragment.class);
diff --git a/app/src/main/java/ru/vtosters/lite/utils/About.java b/app/src/main/java/ru/vtosters/lite/utils/About.java
index 576ee8a53..c4ee3ae7a 100644
--- a/app/src/main/java/ru/vtosters/lite/utils/About.java
+++ b/app/src/main/java/ru/vtosters/lite/utils/About.java
@@ -1,23 +1,14 @@
package ru.vtosters.lite.utils;
-import android.content.Context;
-import android.content.res.AssetManager;
-import android.text.TextUtils;
import android.view.View;
import com.vtosters.lite.R;
-import java.io.IOException;
-import java.util.Scanner;
-
-import static ru.vtosters.lite.utils.AndroidUtils.*;
+import static ru.vtosters.lite.utils.AndroidUtils.getApplicationName;
+import static ru.vtosters.lite.utils.AndroidUtils.sendToast;
import static ru.vtosters.lite.utils.Preferences.devmenu;
import static ru.vtosters.lite.utils.Preferences.getBuildName;
public class About {
- public static String getBuildNumber() {
- return getBuild(getGlobalContext(), "version.properties");
- }
-
public static void injectToToolBar(View view) {
view.setOnLongClickListener(v -> {
if (devmenu()) {
@@ -31,24 +22,10 @@ public class About {
}
public static String getCommitLink() {
- return "https://github.com/vtosters/lite/commit/" + getBuild(getGlobalContext(), "version.properties"); // https://git.maki.su/gdlbo/lite/commit/ github repo
- }
-
- public static String getBuild(Context context, String name) {
- try {
- final var scanner = new Scanner(context.getAssets().open(name, AssetManager.ACCESS_BUFFER));
- while (scanner.hasNextLine()) {
- final var line = scanner.findInLine("VERSION_BUILD=.+");
- if (!TextUtils.isEmpty(line))
- return line.substring(14);
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- return "0000000";
+ return "https://github.com/vtosters/lite/commit/" + VersionReader.getVersionCommit(); // https://git.maki.su/gdlbo/lite/commit/ github repo
}
public static String getAppVersion() {
- return getApplicationName() + " " + getBuildName() + " | " + getBuildNumber();
+ return getApplicationName() + " " + getBuildName() + " | " + VersionReader.getVersionFull();
}
}
diff --git a/app/src/main/java/ru/vtosters/lite/utils/AnalyticsHelper.java b/app/src/main/java/ru/vtosters/lite/utils/AnalyticsHelper.java
index 9528f98ab..dff026ed7 100644
--- a/app/src/main/java/ru/vtosters/lite/utils/AnalyticsHelper.java
+++ b/app/src/main/java/ru/vtosters/lite/utils/AnalyticsHelper.java
@@ -23,8 +23,8 @@ public class AnalyticsHelper {
@Override
public void onPreparedLog(@NonNull Log log, @NonNull String groupName, int flags) {
var device = log.getDevice();
- device.setAppVersion(About.getBuildNumber());
- device.setAppBuild(About.getBuildNumber());
+ device.setAppVersion(VersionReader.getVersionBuild());
+ device.setAppBuild(VersionReader.getVersionBuild());
}
};
diff --git a/app/src/main/java/ru/vtosters/lite/utils/GmsUtils.java b/app/src/main/java/ru/vtosters/lite/utils/GmsUtils.java
index 89ed416fc..9b0de444b 100644
--- a/app/src/main/java/ru/vtosters/lite/utils/GmsUtils.java
+++ b/app/src/main/java/ru/vtosters/lite/utils/GmsUtils.java
@@ -2,9 +2,7 @@ package ru.vtosters.lite.utils;
import android.app.NotificationChannel;
import android.app.NotificationManager;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.os.Build;
import com.google.firebase.iid.FirebaseInstanceIdReceiver;
import com.google.firebase.iid.FirebaseInstanceIdReceiver2;
diff --git a/app/src/main/java/ru/vtosters/lite/utils/IOUtils.java b/app/src/main/java/ru/vtosters/lite/utils/IOUtils.java
index 38d0be563..0f60256a5 100644
--- a/app/src/main/java/ru/vtosters/lite/utils/IOUtils.java
+++ b/app/src/main/java/ru/vtosters/lite/utils/IOUtils.java
@@ -18,7 +18,7 @@ import java.util.Arrays;
public class IOUtils {
public static final int BUFFER_SIZE = 8192;
- public static byte[] decodeStream(InputStream encIs,byte[] aesKey)
+ public static byte[] decodeStream(InputStream encIs, byte[] aesKey)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IOException {
CipherInputStream cip = new CipherInputStream(encIs, getCipher(aesKey));
return readFully(cip);
diff --git a/app/src/main/java/ru/vtosters/lite/utils/NetworkUtils.java b/app/src/main/java/ru/vtosters/lite/utils/NetworkUtils.java
index 72e2129d9..0d83bb3ac 100644
--- a/app/src/main/java/ru/vtosters/lite/utils/NetworkUtils.java
+++ b/app/src/main/java/ru/vtosters/lite/utils/NetworkUtils.java
@@ -6,7 +6,6 @@ import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.os.Build;
import android.telephony.TelephonyManager;
-import androidx.core.content.ContextCompat;
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
diff --git a/app/src/main/java/ru/vtosters/lite/utils/OTAUtils.java b/app/src/main/java/ru/vtosters/lite/utils/OTAUtils.java
index f5ae30491..6aae19e1d 100644
--- a/app/src/main/java/ru/vtosters/lite/utils/OTAUtils.java
+++ b/app/src/main/java/ru/vtosters/lite/utils/OTAUtils.java
@@ -84,7 +84,7 @@ public class OTAUtils {
public boolean isNewVersion() {
try {
- return !mCommitSHA.startsWith(About.getBuildNumber());
+ return !mCommitSHA.startsWith(VersionReader.getVersionBuild());
} catch (Exception e) {
return false;
}
diff --git a/app/src/main/java/ru/vtosters/lite/utils/VersionReader.java b/app/src/main/java/ru/vtosters/lite/utils/VersionReader.java
new file mode 100644
index 000000000..455b5012f
--- /dev/null
+++ b/app/src/main/java/ru/vtosters/lite/utils/VersionReader.java
@@ -0,0 +1,33 @@
+package ru.vtosters.lite.utils;
+
+import java.io.IOException;
+import java.util.Properties;
+
+public class VersionReader {
+ public static String getValue(String key) {
+ try (var inputStream = AndroidUtils.getGlobalContext().getAssets().open("version.properties")) {
+ Properties properties = new Properties();
+ properties.load(inputStream);
+ return properties.getProperty(key);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+ public static String getVersionBuild() {
+ return getValue("VERSION_BUILD");
+ }
+
+ public static String getVersionBranch() {
+ return getValue("VERSION_BRANCH");
+ }
+
+ public static String getVersionCommit() {
+ return getValue("VERSION_COMMIT");
+ }
+
+ public static String getVersionFull() {
+ return getValue("VERSION_FULL");
+ }
+}
diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
index 2b068d114..bda706cb2 100644
--- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -1,30 +1,30 @@
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ android:endX="85.84757"
+ android:endY="92.4963"
+ android:startX="42.9492"
+ android:startY="49.59793"
+ android:type="linear">
+ android:color="#44000000"
+ android:offset="0.0"/>
+ android:color="#00000000"
+ android:offset="1.0"/>
+ android:fillColor="#FFFFFF"
+ android:fillType="nonZero"
+ android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
+ android:strokeWidth="1"
+ android:strokeColor="#00000000"/>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
index 07d5da9cb..194b3a0a5 100644
--- a/app/src/main/res/drawable/ic_launcher_background.xml
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -1,170 +1,170 @@
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ android:fillColor="#3DDC84"
+ android:pathData="M0,0h108v108h-108z"/>
+ android:fillColor="#00000000"
+ android:pathData="M9,0L9,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M19,0L19,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M29,0L29,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M39,0L39,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M49,0L49,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M59,0L59,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M69,0L69,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M79,0L79,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M89,0L89,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M99,0L99,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M0,9L108,9"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M0,19L108,19"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M0,29L108,29"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M0,39L108,39"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M0,49L108,49"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M0,59L108,59"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M0,69L108,69"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M0,79L108,79"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M0,89L108,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M0,99L108,99"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M19,29L89,29"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M19,39L89,39"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M19,49L89,49"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M19,59L89,59"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M19,69L89,69"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M19,79L89,79"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M29,19L29,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M39,19L39,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M49,19L49,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M59,19L59,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M69,19L69,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
+ android:fillColor="#00000000"
+ android:pathData="M79,19L79,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF"/>
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
index eca70cfe5..bbd3e0212 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -1,5 +1,5 @@
-
-
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
index eca70cfe5..bbd3e0212 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -1,5 +1,5 @@
-
-
+
+
\ No newline at end of file
From a11a88e23f6fa6f62cf94e620ba13ad612550fde Mon Sep 17 00:00:00 2001
From: gdlbo <41114720+gdlbo@users.noreply.github.com>
Date: Fri, 12 May 2023 22:22:27 +0300
Subject: [PATCH 012/125] moved debug activation to long click of logo/appver
---
.../java/ru/vtosters/lite/utils/About.java | 4 +-
smali/res/layout/about_app_header_item.xml | 41 ++++++++++---------
.../com/vk/about/AboutAppFragment.smali | 2 -
.../com/vk/about/holders2.smali | 11 +++++
4 files changed, 34 insertions(+), 24 deletions(-)
diff --git a/app/src/main/java/ru/vtosters/lite/utils/About.java b/app/src/main/java/ru/vtosters/lite/utils/About.java
index c4ee3ae7a..e7133cc9b 100644
--- a/app/src/main/java/ru/vtosters/lite/utils/About.java
+++ b/app/src/main/java/ru/vtosters/lite/utils/About.java
@@ -9,7 +9,7 @@ import static ru.vtosters.lite.utils.Preferences.devmenu;
import static ru.vtosters.lite.utils.Preferences.getBuildName;
public class About {
- public static void injectToToolBar(View view) {
+ public static void inject(View view) {
view.setOnLongClickListener(v -> {
if (devmenu()) {
sendToast(AndroidUtils.getString(R.string.debug_menu_already_activated));
@@ -22,7 +22,7 @@ public class About {
}
public static String getCommitLink() {
- return "https://github.com/vtosters/lite/commit/" + VersionReader.getVersionCommit(); // https://git.maki.su/gdlbo/lite/commit/ github repo
+ return "https://github.com/vtosters/lite/commit/" + VersionReader.getVersionCommit();
}
public static String getAppVersion() {
diff --git a/smali/res/layout/about_app_header_item.xml b/smali/res/layout/about_app_header_item.xml
index e015a18b2..a09734e4c 100644
--- a/smali/res/layout/about_app_header_item.xml
+++ b/smali/res/layout/about_app_header_item.xml
@@ -1,28 +1,29 @@
+ android:layout_width="fill_parent"
+ android:layout_height="196.0dip"
+ android:background="@drawable/highlight">
-
-
-
-
+ android:layout_gravity="center"
+ android:orientation="vertical">
+
+
+
+
\ No newline at end of file
diff --git a/smali/smali_classes2/com/vk/about/AboutAppFragment.smali b/smali/smali_classes2/com/vk/about/AboutAppFragment.smali
index 5cf8f5875..baff1503f 100644
--- a/smali/smali_classes2/com/vk/about/AboutAppFragment.smali
+++ b/smali/smali_classes2/com/vk/about/AboutAppFragment.smali
@@ -509,8 +509,6 @@
move-result-object p2
- invoke-static {p2}, Lru/vtosters/lite/utils/About;->injectToToolBar(Landroid/view/View;)V
-
check-cast p2, Landroidx/appcompat/widget/Toolbar;
const v0, 0x7f080376
diff --git a/smali/smali_classes2/com/vk/about/holders2.smali b/smali/smali_classes2/com/vk/about/holders2.smali
index b1fb2d08e..61288b609 100644
--- a/smali/smali_classes2/com/vk/about/holders2.smali
+++ b/smali/smali_classes2/com/vk/about/holders2.smali
@@ -10,6 +10,15 @@
.line 1
invoke-direct {p0, p1}, Lcom/vk/about/holders;->(Landroid/view/View;)V
+ const v0, 0x7f0a00a8
+
+ .line 2
+ invoke-virtual {p1, v0}, Landroid/view/View;->findViewById(I)Landroid/view/View;
+
+ move-result-object v0
+
+ invoke-static {v0}, Lru/vtosters/lite/utils/About;->inject(Landroid/view/View;)V
+
const v0, 0x7f0a0e71
.line 2
@@ -17,6 +26,8 @@
move-result-object v0
+ invoke-static {v0}, Lru/vtosters/lite/utils/About;->inject(Landroid/view/View;)V
+
const-string v1, "itemView.findViewById(R.id.version_name)"
invoke-static {v0, v1}, Lkotlin/jvm/internal/Intrinsics;->a(Ljava/lang/Object;Ljava/lang/String;)V
From 0e94efc4f7fa6c563cf97e8bbb5ebd64c52c3cb6 Mon Sep 17 00:00:00 2001
From: gdlbo <41114720+gdlbo@users.noreply.github.com>
Date: Sat, 13 May 2023 14:38:29 +0300
Subject: [PATCH 013/125] Multithreading verifs downloading
---
.../vtosters/lite/utils/VTVerifications.java | 49 +++++++++----------
1 file changed, 23 insertions(+), 26 deletions(-)
diff --git a/app/src/main/java/ru/vtosters/lite/utils/VTVerifications.java b/app/src/main/java/ru/vtosters/lite/utils/VTVerifications.java
index 2ed647272..6bed0005c 100644
--- a/app/src/main/java/ru/vtosters/lite/utils/VTVerifications.java
+++ b/app/src/main/java/ru/vtosters/lite/utils/VTVerifications.java
@@ -5,19 +5,19 @@ import android.util.Log;
import com.vk.dto.common.VerifyInfo;
import com.vk.navigation.NavigatorKeys;
import okhttp3.*;
+import okio.BufferedSink;
+import okio.Okio;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
+import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
-import static ru.vtosters.lite.utils.Preferences.getBoolValue;
-import static ru.vtosters.lite.utils.Preferences.hasVerification;
-
public class VTVerifications {
public static final List sVerifications = new ArrayList<>();
public static final List sPrometheuses = new ArrayList<>();
@@ -28,7 +28,7 @@ public class VTVerifications {
public static void load(Context context) {
var prefs = context.getSharedPreferences("vt_another_data", 0);
- if ((!NetworkUtils.isNetworkConnected() && NetworkUtils.isInternetSlow() || getBoolValue("isRoamingState", false)) && prefs.contains("ids")) {
+ if ((!NetworkUtils.isNetworkConnected() && NetworkUtils.isInternetSlow() || Preferences.getBoolValue("isRoamingState", false)) && prefs.contains("ids")) {
parseJson(prefs.getString("ids", "[]"));
return;
}
@@ -38,27 +38,24 @@ public class VTVerifications {
.a(RequestBody.a(MediaType.b("application/json; charset=UTF-8"), "{\"types\":[0,228,404,1337]}"))
.a();
- sClient.a(request).a(new Callback() {
- @Override
- public void a(Call call, Response response) {
- try {
- var payload = response.a().g();
- parseJson(payload);
- prefs.edit()
- .putString("ids", payload)
- .apply();
- } catch (Exception e) {
- e.printStackTrace();
- }
+ try (Response response = sClient.a(request).execute()) {
+ File file = new File(AndroidUtils.getGlobalContext().getCacheDir(), "response.json");
+
+ try (BufferedSink sink = Okio.a(Okio.b(file))) {
+ sink.a(response.a().f());
}
- @Override
- public void a(Call call, IOException e) {
- Log.d("VTVerifications", String.valueOf(e));
- }
+ String r = IOUtils.readAllLines(file);
- });
+ parseJson(r);
+ prefs.edit()
+ .putString("ids", r)
+ .apply();
+ } catch (IOException e) {
+ if (prefs.contains("ids")) parseJson(prefs.getString("ids", "[]"));
+ Log.d("VTVerifications", e.getMessage());
+ }
}
/**
@@ -104,7 +101,7 @@ public class VTVerifications {
}
public static boolean vtverif() {
- return getBoolValue("VT_Verification", true);
+ return Preferences.getBoolValue("VT_Verification", true);
}
private static int getId(JSONObject json) {
@@ -121,7 +118,7 @@ public class VTVerifications {
return true;
}
- if (!getBoolValue("VT_Verification", true)) {
+ if (!Preferences.getBoolValue("VT_Verification", true)) {
return false;
}
@@ -133,7 +130,7 @@ public class VTVerifications {
return true;
}
- if (!getBoolValue("VT_Fire", true)) {
+ if (!Preferences.getBoolValue("VT_Fire", true)) {
return false;
}
@@ -141,7 +138,7 @@ public class VTVerifications {
}
public static boolean hasDeveloper(JSONObject jSONObject) {
- if (!getBoolValue("VT_Dev", true)) {
+ if (!Preferences.getBoolValue("VT_Dev", true)) {
return false;
}
@@ -153,6 +150,6 @@ public class VTVerifications {
}
public static boolean haveDonateButton() {
- return hasVerification() || new Random().nextInt(6) != 1;
+ return Preferences.hasVerification() || new Random().nextInt(6) != 1;
}
}
From 34a0615003aa1d59e09ebea469390d596592b0e9 Mon Sep 17 00:00:00 2001
From: gdlbo <41114720+gdlbo@users.noreply.github.com>
Date: Sat, 13 May 2023 15:07:27 +0300
Subject: [PATCH 014/125] added multithread download for some large methods
---
.../messages/MessagesDownloader.java | 54 +++++++++----------
.../ru/vtosters/lite/music/M3UDownloader.java | 19 +++++--
.../java/ru/vtosters/lite/ssfs/UsersList.java | 29 +++++-----
.../vtosters/lite/utils/VTVerifications.java | 1 +
4 files changed, 55 insertions(+), 48 deletions(-)
diff --git a/app/src/main/java/ru/vtosters/lite/downloaders/messages/MessagesDownloader.java b/app/src/main/java/ru/vtosters/lite/downloaders/messages/MessagesDownloader.java
index 39b192e8b..e5407bbd2 100644
--- a/app/src/main/java/ru/vtosters/lite/downloaders/messages/MessagesDownloader.java
+++ b/app/src/main/java/ru/vtosters/lite/downloaders/messages/MessagesDownloader.java
@@ -1,14 +1,13 @@
package ru.vtosters.lite.downloaders.messages;
-import android.util.Log;
import android.util.SparseArray;
import androidx.annotation.Nullable;
import com.vk.core.util.ToastUtils;
import com.vtosters.lite.R;
-import okhttp3.Call;
-import okhttp3.Callback;
import okhttp3.Request;
import okhttp3.Response;
+import okio.BufferedSink;
+import okio.Okio;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -18,6 +17,7 @@ import ru.vtosters.lite.downloaders.messages.items.MiniUser;
import ru.vtosters.lite.proxy.ProxyUtils;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils;
+import ru.vtosters.lite.utils.IOUtils;
import java.io.File;
import java.io.FileOutputStream;
@@ -108,38 +108,36 @@ public class MessagesDownloader {
var req = new Request.a()
.b(url)
.a();
- VtOkHttpClient.getInstance().a(req).a(new Callback() {
- @Override
- public void a(Call call, IOException e) {
- Log.d("MessagesDownloader", String.valueOf(e));
+
+ try (Response response = VtOkHttpClient.getInstance().a(req).execute()) {
+ File file = new File(AndroidUtils.getGlobalContext().getCacheDir(), "response" + "_user_list" + ".json");
+
+ try (BufferedSink sink = Okio.a(Okio.b(file))) {
+ sink.a(response.a().f());
}
- @Override
- public void a(Call call, Response response)
- throws IOException {
- try {
- JSONObject obj = new JSONObject(response.a().g()).getJSONObject("response");
+ JSONObject obj = new JSONObject(IOUtils.readAllLines(file)).getJSONObject("response");
- SparseArray users = new SparseArray<>();
- parseUsers(users, obj.optJSONArray("profiles"), false);
- parseUsers(users, obj.optJSONArray("groups"), true);
+ SparseArray users = new SparseArray<>();
+ parseUsers(users, obj.optJSONArray("profiles"), false);
+ parseUsers(users, obj.optJSONArray("groups"), true);
- List msgs = parseMessages(obj.getJSONArray("items"));
+ List msgs = parseMessages(obj.getJSONArray("items"));
- usersArray = users;
+ usersArray = users;
- fos.write(format.provideHeader("TODO", formatTime(System.currentTimeMillis())).getBytes(StandardCharsets.UTF_8));
- for (MiniMsg msg : msgs) {
- fos.write(format.provideMessage(msg, users.get(msg.fromId)).getBytes(StandardCharsets.UTF_8));
- }
- fos.write(format.provideDocumentEnd().getBytes(StandardCharsets.UTF_8));
- fos.close();
- ToastUtils.a(AndroidUtils.getString(R.string.saved_as) + " " + out.getAbsolutePath());
- } catch (IOException | JSONException e) {
- e.printStackTrace();
- }
+ fos.write(format.provideHeader("TODO", formatTime(System.currentTimeMillis())).getBytes(StandardCharsets.UTF_8));
+ for (MiniMsg msg : msgs) {
+ fos.write(format.provideMessage(msg, users.get(msg.fromId)).getBytes(StandardCharsets.UTF_8));
}
- });
+ fos.write(format.provideDocumentEnd().getBytes(StandardCharsets.UTF_8));
+ fos.close();
+ ToastUtils.a(AndroidUtils.getString(R.string.saved_as) + " " + out.getAbsolutePath());
+
+ file.delete();
+ } catch (IOException | JSONException e) {
+ e.printStackTrace();
+ }
}
private List parseMessages(JSONArray jsonArray) throws JSONException {
diff --git a/app/src/main/java/ru/vtosters/lite/music/M3UDownloader.java b/app/src/main/java/ru/vtosters/lite/music/M3UDownloader.java
index 79b4bca4b..38986dbd5 100644
--- a/app/src/main/java/ru/vtosters/lite/music/M3UDownloader.java
+++ b/app/src/main/java/ru/vtosters/lite/music/M3UDownloader.java
@@ -5,6 +5,9 @@ import com.vk.dto.music.MusicTrack;
import java8.util.concurrent.CompletableFuture;
import okhttp3.OkHttpClient;
import okhttp3.Request;
+import okhttp3.Response;
+import okio.BufferedSink;
+import okio.Okio;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.music.converter.ts.FFMpeg;
@@ -53,13 +56,19 @@ public class M3UDownloader implements ITrackDownloader {
return;
}
- try {
- Request request = new Request.a().b(track.D).a();
- String response = client.a(request).execute().a().g();
- parse(response, outDir, callback, track, cache);
+ Request request = new Request.a().b(track.D).a();
+
+ try (Response response = client.a(request).execute()) {
+ File file = new File(AndroidUtils.getGlobalContext().getCacheDir(), track.y1());
+
+ try (BufferedSink sink = Okio.a(Okio.b(file))) {
+ sink.a(response.a().f());
+ }
+
+ parse(IOUtils.readAllLines(file), outDir, callback, track, cache);
+ file.delete();
} catch (IOException e) {
e.printStackTrace();
- callback.onFailure();
}
}
diff --git a/app/src/main/java/ru/vtosters/lite/ssfs/UsersList.java b/app/src/main/java/ru/vtosters/lite/ssfs/UsersList.java
index 030d3b646..3f3252127 100644
--- a/app/src/main/java/ru/vtosters/lite/ssfs/UsersList.java
+++ b/app/src/main/java/ru/vtosters/lite/ssfs/UsersList.java
@@ -1,18 +1,20 @@
package ru.vtosters.lite.ssfs;
import android.util.Log;
-import okhttp3.Call;
-import okhttp3.Callback;
import okhttp3.Request;
import okhttp3.Response;
+import okio.BufferedSink;
+import okio.Okio;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils;
+import ru.vtosters.lite.utils.IOUtils;
import ru.vtosters.lite.utils.NetworkUtils;
+import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -35,21 +37,18 @@ public class UsersList {
.b(Utils.getDomain() + "/api/getUsersWithServiceDescriptionsAndBanners")
.a();
- VtOkHttpClient.getInstance().a(request).a(new Callback() {
- @Override
- public void a(Call call, IOException e) {
- e.printStackTrace();
+ try (Response response = VtOkHttpClient.getInstance().a(request).execute()) {
+ File file = new File(AndroidUtils.getGlobalContext().getCacheDir(), "response" + "_user_list" + ".json");
+
+ try (BufferedSink sink = Okio.a(Okio.b(file))) {
+ sink.a(response.a().f());
}
- @Override
- public void a(Call call, Response response) {
- try {
- parseJson(new JSONObject(response.a().g()).getJSONObject("response"));
- } catch (JSONException | IOException e) {
- e.printStackTrace();
- }
- }
- });
+ parseJson(new JSONObject(IOUtils.readAllLines(file)).getJSONObject("response"));
+ file.delete();
+ } catch (IOException | JSONException e) {
+ e.printStackTrace();
+ }
}
public static boolean hasDescription(int id) {
diff --git a/app/src/main/java/ru/vtosters/lite/utils/VTVerifications.java b/app/src/main/java/ru/vtosters/lite/utils/VTVerifications.java
index 6bed0005c..b50487eb9 100644
--- a/app/src/main/java/ru/vtosters/lite/utils/VTVerifications.java
+++ b/app/src/main/java/ru/vtosters/lite/utils/VTVerifications.java
@@ -52,6 +52,7 @@ public class VTVerifications {
prefs.edit()
.putString("ids", r)
.apply();
+ file.delete();
} catch (IOException e) {
if (prefs.contains("ids")) parseJson(prefs.getString("ids", "[]"));
Log.d("VTVerifications", e.getMessage());
From 2d96619c675966eae5ecb71300e884a15a35efe9 Mon Sep 17 00:00:00 2001
From: gdlbo <41114720+gdlbo@users.noreply.github.com>
Date: Sat, 13 May 2023 15:43:29 +0300
Subject: [PATCH 015/125] Create Multithreading.java
---
.../lite/downloaders/Multithreading.java | 84 +++++++++++++++++++
1 file changed, 84 insertions(+)
create mode 100644 app/src/main/java/ru/vtosters/lite/downloaders/Multithreading.java
diff --git a/app/src/main/java/ru/vtosters/lite/downloaders/Multithreading.java b/app/src/main/java/ru/vtosters/lite/downloaders/Multithreading.java
new file mode 100644
index 000000000..f07e97f5d
--- /dev/null
+++ b/app/src/main/java/ru/vtosters/lite/downloaders/Multithreading.java
@@ -0,0 +1,84 @@
+package ru.vtosters.lite.downloaders;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+public class Multithreading {
+ public static void download(URL url, File f, int threadCount) {
+ try {
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestMethod("GET");
+ int responseCode = connection.getResponseCode();
+ if (responseCode == HttpURLConnection.HTTP_OK) {
+ int fileSize = connection.getContentLength();
+ connection.disconnect();
+ RandomAccessFile file = new RandomAccessFile(f, "rw");
+ file.setLength(fileSize);
+ file.close();
+ if (threadCount == 0) threadCount = 4;
+ int partSize = fileSize / threadCount;
+ DownloadThread[] threads = new DownloadThread[threadCount];
+ for (int i = 0; i < threadCount; i++) {
+ int startByte = i * partSize;
+ int endByte = (i + 1) * partSize - 1;
+ if (i == threadCount - 1) {
+ endByte = fileSize - 1;
+ }
+ DownloadThread thread = new DownloadThread(i, url, f.getName(), startByte, endByte);
+ thread.start();
+ threads[i] = thread;
+ }
+ for (DownloadThread thread : threads) {
+ thread.join();
+ }
+ } else {
+ Log.d("Multithreading", "Error, code: " + responseCode);
+ }
+ } catch (IOException | InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ static class DownloadThread extends Thread {
+ private final int threadId;
+ private final URL url;
+ private final String fileName;
+ private final int startByte;
+ private final int endByte;
+
+ public DownloadThread(int threadId, URL url, String fileName, int startByte, int endByte) {
+ this.threadId = threadId;
+ this.url = url;
+ this.fileName = fileName;
+ this.startByte = startByte;
+ this.endByte = endByte;
+ }
+
+ @Override
+ public void run() {
+ try {
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestProperty("Range", "bytes=" + startByte + "-" + endByte);
+ InputStream input = connection.getInputStream();
+ RandomAccessFile file = new RandomAccessFile(fileName, "rw");
+ file.seek(startByte);
+ byte[] buffer = new byte[1024];
+ int length = input.read(buffer);
+ while (length != -1) {
+ file.write(buffer, 0, length);
+ length = input.read(buffer);
+ }
+ file.close();
+ input.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
From 5a7a6281173f229557f80f7055124a26d73664a3 Mon Sep 17 00:00:00 2001
From: gdlbo <41114720+gdlbo@users.noreply.github.com>
Date: Sun, 14 May 2023 17:30:14 +0300
Subject: [PATCH 016/125] Revert "added multithread download for some large
methods"
This reverts commit 34a0615003aa1d59e09ebea469390d596592b0e9.
---
.../messages/MessagesDownloader.java | 54 ++++++++++---------
.../ru/vtosters/lite/music/M3UDownloader.java | 19 ++-----
.../java/ru/vtosters/lite/ssfs/UsersList.java | 29 +++++-----
.../vtosters/lite/utils/VTVerifications.java | 1 -
4 files changed, 48 insertions(+), 55 deletions(-)
diff --git a/app/src/main/java/ru/vtosters/lite/downloaders/messages/MessagesDownloader.java b/app/src/main/java/ru/vtosters/lite/downloaders/messages/MessagesDownloader.java
index e5407bbd2..39b192e8b 100644
--- a/app/src/main/java/ru/vtosters/lite/downloaders/messages/MessagesDownloader.java
+++ b/app/src/main/java/ru/vtosters/lite/downloaders/messages/MessagesDownloader.java
@@ -1,13 +1,14 @@
package ru.vtosters.lite.downloaders.messages;
+import android.util.Log;
import android.util.SparseArray;
import androidx.annotation.Nullable;
import com.vk.core.util.ToastUtils;
import com.vtosters.lite.R;
+import okhttp3.Call;
+import okhttp3.Callback;
import okhttp3.Request;
import okhttp3.Response;
-import okio.BufferedSink;
-import okio.Okio;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -17,7 +18,6 @@ import ru.vtosters.lite.downloaders.messages.items.MiniUser;
import ru.vtosters.lite.proxy.ProxyUtils;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils;
-import ru.vtosters.lite.utils.IOUtils;
import java.io.File;
import java.io.FileOutputStream;
@@ -108,36 +108,38 @@ public class MessagesDownloader {
var req = new Request.a()
.b(url)
.a();
-
- try (Response response = VtOkHttpClient.getInstance().a(req).execute()) {
- File file = new File(AndroidUtils.getGlobalContext().getCacheDir(), "response" + "_user_list" + ".json");
-
- try (BufferedSink sink = Okio.a(Okio.b(file))) {
- sink.a(response.a().f());
+ VtOkHttpClient.getInstance().a(req).a(new Callback() {
+ @Override
+ public void a(Call call, IOException e) {
+ Log.d("MessagesDownloader", String.valueOf(e));
}
- JSONObject obj = new JSONObject(IOUtils.readAllLines(file)).getJSONObject("response");
+ @Override
+ public void a(Call call, Response response)
+ throws IOException {
+ try {
+ JSONObject obj = new JSONObject(response.a().g()).getJSONObject("response");
- SparseArray users = new SparseArray<>();
- parseUsers(users, obj.optJSONArray("profiles"), false);
- parseUsers(users, obj.optJSONArray("groups"), true);
+ SparseArray users = new SparseArray<>();
+ parseUsers(users, obj.optJSONArray("profiles"), false);
+ parseUsers(users, obj.optJSONArray("groups"), true);
- List msgs = parseMessages(obj.getJSONArray("items"));
+ List msgs = parseMessages(obj.getJSONArray("items"));
- usersArray = users;
+ usersArray = users;
- fos.write(format.provideHeader("TODO", formatTime(System.currentTimeMillis())).getBytes(StandardCharsets.UTF_8));
- for (MiniMsg msg : msgs) {
- fos.write(format.provideMessage(msg, users.get(msg.fromId)).getBytes(StandardCharsets.UTF_8));
+ fos.write(format.provideHeader("TODO", formatTime(System.currentTimeMillis())).getBytes(StandardCharsets.UTF_8));
+ for (MiniMsg msg : msgs) {
+ fos.write(format.provideMessage(msg, users.get(msg.fromId)).getBytes(StandardCharsets.UTF_8));
+ }
+ fos.write(format.provideDocumentEnd().getBytes(StandardCharsets.UTF_8));
+ fos.close();
+ ToastUtils.a(AndroidUtils.getString(R.string.saved_as) + " " + out.getAbsolutePath());
+ } catch (IOException | JSONException e) {
+ e.printStackTrace();
+ }
}
- fos.write(format.provideDocumentEnd().getBytes(StandardCharsets.UTF_8));
- fos.close();
- ToastUtils.a(AndroidUtils.getString(R.string.saved_as) + " " + out.getAbsolutePath());
-
- file.delete();
- } catch (IOException | JSONException e) {
- e.printStackTrace();
- }
+ });
}
private List parseMessages(JSONArray jsonArray) throws JSONException {
diff --git a/app/src/main/java/ru/vtosters/lite/music/M3UDownloader.java b/app/src/main/java/ru/vtosters/lite/music/M3UDownloader.java
index 38986dbd5..79b4bca4b 100644
--- a/app/src/main/java/ru/vtosters/lite/music/M3UDownloader.java
+++ b/app/src/main/java/ru/vtosters/lite/music/M3UDownloader.java
@@ -5,9 +5,6 @@ import com.vk.dto.music.MusicTrack;
import java8.util.concurrent.CompletableFuture;
import okhttp3.OkHttpClient;
import okhttp3.Request;
-import okhttp3.Response;
-import okio.BufferedSink;
-import okio.Okio;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.music.converter.ts.FFMpeg;
@@ -56,19 +53,13 @@ public class M3UDownloader implements ITrackDownloader {
return;
}
- Request request = new Request.a().b(track.D).a();
-
- try (Response response = client.a(request).execute()) {
- File file = new File(AndroidUtils.getGlobalContext().getCacheDir(), track.y1());
-
- try (BufferedSink sink = Okio.a(Okio.b(file))) {
- sink.a(response.a().f());
- }
-
- parse(IOUtils.readAllLines(file), outDir, callback, track, cache);
- file.delete();
+ try {
+ Request request = new Request.a().b(track.D).a();
+ String response = client.a(request).execute().a().g();
+ parse(response, outDir, callback, track, cache);
} catch (IOException e) {
e.printStackTrace();
+ callback.onFailure();
}
}
diff --git a/app/src/main/java/ru/vtosters/lite/ssfs/UsersList.java b/app/src/main/java/ru/vtosters/lite/ssfs/UsersList.java
index 3f3252127..030d3b646 100644
--- a/app/src/main/java/ru/vtosters/lite/ssfs/UsersList.java
+++ b/app/src/main/java/ru/vtosters/lite/ssfs/UsersList.java
@@ -1,20 +1,18 @@
package ru.vtosters.lite.ssfs;
import android.util.Log;
+import okhttp3.Call;
+import okhttp3.Callback;
import okhttp3.Request;
import okhttp3.Response;
-import okio.BufferedSink;
-import okio.Okio;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils;
-import ru.vtosters.lite.utils.IOUtils;
import ru.vtosters.lite.utils.NetworkUtils;
-import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -37,18 +35,21 @@ public class UsersList {
.b(Utils.getDomain() + "/api/getUsersWithServiceDescriptionsAndBanners")
.a();
- try (Response response = VtOkHttpClient.getInstance().a(request).execute()) {
- File file = new File(AndroidUtils.getGlobalContext().getCacheDir(), "response" + "_user_list" + ".json");
-
- try (BufferedSink sink = Okio.a(Okio.b(file))) {
- sink.a(response.a().f());
+ VtOkHttpClient.getInstance().a(request).a(new Callback() {
+ @Override
+ public void a(Call call, IOException e) {
+ e.printStackTrace();
}
- parseJson(new JSONObject(IOUtils.readAllLines(file)).getJSONObject("response"));
- file.delete();
- } catch (IOException | JSONException e) {
- e.printStackTrace();
- }
+ @Override
+ public void a(Call call, Response response) {
+ try {
+ parseJson(new JSONObject(response.a().g()).getJSONObject("response"));
+ } catch (JSONException | IOException e) {
+ e.printStackTrace();
+ }
+ }
+ });
}
public static boolean hasDescription(int id) {
diff --git a/app/src/main/java/ru/vtosters/lite/utils/VTVerifications.java b/app/src/main/java/ru/vtosters/lite/utils/VTVerifications.java
index b50487eb9..6bed0005c 100644
--- a/app/src/main/java/ru/vtosters/lite/utils/VTVerifications.java
+++ b/app/src/main/java/ru/vtosters/lite/utils/VTVerifications.java
@@ -52,7 +52,6 @@ public class VTVerifications {
prefs.edit()
.putString("ids", r)
.apply();
- file.delete();
} catch (IOException e) {
if (prefs.contains("ids")) parseJson(prefs.getString("ids", "[]"));
Log.d("VTVerifications", e.getMessage());
From 2e3b45a88ffc06678f08d8f4f7eb2699c1fc1afa Mon Sep 17 00:00:00 2001
From: gdlbo <41114720+gdlbo@users.noreply.github.com>
Date: Sun, 14 May 2023 17:30:14 +0300
Subject: [PATCH 017/125] Revert "Multithreading verifs downloading"
This reverts commit 0e94efc4f7fa6c563cf97e8bbb5ebd64c52c3cb6.
---
.../vtosters/lite/utils/VTVerifications.java | 49 ++++++++++---------
1 file changed, 26 insertions(+), 23 deletions(-)
diff --git a/app/src/main/java/ru/vtosters/lite/utils/VTVerifications.java b/app/src/main/java/ru/vtosters/lite/utils/VTVerifications.java
index 6bed0005c..2ed647272 100644
--- a/app/src/main/java/ru/vtosters/lite/utils/VTVerifications.java
+++ b/app/src/main/java/ru/vtosters/lite/utils/VTVerifications.java
@@ -5,19 +5,19 @@ import android.util.Log;
import com.vk.dto.common.VerifyInfo;
import com.vk.navigation.NavigatorKeys;
import okhttp3.*;
-import okio.BufferedSink;
-import okio.Okio;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
-import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
+import static ru.vtosters.lite.utils.Preferences.getBoolValue;
+import static ru.vtosters.lite.utils.Preferences.hasVerification;
+
public class VTVerifications {
public static final List sVerifications = new ArrayList<>();
public static final List sPrometheuses = new ArrayList<>();
@@ -28,7 +28,7 @@ public class VTVerifications {
public static void load(Context context) {
var prefs = context.getSharedPreferences("vt_another_data", 0);
- if ((!NetworkUtils.isNetworkConnected() && NetworkUtils.isInternetSlow() || Preferences.getBoolValue("isRoamingState", false)) && prefs.contains("ids")) {
+ if ((!NetworkUtils.isNetworkConnected() && NetworkUtils.isInternetSlow() || getBoolValue("isRoamingState", false)) && prefs.contains("ids")) {
parseJson(prefs.getString("ids", "[]"));
return;
}
@@ -38,24 +38,27 @@ public class VTVerifications {
.a(RequestBody.a(MediaType.b("application/json; charset=UTF-8"), "{\"types\":[0,228,404,1337]}"))
.a();
+ sClient.a(request).a(new Callback() {
- try (Response response = sClient.a(request).execute()) {
- File file = new File(AndroidUtils.getGlobalContext().getCacheDir(), "response.json");
-
- try (BufferedSink sink = Okio.a(Okio.b(file))) {
- sink.a(response.a().f());
+ @Override
+ public void a(Call call, Response response) {
+ try {
+ var payload = response.a().g();
+ parseJson(payload);
+ prefs.edit()
+ .putString("ids", payload)
+ .apply();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
}
- String r = IOUtils.readAllLines(file);
+ @Override
+ public void a(Call call, IOException e) {
+ Log.d("VTVerifications", String.valueOf(e));
+ }
- parseJson(r);
- prefs.edit()
- .putString("ids", r)
- .apply();
- } catch (IOException e) {
- if (prefs.contains("ids")) parseJson(prefs.getString("ids", "[]"));
- Log.d("VTVerifications", e.getMessage());
- }
+ });
}
/**
@@ -101,7 +104,7 @@ public class VTVerifications {
}
public static boolean vtverif() {
- return Preferences.getBoolValue("VT_Verification", true);
+ return getBoolValue("VT_Verification", true);
}
private static int getId(JSONObject json) {
@@ -118,7 +121,7 @@ public class VTVerifications {
return true;
}
- if (!Preferences.getBoolValue("VT_Verification", true)) {
+ if (!getBoolValue("VT_Verification", true)) {
return false;
}
@@ -130,7 +133,7 @@ public class VTVerifications {
return true;
}
- if (!Preferences.getBoolValue("VT_Fire", true)) {
+ if (!getBoolValue("VT_Fire", true)) {
return false;
}
@@ -138,7 +141,7 @@ public class VTVerifications {
}
public static boolean hasDeveloper(JSONObject jSONObject) {
- if (!Preferences.getBoolValue("VT_Dev", true)) {
+ if (!getBoolValue("VT_Dev", true)) {
return false;
}
@@ -150,6 +153,6 @@ public class VTVerifications {
}
public static boolean haveDonateButton() {
- return Preferences.hasVerification() || new Random().nextInt(6) != 1;
+ return hasVerification() || new Random().nextInt(6) != 1;
}
}
From 0deafb560678a0bb09c8350c625e3c72ff92cf2a Mon Sep 17 00:00:00 2001
From: Denis <77270222+ayonovdenizs@users.noreply.github.com>
Date: Mon, 15 May 2023 15:19:27 +0600
Subject: [PATCH 018/125] Update features.md - editing the list of functions
(#1297)
---
.github/features.md | 32 +++++++++++++++++---------------
1 file changed, 17 insertions(+), 15 deletions(-)
diff --git a/.github/features.md b/.github/features.md
index 4b8122f7f..cd3d6a7b5 100644
--- a/.github/features.md
+++ b/.github/features.md
@@ -1,10 +1,13 @@
### Весь функционал модификации
# Реклама
- * Блокировка рекламы в ленте
- * Блокировка историй с возможной рекламой
- * Блокировка постов через фильтры
- * Возможность добавлять в вайтлист фильтров группы, чтобы в них не удалялись посты
+ * Блокировка рекламы в ленте +
+ * Блокировка историй с возможной рекламой +
+ * Блокировка постов:
+ * С источником
+ * С MiniApp
+ * Через фильтры групп, слов
+ * Возможность добавлять в вайтлист фильтров группы, чтобы в них не удалялись посты+
* Возможность отключить рекомендации от ВК
* Отключение отправки метрики
@@ -31,7 +34,7 @@
* Редактор элементов Superapp
* Возможность отключать новый дизайн версий VK (Milkshake)
* Название заголовков в докбаре
- * Возможность использовать акцент для иконок
+ * Возможность использовать свой акцент для иконок
* Возможность отключить счётчики в докбаре
* Смена иконки приложения (содержит много уникальных иконок для донатеров)
* Возможность покрасить панель навигации, веб-страницы
@@ -43,42 +46,41 @@
* Встроенный переводчик текста для сообщений
* Альтернативный дизайн эмодзи меню
* Возможность использовать системные эмодзи
- * Возможнсть использовать Enter с аппаратной клавиатуры
* Панель ввода из приложения VK для iOS
- * Сокращение постов **(Показать полностью)**
+ * Сокращение постов **(Показать полностью)**
+ * Результаты опросов без участия
# Медиаконтент
- * Результаты опросов без участия
* Интеграция с LAST.FM (а также возможность скробблинга)
* Возможность **кешировать музыку / автокешировать ее при прослушивании**
* Возможность выбирать раздел музыки, открываемый по умолчанию
* Возможность скачивать видео, очищать историю просмотра видео
- * Возможность найти фотографию через поисковые системы
+ * Возможность найти фотографию через поисковые системы ?
* Интеграция с VK X
- * Возможность кешировать треки в приложении
- * Поддержка новых текстов в музыке (и с Genius)
+ * Поддержка новых текстов в музыке (и с Genius в том числе)
# Сообщения
* Шифрование сообщений
* Возможность видеть удаленные сообщения
* Нечиталка/неписалка для ЛС/бесед/ботов/групп
* Возможность загружать свои стикерпаки из Telegram во ВКонтакте
+ * Возможнсть использовать Enter с аппаратной клавиатуры
# Активность
- * Функционал, позволяющий оставаться в «оффлайне» (принудительный оффлайн/не отправлять статус онлайна и т.п.)
+ * Возможность сохранять удалённые сообщения
+ * Функционал, позволяющий оставаться в «оффлайне» (принудительный оффлайн/не отправлять статус онлайна и т.п.)
* Возможность отключить информацию о том, что вы просмотрели историю/прослушали голосовое сообщение
* Возможность видеть онлайн пользователей, если у вас скрыт онлайн через VK Messenger
* Возможность сохранять настройки бесшумных сообщений/сообщений-бомбочек
* Отключить уведомление о том, что вы сделали скриншот в беседе
* Отключение редиректа через away.php
+ * Отключение бизнес-уведомлений
# Прокси
* Поддержка сторонних прокси, таких как: Zaborona, VikaMobile, рандомные прокси и прочие
* Возможность использовать **HTTP/HTTPS/SOCKS** прокси, менять домены **API/OAuth/Static**
# Другое
- * Синхронизация контактов с VK
- * Очистка кеша приложения (**всё/стикеры/изображения/видео/сообщения/WebView**)
* Автоочистка кеша при достижении указанного вами значения (**100/500 MB, 1/2/5 GB**)
- * Возможность создания резервной копии (бекапа) настроек и аккаунтов
\ No newline at end of file
+ * Возможность создания резервной копии (бекапа) настроек и аккаунтов
From 594f24d64016005ad58176e2bca9ce57a38751f1 Mon Sep 17 00:00:00 2001
From: gdlbo <41114720+gdlbo@users.noreply.github.com>
Date: Mon, 15 May 2023 21:28:45 +0300
Subject: [PATCH 019/125] Refactored issues with photo compression (#1300)
Added new ways of compression
Added photo copy button
---
.../com/vk/medianative/MediaImageEncoder.java | 93 ++++
app/src/main/java/com/vtosters/lite/R.java | 156 +++++--
.../lite/downloaders/Multithreading.java | 84 ----
.../ru/vtosters/lite/hooks/PhotoViewer.java | 138 ++++--
.../ru/vtosters/lite/hooks/TogglesHook.java | 5 +-
.../lite/ui/fragments/VTSettings.java | 3 +-
.../ru/vtosters/lite/utils/Preferences.java | 5 +-
smali/res/menu/photo_viewer.xml | 4 +
smali/res/values-ru/arrays.xml | 5 +
smali/res/values/arrays.xml | 10 +
smali/res/values/ids.xml | 1 +
smali/res/xml/preferences_media.xml | 12 +-
.../vk/medianative/MediaImageEncoder.smali | 432 ------------------
...EngineProvider$createVkAppConfig$6$1.smali | 19 +-
.../im/converters/ImPhotoConverter$a.smali | 4 +-
.../upload/UploadCompressStrategy$a2.smali | 4 +-
.../lite/upload/l/PhotoUploadTask.smali | 11 +-
17 files changed, 342 insertions(+), 644 deletions(-)
create mode 100644 app/src/main/java/com/vk/medianative/MediaImageEncoder.java
delete mode 100644 app/src/main/java/ru/vtosters/lite/downloaders/Multithreading.java
delete mode 100644 smali/smali_classes3/com/vk/medianative/MediaImageEncoder.smali
diff --git a/app/src/main/java/com/vk/medianative/MediaImageEncoder.java b/app/src/main/java/com/vk/medianative/MediaImageEncoder.java
new file mode 100644
index 000000000..7bd6ec433
--- /dev/null
+++ b/app/src/main/java/com/vk/medianative/MediaImageEncoder.java
@@ -0,0 +1,93 @@
+package com.vk.medianative;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.os.Build;
+import android.util.Log;
+import ru.vtosters.lite.utils.Preferences;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class MediaImageEncoder {
+ private static boolean compressBitmap(Bitmap bitmap, File file, CompressFormat format, int quality) {
+ try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
+ return bitmap.compress(format, quality, fileOutputStream);
+ } catch (IOException e) {
+ Log.e(MediaImageEncoder.class.getSimpleName(), "Bitmap compression error: " + e);
+ return false;
+ }
+ }
+
+ private static boolean compressBitmapJpeg(Bitmap bitmap, File file, int quality) {
+ return compressBitmap(bitmap, file, CompressFormat.JPEG, quality);
+ }
+
+ private static boolean compressBitmapLossless(Bitmap bitmap, File file) {
+ return compressBitmap(bitmap, file, CompressFormat.PNG, 100);
+ }
+
+ private static boolean compressBitmapNative(Bitmap bitmap, File file, int quality) {
+ if (MediaNative.isX86() || MediaNative.isAsus() || Build.VERSION.SDK_INT < 24) {
+ Log.e(MediaImageEncoder.class.getSimpleName(), "JPEG turbo not supported on this device!");
+ return compressBitmapJpeg(bitmap, file, quality);
+ }
+
+ byte[] jpegData = MediaNative.compressBitmapJpeg(bitmap, quality);
+
+ if (jpegData == null) {
+ return false;
+ }
+
+ try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
+ fileOutputStream.write(jpegData);
+ return true;
+ } catch (IOException e) {
+ Log.e(MediaImageEncoder.class.getSimpleName(), "JPEG compression error: " + e);
+ return false;
+ }
+ }
+
+ public static boolean encodePicture(Bitmap bitmap, File file, int quality) {
+ if (bitmap != null && !bitmap.isRecycled() && bitmap.getWidth() * bitmap.getHeight() != 0) {
+ switch (Preferences.getPreferences().getString("compression", "jpeg")) {
+ case "skip" -> {
+ return compressBitmapLossless(bitmap, file);
+ }
+ case "jpeg" -> {
+ return compressBitmapJpeg(bitmap, file, quality);
+ }
+ case "native" -> {
+ return compressBitmapNative(bitmap, file, quality);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public static boolean needToSkipCompression() {
+ return Preferences.getPreferences().getString("compression", "jpeg").equals("skip");
+ }
+
+ public static boolean needToCompress() {
+ return !needToSkipCompression();
+ }
+
+ public static boolean encodeJpeg(Bitmap bitmap, File file) {
+ return encodePicture(bitmap, file, 90);
+ }
+
+ public static boolean encodeJpegWithoutCompression(Bitmap bitmap, File file) {
+ return encodePicture(bitmap, file, 95);
+ }
+
+ public static boolean encodeJpeg(Bitmap bitmap, File file, int quality) {
+ if (bitmap != null && !bitmap.isRecycled() && bitmap.getWidth() * bitmap.getHeight() != 0) {
+ return needToCompress() ? compressBitmapNative(bitmap, file, quality) : compressBitmapJpeg(bitmap, file, quality);
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/app/src/main/java/com/vtosters/lite/R.java b/app/src/main/java/com/vtosters/lite/R.java
index d39ab7a9a..7ba2b6a35 100644
--- a/app/src/main/java/com/vtosters/lite/R.java
+++ b/app/src/main/java/com/vtosters/lite/R.java
@@ -222,8 +222,10 @@ public final class R {
public static final int undel_value = 0x7f030075; // 2130903157
public static final int wallpaper_change_dialog = 0x7f030076; // 2130903158
public static final int accent_select_type = 0x7f030077; // 2130903159
- public static final int theme_type_name = 0x7f030078; // 2130903160
- public static final int theme_type_name_checkbox = 0x7f030079; // 2130903161
+ public static final int compression = 0x7f030078; // 2130903160
+ public static final int compression_value = 0x7f030079; // 2130903161
+ public static final int theme_type_name = 0x7f03007a; // 2130903162
+ public static final int theme_type_name_checkbox = 0x7f03007b; // 2130903163
}
public static final class attr {
@@ -4995,7 +4997,9 @@ public final class R {
public static final int $splash__8 = 0x7f080be6; // 2131233766
public static final int $splash__9 = 0x7f080be7; // 2131233767
public static final int checker_background = 0x7f080be8; // 2131233768
- public static final int splash = 0x7f080be9; // 2131233769
+ public static final int custom_thumb_selector = 0x7f080be9; // 2131233769
+ public static final int custom_track_selector = 0x7f080bea; // 2131233770
+ public static final int splash = 0x7f080beb; // 2131233771
public static final int abc_control_background_material = 0x7f080032; // 2131230770
public static final int ic_action_close = 0x7f080322; // 2131231522
public static final int ic_camera_outline_28_new = 0x7f0803a0; // 2131231648
@@ -11054,6 +11058,8 @@ public final class R {
public static final int color_name = 0x7f0a1022; // 2131365922
public static final int color_value = 0x7f0a1023; // 2131365923
public static final int color_preview_border = 0x7f0a1024; // 2131365924
+ public static final int account_backup_view = 0x7f0a1025; // 2131365925
+ public static final int copy_image = 0x7f0a1026; // 2131365926
}
public static final class integer {
@@ -12993,6 +12999,30 @@ public final class R {
public static final int vt_changeable_foreground = 0x7f0f0044; // 2131689540
public static final int vt_launcher = 0x7f0f0045; // 2131689541
public static final int vt_party_foreground = 0x7f0f0047; // 2131689543
+ public static final int ic_launcher_balloon_pink = 0x7f0f0048; // 2131689544
+ public static final int ic_launcher_balloon_pink_background = 0x7f0f0049; // 2131689545
+ public static final int ic_launcher_balloon_pink_foreground = 0x7f0f004a; // 2131689546
+ public static final int ic_launcher_bubble_gum = 0x7f0f004b; // 2131689547
+ public static final int ic_launcher_bubble_gum_background = 0x7f0f004c; // 2131689548
+ public static final int ic_launcher_bubble_gum_foreground = 0x7f0f004d; // 2131689549
+ public static final int ic_launcher_comics = 0x7f0f004e; // 2131689550
+ public static final int ic_launcher_comics_background = 0x7f0f004f; // 2131689551
+ public static final int ic_launcher_comics_foreground = 0x7f0f0050; // 2131689552
+ public static final int ic_launcher_flowers = 0x7f0f0051; // 2131689553
+ public static final int ic_launcher_flowers_background = 0x7f0f0052; // 2131689554
+ public static final int ic_launcher_flowers_foreground = 0x7f0f0053; // 2131689555
+ public static final int ic_launcher_metal_dark = 0x7f0f0054; // 2131689556
+ public static final int ic_launcher_metal_dark_background = 0x7f0f0055; // 2131689557
+ public static final int ic_launcher_metal_dark_foreground = 0x7f0f0056; // 2131689558
+ public static final int ic_launcher_pin_blue = 0x7f0f0057; // 2131689559
+ public static final int ic_launcher_pin_blue_background = 0x7f0f0058; // 2131689560
+ public static final int ic_launcher_pin_blue_foreground = 0x7f0f0059; // 2131689561
+ public static final int ic_launcher_rabbit = 0x7f0f005a; // 2131689562
+ public static final int ic_launcher_rabbit_background = 0x7f0f005b; // 2131689563
+ public static final int ic_launcher_rabbit_foreground = 0x7f0f005c; // 2131689564
+ public static final int ic_launcher_shine = 0x7f0f005d; // 2131689565
+ public static final int ic_launcher_shine_background = 0x7f0f005e; // 2131689566
+ public static final int ic_launcher_shine_foreground = 0x7f0f005f; // 2131689567
public static final int vt_adaptive_launcher_foneground = 0x7f0f0043; // 2131689539
public static final int vt_launcher_round = 0x7f0f0046; // 2131689542
public static final int ic_logo_facebook_28 = 0x7f0f0003; // 2131689475
@@ -19547,49 +19577,82 @@ public final class R {
public static final int accent_color_summ = 0x7f12183b; // 2131892283
public static final int accent_color_title = 0x7f12183c; // 2131892284
public static final int accents = 0x7f12183d; // 2131892285
- public static final int add_to_ads_stories_whitelist = 0x7f12183e; // 2131892286
- public static final int amoledtheme_summ = 0x7f12183f; // 2131892287
- public static final int amoledtheme_title = 0x7f121840; // 2131892288
- public static final int app_open_by_default_settings = 0x7f121841; // 2131892289
- public static final int audio_deleted_from_cache = 0x7f121842; // 2131892290
- public static final int blockminiapps_summ = 0x7f121843; // 2131892291
- public static final int blockminiapps_title = 0x7f121844; // 2131892292
- public static final int businessDisabler_title = 0x7f121845; // 2131892293
- public static final int by_choice = 0x7f121846; // 2131892294
- public static final int calls_not_available = 0x7f121847; // 2131892295
- public static final int cannot_write = 0x7f121848; // 2131892296
- public static final int change_accent_color = 0x7f121849; // 2131892297
- public static final int copydebuginfo_summ = 0x7f12184a; // 2131892298
- public static final int copydebuginfo_title = 0x7f12184b; // 2131892299
- public static final int copyownlink_summ = 0x7f12184c; // 2131892300
- public static final int copyownlink_title = 0x7f12184d; // 2131892301
- public static final int crash_service_name = 0x7f12184e; // 2131892302
- public static final int current_theme = 0x7f12184f; // 2131892303
- public static final int custom_links_warning = 0x7f121850; // 2131892304
- public static final int data_updated = 0x7f121851; // 2131892305
- public static final int device_info = 0x7f121852; // 2131892306
- public static final int device_info_copied = 0x7f121853; // 2131892307
- public static final int dialogstats = 0x7f121854; // 2131892308
- public static final int downloading_update = 0x7f121855; // 2131892309
- public static final int invalid_pack_link = 0x7f121856; // 2131892310
- public static final int maxquality_summ = 0x7f121857; // 2131892311
- public static final int maxquality_title = 0x7f121858; // 2131892312
- public static final int monettheme_summ = 0x7f121859; // 2131892313
- public static final int monettheme_title = 0x7f12185a; // 2131892314
- public static final int newsfeed_notif_summ = 0x7f12185b; // 2131892315
- public static final int newsfeed_notif_title = 0x7f12185c; // 2131892316
- public static final int open_bot = 0x7f12185d; // 2131892317
- public static final int promotedstickers_title = 0x7f12185e; // 2131892318
- public static final int remove_from_ads_stories_whitelist = 0x7f12185f; // 2131892319
- public static final int select_color = 0x7f121860; // 2131892320
- public static final int select_palette = 0x7f121861; // 2131892321
- public static final int sources = 0x7f121862; // 2131892322
- public static final int updateverifdata_summ = 0x7f121863; // 2131892323
- public static final int updateverifdata_title = 0x7f121864; // 2131892324
- public static final int useOldAppVer_summ = 0x7f121865; // 2131892325
- public static final int useOldAppVer_title = 0x7f121866; // 2131892326
- public static final int vkx_integration_enabled_info = 0x7f121867; // 2131892327
- public static final int writebar_ios_title = 0x7f121868; // 2131892328
+ public static final int accounts_saved_by_path = 0x7f12183e; // 2131892286
+ public static final int add_to_ads_stories_whitelist = 0x7f12183f; // 2131892287
+ public static final int amoledtheme_summ = 0x7f121840; // 2131892288
+ public static final int amoledtheme_title = 0x7f121841; // 2131892289
+ public static final int appLanguage = 0x7f121842; // 2131892290
+ public static final int app_open_by_default_settings = 0x7f121843; // 2131892291
+ public static final int applying_accent = 0x7f121844; // 2131892292
+ public static final int audio_deleted_from_cache = 0x7f121845; // 2131892293
+ public static final int blockminiapps_summ = 0x7f121846; // 2131892294
+ public static final int blockminiapps_title = 0x7f121847; // 2131892295
+ public static final int businessDisabler_title = 0x7f121848; // 2131892296
+ public static final int by_choice = 0x7f121849; // 2131892297
+ public static final int calls_not_available = 0x7f12184a; // 2131892298
+ public static final int cannot_write = 0x7f12184b; // 2131892299
+ public static final int change_accent_color = 0x7f12184c; // 2131892300
+ public static final int copydebuginfo_summ = 0x7f12184d; // 2131892301
+ public static final int copydebuginfo_title = 0x7f12184e; // 2131892302
+ public static final int copyownlink_summ = 0x7f12184f; // 2131892303
+ public static final int copyownlink_title = 0x7f121850; // 2131892304
+ public static final int crash_service_name = 0x7f121851; // 2131892305
+ public static final int currentLanguage = 0x7f121852; // 2131892306
+ public static final int current_theme = 0x7f121853; // 2131892307
+ public static final int custom_links_warning = 0x7f121854; // 2131892308
+ public static final int data = 0x7f121855; // 2131892309
+ public static final int data_updated = 0x7f121856; // 2131892310
+ public static final int datasettings_title = 0x7f121857; // 2131892311
+ public static final int device_info = 0x7f121858; // 2131892312
+ public static final int device_info_copied = 0x7f121859; // 2131892313
+ public static final int dialogstats = 0x7f12185a; // 2131892314
+ public static final int disable_analytics = 0x7f12185b; // 2131892315
+ public static final int disable_analytics_summ = 0x7f12185c; // 2131892316
+ public static final int downloading_update = 0x7f12185d; // 2131892317
+ public static final int error_applying_accent = 0x7f12185e; // 2131892318
+ public static final int error_no_text = 0x7f12185f; // 2131892319
+ public static final int error_saving_file = 0x7f121860; // 2131892320
+ public static final int failed_to_get_text = 0x7f121861; // 2131892321
+ public static final int invalid_pack_link = 0x7f121862; // 2131892322
+ public static final int invalidate_theme_cache_title = 0x7f121863; // 2131892323
+ public static final int logColors_title = 0x7f121864; // 2131892324
+ public static final int maxquality_summ = 0x7f121865; // 2131892325
+ public static final int maxquality_title = 0x7f121866; // 2131892326
+ public static final int monettheme_summ = 0x7f121867; // 2131892327
+ public static final int monettheme_title = 0x7f121868; // 2131892328
+ public static final int newsfeed_notif_summ = 0x7f121869; // 2131892329
+ public static final int newsfeed_notif_title = 0x7f12186a; // 2131892330
+ public static final int open_bot = 0x7f12186b; // 2131892331
+ public static final int promotedstickers_title = 0x7f12186c; // 2131892332
+ public static final int remove_encryption_key = 0x7f12186d; // 2131892333
+ public static final int remove_from_ads_stories_whitelist = 0x7f12186e; // 2131892334
+ public static final int reset_accounts = 0x7f12186f; // 2131892335
+ public static final int reset_all_settings = 0x7f121870; // 2131892336
+ public static final int restore_accounts = 0x7f121871; // 2131892337
+ public static final int restore_settings = 0x7f121872; // 2131892338
+ public static final int save_accounts = 0x7f121873; // 2131892339
+ public static final int save_online_data = 0x7f121874; // 2131892340
+ public static final int select_color = 0x7f121875; // 2131892341
+ public static final int select_palette = 0x7f121876; // 2131892342
+ public static final int sources = 0x7f121877; // 2131892343
+ public static final int storiesGroupsAdBlock = 0x7f121878; // 2131892344
+ public static final int storiesGroupsAdBlocksummary = 0x7f121879; // 2131892345
+ public static final int systemtheme = 0x7f12187a; // 2131892346
+ public static final int systemtheme_enabled = 0x7f12187b; // 2131892347
+ public static final int systemtheme_summ = 0x7f12187c; // 2131892348
+ public static final int updateverifdata_summ = 0x7f12187d; // 2131892349
+ public static final int updateverifdata_title = 0x7f12187e; // 2131892350
+ public static final int useCustomPrefsStyle_summ = 0x7f12187f; // 2131892351
+ public static final int useCustomPrefsStyle_title = 0x7f121880; // 2131892352
+ public static final int useGenius_summ = 0x7f121881; // 2131892353
+ public static final int useGenius_title = 0x7f121882; // 2131892354
+ public static final int useNewColorEngine_summ = 0x7f121883; // 2131892355
+ public static final int useNewColorEngine_title = 0x7f121884; // 2131892356
+ public static final int useOldAppVer_summ = 0x7f121885; // 2131892357
+ public static final int useOldAppVer_title = 0x7f121886; // 2131892358
+ public static final int vkx_integration_enabled_info = 0x7f121887; // 2131892359
+ public static final int whitelisted_ad_groups_stories_title = 0x7f121888; // 2131892360
+ public static final int writebar_ios_title = 0x7f121889; // 2131892361
}
public static final class style {
@@ -20786,5 +20849,6 @@ public final class R {
public static final int preferences_proxy = 0x7f15001c; // 2132082716
public static final int preferences_tgs = 0x7f15001d; // 2132082717
public static final int preferences_themes = 0x7f15001e; // 2132082718
+ public static final int locales_config = 0x7f15001f; // 2132082719
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ru/vtosters/lite/downloaders/Multithreading.java b/app/src/main/java/ru/vtosters/lite/downloaders/Multithreading.java
deleted file mode 100644
index f07e97f5d..000000000
--- a/app/src/main/java/ru/vtosters/lite/downloaders/Multithreading.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package ru.vtosters.lite.downloaders;
-
-import android.util.Log;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.RandomAccessFile;
-import java.net.HttpURLConnection;
-import java.net.URL;
-
-public class Multithreading {
- public static void download(URL url, File f, int threadCount) {
- try {
- HttpURLConnection connection = (HttpURLConnection) url.openConnection();
- connection.setRequestMethod("GET");
- int responseCode = connection.getResponseCode();
- if (responseCode == HttpURLConnection.HTTP_OK) {
- int fileSize = connection.getContentLength();
- connection.disconnect();
- RandomAccessFile file = new RandomAccessFile(f, "rw");
- file.setLength(fileSize);
- file.close();
- if (threadCount == 0) threadCount = 4;
- int partSize = fileSize / threadCount;
- DownloadThread[] threads = new DownloadThread[threadCount];
- for (int i = 0; i < threadCount; i++) {
- int startByte = i * partSize;
- int endByte = (i + 1) * partSize - 1;
- if (i == threadCount - 1) {
- endByte = fileSize - 1;
- }
- DownloadThread thread = new DownloadThread(i, url, f.getName(), startByte, endByte);
- thread.start();
- threads[i] = thread;
- }
- for (DownloadThread thread : threads) {
- thread.join();
- }
- } else {
- Log.d("Multithreading", "Error, code: " + responseCode);
- }
- } catch (IOException | InterruptedException e) {
- e.printStackTrace();
- }
- }
-
- static class DownloadThread extends Thread {
- private final int threadId;
- private final URL url;
- private final String fileName;
- private final int startByte;
- private final int endByte;
-
- public DownloadThread(int threadId, URL url, String fileName, int startByte, int endByte) {
- this.threadId = threadId;
- this.url = url;
- this.fileName = fileName;
- this.startByte = startByte;
- this.endByte = endByte;
- }
-
- @Override
- public void run() {
- try {
- HttpURLConnection connection = (HttpURLConnection) url.openConnection();
- connection.setRequestProperty("Range", "bytes=" + startByte + "-" + endByte);
- InputStream input = connection.getInputStream();
- RandomAccessFile file = new RandomAccessFile(fileName, "rw");
- file.seek(startByte);
- byte[] buffer = new byte[1024];
- int length = input.read(buffer);
- while (length != -1) {
- file.write(buffer, 0, length);
- length = input.read(buffer);
- }
- file.close();
- input.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-}
diff --git a/app/src/main/java/ru/vtosters/lite/hooks/PhotoViewer.java b/app/src/main/java/ru/vtosters/lite/hooks/PhotoViewer.java
index c896fb9dc..cb61c4b9d 100644
--- a/app/src/main/java/ru/vtosters/lite/hooks/PhotoViewer.java
+++ b/app/src/main/java/ru/vtosters/lite/hooks/PhotoViewer.java
@@ -12,6 +12,7 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.Nullable;
+import androidx.core.content.FileProvider;
import com.vk.core.dialogs.actionspopup.ActionsPopup;
import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vk.core.ui.themes.VKThemeHelper;
@@ -23,6 +24,8 @@ import com.vtosters.lite.R;
import com.vtosters.lite.attachments.DocumentAttachment;
import com.vtosters.lite.attachments.PhotoAttachment;
import okhttp3.*;
+import okio.BufferedSink;
+import okio.Okio;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
@@ -30,6 +33,7 @@ import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.LifecycleUtils;
import ru.vtosters.lite.utils.Preferences;
+import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;
@@ -37,58 +41,83 @@ import java.util.List;
public class PhotoViewer {
static OkHttpClient client = VtOkHttpClient.getInstance();
+ @SuppressLint("NonConstantResourceId")
public static boolean interceptClick(AttachmentWithMedia attachment, MenuItem item, View view) {
- if (item.getItemId() == R.id.search_photo) {
- if (attachment instanceof PhotoAttachment) {
- searchDialog(LifecycleUtils.getCurrentActivity(), getUrlFromPhotoAttachment((PhotoAttachment) attachment));
- } else if (attachment instanceof DocumentAttachment) {
- var documentAttachment = (DocumentAttachment) attachment;
- if (documentAttachment.J != null)
- try {
- searchDialog(LifecycleUtils.getCurrentActivity(), getUrlFromDocumentAttachment(documentAttachment));
- } catch (Exception e) {
+ switch (item.getItemId()) {
+ case R.id.copy_image -> {
+ if (attachment instanceof PhotoAttachment) {
+ copyImage(getUrlFromPhotoAttachment((PhotoAttachment) attachment), String.valueOf(attachment.getId()));
+ } else if (attachment instanceof DocumentAttachment) {
+ var documentAttachment = (DocumentAttachment) attachment;
+ if (documentAttachment.J != null)
+ try {
+ copyImage(getUrlFromDocumentAttachment(documentAttachment), String.valueOf(attachment.getId()));
+ } catch (Exception e) {
+ ToastUtils.a(R.string.photo_get_error);
+ }
+ else
ToastUtils.a(R.string.photo_get_error);
- }
- else
- ToastUtils.a(R.string.photo_get_error);
+ }
+ return true;
}
- return true;
- } else if (item.getItemId() == R.id.copy_photo_url) {
- if (attachment instanceof PhotoAttachment) {
- copyPhotoUrl(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
- } else if (attachment instanceof DocumentAttachment) {
- var documentAttachment = (DocumentAttachment) attachment;
- if (documentAttachment.J != null)
- try {
- copyPhotoUrl(getUrlFromDocumentAttachment(documentAttachment));
- } catch (Exception e) {
+ case R.id.copy_photo_url -> {
+ if (attachment instanceof PhotoAttachment) {
+ copyPhotoUrl(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
+ } else if (attachment instanceof DocumentAttachment) {
+ var documentAttachment = (DocumentAttachment) attachment;
+ if (documentAttachment.J != null)
+ try {
+ copyPhotoUrl(getUrlFromDocumentAttachment(documentAttachment));
+ } catch (Exception e) {
+ ToastUtils.a(R.string.photo_get_error);
+ }
+ else
ToastUtils.a(R.string.photo_get_error);
- }
- else
- ToastUtils.a(R.string.photo_get_error);
+ }
+ return true;
}
- return true;
- } else if (item.getItemId() == R.id.open_original_photo) {
- if (attachment instanceof PhotoAttachment) {
- openUrl(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
- } else if (attachment instanceof DocumentAttachment) {
- var documentAttachment = (DocumentAttachment) attachment;
- if (documentAttachment.J != null)
- try {
- openUrl(getUrlFromDocumentAttachment(documentAttachment));
- } catch (Exception e) {
+ case R.id.open_original_photo -> {
+ if (attachment instanceof PhotoAttachment) {
+ openUrl(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
+ } else if (attachment instanceof DocumentAttachment) {
+ var documentAttachment = (DocumentAttachment) attachment;
+ if (documentAttachment.J != null)
+ try {
+ openUrl(getUrlFromDocumentAttachment(documentAttachment));
+ } catch (Exception e) {
+ ToastUtils.a(R.string.photo_get_error);
+ }
+ else
ToastUtils.a(R.string.photo_get_error);
- }
- else
- ToastUtils.a(R.string.photo_get_error);
+ }
+ return true;
}
- return true;
}
return true;
}
@SuppressLint("UseCompatLoadingForDrawables")
public static void addMenuItems(AttachmentWithMedia attachment, ActionsPopup.b actionPopup, int i, boolean z) {
+ actionPopup.a(R.string.copy,
+ ContextExtKt.b(AndroidUtils.getGlobalContext(), R.drawable.ic_copy_outline_28, VKThemeHelper.g(R.attr.header_tint)),
+ false,
+ () -> {
+ if (attachment instanceof PhotoAttachment) {
+ copyImage(getUrlFromPhotoAttachment((PhotoAttachment) attachment), String.valueOf(attachment.getId()));
+ } else if (attachment instanceof DocumentAttachment) {
+ var documentAttachment = (DocumentAttachment) attachment;
+ if (documentAttachment.J != null)
+ try {
+ copyImage(getUrlFromDocumentAttachment(documentAttachment), String.valueOf(attachment.getId()));
+ } catch (Exception e) {
+ ToastUtils.a(R.string.photo_get_error);
+ }
+ else
+ ToastUtils.a(R.string.photo_get_error);
+ }
+ return null;
+ }
+ );
actionPopup.a(R.string.search_photo_content,
ContextExtKt.b(AndroidUtils.getGlobalContext(), R.drawable.ic_menu_search_outline_28, VKThemeHelper.g(R.attr.header_tint)),
false,
@@ -194,6 +223,37 @@ public class PhotoViewer {
}
}
+ private static void copyImage(String link, String name) {
+ Request request = new Request.a().b(link).a();
+ File file = new File(AndroidUtils.getGlobalContext().getExternalCacheDir(), name + ".jpg");
+
+ if (file.exists()) {
+ copyImageToClipboard(file);
+ return;
+ }
+
+ new Thread() {
+ @Override
+ public void run() {
+ try (Response response = client.a(request).execute()) {
+ try (BufferedSink sink = Okio.a(Okio.b(file))) {
+ sink.a(response.a().f());
+ }
+
+ copyImageToClipboard(file);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }.start();
+ }
+
+ private static void copyImageToClipboard(File file) {
+ Uri path = FileProvider.getUriForFile(AndroidUtils.getGlobalContext(), "com.vtosters.lite.common.VKFileProvider", file);
+ ClipData clip = ClipData.newUri(AndroidUtils.getGlobalContext().getContentResolver(), "Image", path);
+ ((ClipboardManager) AndroidUtils.getGlobalContext().getSystemService(Context.CLIPBOARD_SERVICE)).setPrimaryClip(clip);
+ }
+
private static int selectedEngine() {
return Preferences.getPreferences().getInt("search_engine", 0);
}
diff --git a/app/src/main/java/ru/vtosters/lite/hooks/TogglesHook.java b/app/src/main/java/ru/vtosters/lite/hooks/TogglesHook.java
index 2053a4401..35322822d 100644
--- a/app/src/main/java/ru/vtosters/lite/hooks/TogglesHook.java
+++ b/app/src/main/java/ru/vtosters/lite/hooks/TogglesHook.java
@@ -1,5 +1,6 @@
package ru.vtosters.lite.hooks;
+import com.vk.medianative.MediaImageEncoder;
import com.vk.toggle.FeatureManager;
import com.vk.toggle.Features;
@@ -8,7 +9,7 @@ import static ru.vtosters.lite.utils.Preferences.*;
public class TogglesHook {
public static boolean shouldPatch(Features.Type feature) {
return switch (feature) {
- case AB_IM_LONGPOLL_MSG_BATCHING, FEATURE_MOZJPEG, AB_IM_VIEW_POOL, AB_MULTI_GIFTS, AB_NEWS_VIDEO_LAYOUT_TEXT, AB_STICKERS_DISCOVER, AB_GIFTS_FROM_KEYBOARD, FEATURE_FEED_DEFAULT_LIST_ALWAYS, EXPERIMENT_NEWS_VIDEO_LAYOUT_TEXT, FEATURE_DISCOVER_CATEGORIES, FEATURE_FAVE_PERF_IMPROVE, FEATURE_GROUP_ADMIN_MESSAGES, FEATURE_GROUP_MSG_PUSH_PARAM, FEATURE_IMAGE_QUALITY_UPGRADE, FEATURE_IM_AUDIO_MSG_TRANSCRIPT, FEATURE_IM_CASPER_MSGS, FEATURE_IM_DISABLE_FORCE_OPEN_VK_ME, FEATURE_IM_GIF_AUTOPLAY, FEATURE_IM_MR_IN_VKAPP, FEATURE_MEMORIES_ENABLED, FEATURE_MENU_GAMES_CAROUSEL, FEATURE_MILKSHAKE, FEATURE_MILKSHAKE_CHANGE_FEED_BY_TIMEOUT, FEATURE_MILKSHAKE_NEWS_SCROLL_ON_BACK, FEATURE_MILKSHAKE_SWITCH_THEME_ON_TAP, FEATURE_ML_BRANDS, FEATURE_ML_FEATURES, FEATURE_ML_MODELS_LOADING, FEATURE_MONEY_TRANSFERS_VKPAY, FEATURE_MUSIC_ARTIST_CATALOG, FEATURE_MUSIC_NEW_CATALOG, FEATURE_NARRATIVE_SNIPPET_TYPE, FEATURE_NEWS_GAMES_IN_DISCOVER, FEATURE_NEWS_HEADER_SCROLL, FEATURE_NEW_FORMAT_SHOW_WPB, FEATURE_PODCASTS_PAGE, FEATURE_QR_CREATE_QR, FEATURE_QR_DYNAMIC_FRAME, FEATURE_QR_SCAN_FROM_PHOTO, FEATURE_QR_VISION_DECODER, FEATURE_QUEUE_COUNTERS, FEATURE_REEF, FEATURE_ROAMING_AUTOPLAY, FEATURE_SHARE_MSGS_ON_INVITE, FEATURE_STICKERS_BOT_LINK, FEATURE_STICKERS_NEW_CATALOG, FEATURE_STORIES_AVATAR, FEATURE_STORIES_FEED_TYPE, FEATURE_STORIES_SHOW_ALWAYS, FEATURE_STORY_ANSWER_PUBLIC, FEATURE_STORY_APP_STICKER, FEATURE_STORY_ARCHIVE, FEATURE_STORY_ARCHIVE_POST, FEATURE_STORY_BAKGROUND_ALL, FEATURE_STORY_BOX, FEATURE_STORY_CADRE, FEATURE_STORY_CAMERA_TOOLTIP, FEATURE_STORY_EDITOR_GALLERY, FEATURE_STORY_EDITOR_TYPE, FEATURE_STORY_ENDLESS_VIDEO, FEATURE_STORY_FAST_REACTIONS, FEATURE_STORY_GIF, FEATURE_STORY_GROUPED, FEATURE_STORY_HIGH_QUALITY, FEATURE_STORY_MASKS_FRONTAL, FEATURE_STORY_MENTION_ICON, FEATURE_STORY_MODERN_PUBLISH, FEATURE_STORY_MUSIC_EDITOR, FEATURE_STORY_MUSIC_REPLIES, FEATURE_STORY_MUSIC_REPLIES_2, FEATURE_STORY_NEW_FRAME, FEATURE_STORY_ONE_TIME, FEATURE_STORY_PHOTO_SHARING, FEATURE_STORY_PHOTO_STICKER, FEATURE_STORY_POLLS, FEATURE_STORY_POST_REPOST, FEATURE_STORY_PRELOADING, FEATURE_STORY_QUESTION, FEATURE_STORY_QUESTION_STYLE, FEATURE_STORY_RLOTTIE, FEATURE_STORY_RLOTTIE_CACHE, FEATURE_STORY_SAVE_DEFAULT_ON, FEATURE_STORY_SEND_DIALOG_LIST, FEATURE_STORY_STICKER_PACK, FEATURE_STORY_TIME, FEATURE_STORY_VIEWER_CAMERA, FEATURE_STORY_VOICE_ANSWER, FEATURE_STORY_WITH_CONTEXT, FEATURE_SUPERAPP_MENU, FEATURE_TRAFFIC_SAVER, FEATURE_VKPAY_WIDGET, FEATURE_VKUI_INTERNAL_TO_MINI_APPS, FEATURE_VKUI_TOKEN_CACHE, FEATURE_VK_APPS_SEARCH, FEATURE_VOTES_BALANCE, FEATURE_WEBVIEW_TOKEN_ACTIVE, FEATURE_MILKSHAKE_FORCE_DISABLED, FEATURE_MILKSHAKE_ACTIVATION_DISABLED, EXPERIMENT_NEWS_DISABLE_CACHE ->
+ case AB_IM_LONGPOLL_MSG_BATCHING, AB_IM_VIEW_POOL, AB_MULTI_GIFTS, AB_NEWS_VIDEO_LAYOUT_TEXT, AB_STICKERS_DISCOVER, AB_GIFTS_FROM_KEYBOARD, FEATURE_FEED_DEFAULT_LIST_ALWAYS, EXPERIMENT_NEWS_VIDEO_LAYOUT_TEXT, FEATURE_DISCOVER_CATEGORIES, FEATURE_FAVE_PERF_IMPROVE, FEATURE_GROUP_ADMIN_MESSAGES, FEATURE_GROUP_MSG_PUSH_PARAM, FEATURE_IMAGE_QUALITY_UPGRADE, FEATURE_IM_AUDIO_MSG_TRANSCRIPT, FEATURE_IM_CASPER_MSGS, FEATURE_IM_DISABLE_FORCE_OPEN_VK_ME, FEATURE_IM_GIF_AUTOPLAY, FEATURE_IM_MR_IN_VKAPP, FEATURE_MEMORIES_ENABLED, FEATURE_MENU_GAMES_CAROUSEL, FEATURE_MILKSHAKE, FEATURE_MILKSHAKE_CHANGE_FEED_BY_TIMEOUT, FEATURE_MILKSHAKE_NEWS_SCROLL_ON_BACK, FEATURE_MILKSHAKE_SWITCH_THEME_ON_TAP, FEATURE_ML_BRANDS, FEATURE_ML_FEATURES, FEATURE_ML_MODELS_LOADING, FEATURE_MONEY_TRANSFERS_VKPAY, FEATURE_MUSIC_ARTIST_CATALOG, FEATURE_MUSIC_NEW_CATALOG, FEATURE_NARRATIVE_SNIPPET_TYPE, FEATURE_NEWS_GAMES_IN_DISCOVER, FEATURE_NEWS_HEADER_SCROLL, FEATURE_NEW_FORMAT_SHOW_WPB, FEATURE_PODCASTS_PAGE, FEATURE_QR_CREATE_QR, FEATURE_QR_DYNAMIC_FRAME, FEATURE_QR_SCAN_FROM_PHOTO, FEATURE_QR_VISION_DECODER, FEATURE_QUEUE_COUNTERS, FEATURE_REEF, FEATURE_ROAMING_AUTOPLAY, FEATURE_SHARE_MSGS_ON_INVITE, FEATURE_STICKERS_BOT_LINK, FEATURE_STICKERS_NEW_CATALOG, FEATURE_STORIES_AVATAR, FEATURE_STORIES_FEED_TYPE, FEATURE_STORIES_SHOW_ALWAYS, FEATURE_STORY_ANSWER_PUBLIC, FEATURE_STORY_APP_STICKER, FEATURE_STORY_ARCHIVE, FEATURE_STORY_ARCHIVE_POST, FEATURE_STORY_BAKGROUND_ALL, FEATURE_STORY_BOX, FEATURE_STORY_CADRE, FEATURE_STORY_CAMERA_TOOLTIP, FEATURE_STORY_EDITOR_GALLERY, FEATURE_STORY_EDITOR_TYPE, FEATURE_STORY_ENDLESS_VIDEO, FEATURE_STORY_FAST_REACTIONS, FEATURE_STORY_GIF, FEATURE_STORY_GROUPED, FEATURE_STORY_HIGH_QUALITY, FEATURE_STORY_MASKS_FRONTAL, FEATURE_STORY_MENTION_ICON, FEATURE_STORY_MODERN_PUBLISH, FEATURE_STORY_MUSIC_EDITOR, FEATURE_STORY_MUSIC_REPLIES, FEATURE_STORY_MUSIC_REPLIES_2, FEATURE_STORY_NEW_FRAME, FEATURE_STORY_ONE_TIME, FEATURE_STORY_PHOTO_SHARING, FEATURE_STORY_PHOTO_STICKER, FEATURE_STORY_POLLS, FEATURE_STORY_POST_REPOST, FEATURE_STORY_PRELOADING, FEATURE_STORY_QUESTION, FEATURE_STORY_QUESTION_STYLE, FEATURE_STORY_RLOTTIE, FEATURE_STORY_RLOTTIE_CACHE, FEATURE_STORY_SAVE_DEFAULT_ON, FEATURE_STORY_SEND_DIALOG_LIST, FEATURE_STORY_STICKER_PACK, FEATURE_STORY_TIME, FEATURE_STORY_VIEWER_CAMERA, FEATURE_STORY_VOICE_ANSWER, FEATURE_STORY_WITH_CONTEXT, FEATURE_SUPERAPP_MENU, FEATURE_TRAFFIC_SAVER, FEATURE_VKPAY_WIDGET, FEATURE_VKUI_INTERNAL_TO_MINI_APPS, FEATURE_VKUI_TOKEN_CACHE, FEATURE_VK_APPS_SEARCH, FEATURE_VOTES_BALANCE, FEATURE_WEBVIEW_TOKEN_ACTIVE, FEATURE_MILKSHAKE_FORCE_DISABLED, FEATURE_MILKSHAKE_ACTIVATION_DISABLED, EXPERIMENT_NEWS_DISABLE_CACHE ->
true;
default -> false;
};
@@ -22,7 +23,7 @@ public class TogglesHook {
case FEATURE_MILKSHAKE_FORCE_DISABLED, FEATURE_MILKSHAKE_ACTIVATION_DISABLED -> !milkshake();
case AB_NEWS_VIDEO_LAYOUT_TEXT, FEATURE_COMPACT_REPOST, EXPERIMENT_NEWS_VIDEO_LAYOUT_TEXT ->
postsredesign();
- case FEATURE_IMAGE_QUALITY_UPGRADE -> !getBoolValue("compressPhotos", true);
+ case FEATURE_IMAGE_QUALITY_UPGRADE -> MediaImageEncoder.needToSkipCompression();
case FEATURE_MENU_GAMES_CAROUSEL -> miniapps();
default -> true;
};
diff --git a/app/src/main/java/ru/vtosters/lite/ui/fragments/VTSettings.java b/app/src/main/java/ru/vtosters/lite/ui/fragments/VTSettings.java
index bd5d81caa..379c7f94d 100644
--- a/app/src/main/java/ru/vtosters/lite/ui/fragments/VTSettings.java
+++ b/app/src/main/java/ru/vtosters/lite/ui/fragments/VTSettings.java
@@ -12,6 +12,7 @@ import com.aefyr.tsg.g2.TelegramStickersService;
import com.vk.about.AboutAppFragment;
import com.vk.balance.BalanceFragment;
import com.vk.identity.fragments.IdentityListFragment;
+import com.vk.medianative.MediaImageEncoder;
import com.vk.notifications.settings.NotificationsSettingsFragment;
import com.vk.webapp.fragments.PrivacyFragment;
import com.vtosters.lite.MainActivity;
@@ -399,7 +400,7 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment impleme
getPreferenceScreen(),
"",
requireContext().getString(R.string.vtlmedia),
- getValAsString(R.string.sett_compress_photos, Preferences.getBoolValue("compressPhotos", true)),
+ getValAsString(R.string.sett_compress_photos, MediaImageEncoder.needToCompress()),
R.drawable.ic_media_outline_28,
preference -> {
NavigatorUtils.switchFragment(requireContext(), MediaFragment.class);
diff --git a/app/src/main/java/ru/vtosters/lite/utils/Preferences.java b/app/src/main/java/ru/vtosters/lite/utils/Preferences.java
index 66d7d0608..954d3e9a0 100644
--- a/app/src/main/java/ru/vtosters/lite/utils/Preferences.java
+++ b/app/src/main/java/ru/vtosters/lite/utils/Preferences.java
@@ -6,6 +6,7 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.StrictMode;
+import com.vk.medianative.MediaImageEncoder;
import com.vtosters.lite.data.Users;
import com.vtosters.lite.fragments.SettingsListFragment;
import ru.vtosters.lite.BuildConfig;
@@ -334,9 +335,7 @@ public class Preferences {
}
public static int compress(int origquality) {
- if (!getBoolValue("compressPhotos", true)) {
- return 100;
- }
+ if (MediaImageEncoder.needToSkipCompression()) return 100;
return origquality;
}
}
diff --git a/smali/res/menu/photo_viewer.xml b/smali/res/menu/photo_viewer.xml
index aaa3c723c..3a28523da 100644
--- a/smali/res/menu/photo_viewer.xml
+++ b/smali/res/menu/photo_viewer.xml
@@ -40,6 +40,10 @@
android:id="@id/report"
android:showAsAction="never"
android:title="@string/report_content" />
+
- Выбрать вручную
- Предустановленные цвета
+
+ - Сжатие без потерь
+ - Стандартное сжатие
+ - Эффективное сжатие
+
diff --git a/smali/res/values/arrays.xml b/smali/res/values/arrays.xml
index 0f12fd056..f42e06642 100644
--- a/smali/res/values/arrays.xml
+++ b/smali/res/values/arrays.xml
@@ -891,4 +891,14 @@
- Color Picker
- From Palettes
+
+ - Lossless compression
+ - Default compression
+ - Effective compression
+
+
+ - skip
+ - jpeg
+ - native
+
diff --git a/smali/res/values/ids.xml b/smali/res/values/ids.xml
index 5157840f5..aa58a3f6d 100644
--- a/smali/res/values/ids.xml
+++ b/smali/res/values/ids.xml
@@ -3920,4 +3920,5 @@
+
diff --git a/smali/res/xml/preferences_media.xml b/smali/res/xml/preferences_media.xml
index 7bb77484b..e0e552f07 100644
--- a/smali/res/xml/preferences_media.xml
+++ b/smali/res/xml/preferences_media.xml
@@ -1,16 +1,18 @@
-
+
()V
- .locals 0
-
- .line 1
- invoke-direct {p0}, Ljava/lang/Object;->()V
-
- return-void
-.end method
-
-.method private static a(Ljava/io/FileOutputStream;Ljava/lang/String;)V
- .locals 2
-
- .line 8
- :try_start_0
- new-instance v0, Ljava/lang/StringBuilder;
-
- invoke-direct {v0}, Ljava/lang/StringBuilder;->