Private
Public Access
Import minamp 1.0 source
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
0df8124629
194 files changed
+7319
No files matched your search
@@ -0,0 +1,53 @@
|
||||
"""Draws the launcher and status-bar icons at ldpi/mdpi/hdpi (supersampled for smooth edges)."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
RES = sys.argv[1]
|
||||
BG = (24, 24, 29, 255)
|
||||
ACCENT = (247, 176, 46, 255)
|
||||
DARK = (22, 19, 12, 255)
|
||||
|
||||
|
||||
def launcher(size):
|
||||
S = size * 8
|
||||
im = Image.new("RGBA", (S, S), (0, 0, 0, 0))
|
||||
d = ImageDraw.Draw(im)
|
||||
pad = S * 0.06
|
||||
d.rounded_rectangle([pad, pad, S - pad, S - pad], radius=S * 0.22, fill=BG)
|
||||
# amber disc with a ring groove and a play notch: a record that is also a play button
|
||||
c = S * 0.46
|
||||
r = S * 0.29
|
||||
d.ellipse([c - r, c - r, c + r, c + r], fill=ACCENT)
|
||||
rr = r * 0.72
|
||||
w = max(2, int(S * 0.018))
|
||||
d.ellipse([c - rr, c - rr, c + rr, c + rr], outline=DARK, width=w)
|
||||
t = r * 0.42
|
||||
d.polygon([(c - t * 0.55, c - t), (c + t * 0.95, c), (c - t * 0.55, c + t)], fill=DARK)
|
||||
# three level bars, lower right
|
||||
bw = S * 0.045
|
||||
for i, h in enumerate((0.10, 0.17, 0.13)):
|
||||
x = S * 0.72 + i * bw * 1.6
|
||||
d.rounded_rectangle([x, S * 0.82 - S * h, x + bw, S * 0.82], radius=bw / 2, fill=ACCENT)
|
||||
return im.resize((size, size), Image.LANCZOS)
|
||||
|
||||
|
||||
def status(size):
|
||||
S = size * 8
|
||||
im = Image.new("RGBA", (S, S), (0, 0, 0, 0))
|
||||
d = ImageDraw.Draw(im)
|
||||
c = S / 2
|
||||
r = S * 0.44
|
||||
d.ellipse([c - r, c - r, c + r, c + r], fill=ACCENT)
|
||||
t = r * 0.45
|
||||
d.polygon([(c - t * 0.55, c - t), (c + t * 0.95, c), (c - t * 0.55, c + t)], fill=(0, 0, 0, 0))
|
||||
return im.resize((size, size), Image.LANCZOS)
|
||||
|
||||
|
||||
for folder, ls, ss in (("drawable-ldpi", 36, 19), ("drawable-mdpi", 48, 25), ("drawable-hdpi", 72, 38)):
|
||||
os.makedirs(os.path.join(RES, folder), exist_ok=True)
|
||||
launcher(ls).save(os.path.join(RES, folder, "ic_launcher.png"))
|
||||
status(ss).save(os.path.join(RES, folder, "ic_stat.png"))
|
||||
launcher(192).save(os.path.join(os.path.dirname(RES.rstrip("/")), "icon-preview.png"))
|
||||
print("icons written")
|
||||
@@ -0,0 +1,27 @@
|
||||
"""Turn a PEM CA bundle into assets/roots/<sha1(subject DER)>-<n>.der for the app's trust store."""
|
||||
import hashlib
|
||||
import os
|
||||
import sys
|
||||
|
||||
from cryptography import x509
|
||||
from cryptography.hazmat.primitives.serialization import Encoding
|
||||
|
||||
|
||||
def main(pem_path, out_dir):
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
for f in os.listdir(out_dir):
|
||||
os.remove(os.path.join(out_dir, f))
|
||||
data = open(pem_path, "rb").read()
|
||||
certs = x509.load_pem_x509_certificates(data)
|
||||
counts = {}
|
||||
for c in certs:
|
||||
h = hashlib.sha1(c.subject.public_bytes()).hexdigest()
|
||||
n = counts.get(h, 0)
|
||||
counts[h] = n + 1
|
||||
with open(os.path.join(out_dir, "%s-%d.der" % (h, n)), "wb") as fh:
|
||||
fh.write(c.public_bytes(Encoding.DER))
|
||||
print("wrote %d roots to %s" % (len(certs), out_dir))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1], sys.argv[2])
|
||||
Executable
+62
@@ -0,0 +1,62 @@
|
||||
#!/bin/bash
|
||||
# Fetches/builds everything build.sh needs into $TOOLS (default ./toolchain), with only
|
||||
# Java 17+, git, curl, python3 (+ cryptography for make_roots.py) and npm's registry.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
PROJ=$PWD
|
||||
T=${TOOLS:-$PWD/toolchain}
|
||||
mkdir -p "$T" && cd "$T"
|
||||
|
||||
echo "== aapt2 (prebuilt, from the aaptjs3 npm package)"
|
||||
if [ ! -x aapt2 ]; then
|
||||
mkdir -p dl && curl -sSL -o dl/aapt.tgz "$(curl -sS https://registry.npmjs.org/aaptjs3/latest | python3 -c 'import json,sys;print(json.load(sys.stdin)["dist"]["tarball"])')"
|
||||
tar xzf dl/aapt.tgz -C dl && cp dl/package/bin/x64/linux/aapt2 . && chmod +x aapt2
|
||||
fi
|
||||
|
||||
echo "== android-8 platform jar"
|
||||
if [ ! -f platforms/android-8/android.jar ]; then
|
||||
git clone --depth 1 --filter=blob:none --sparse https://github.com/Sable/android-platforms platforms
|
||||
(cd platforms && git sparse-checkout set android-8)
|
||||
fi
|
||||
|
||||
echo "== dx (built from AOSP source)"
|
||||
if [ ! -f dx.jar ]; then
|
||||
git clone --depth 1 --branch android10-release --filter=blob:none --sparse https://github.com/aosp-mirror/platform_dalvik dalvik
|
||||
(cd dalvik && git sparse-checkout set dx/src)
|
||||
mkdir -p dxbuild && find dalvik/dx/src -name '*.java' > dxsrc.txt
|
||||
javac -nowarn -encoding UTF-8 -d dxbuild @dxsrc.txt
|
||||
jar cfe dx.jar com.android.dx.command.Main -C dxbuild .
|
||||
fi
|
||||
|
||||
echo "== Bouncy Castle sources, renamed to com.minamp.bc"
|
||||
if [ ! -d bcsrc ]; then
|
||||
git clone --depth 1 --filter=blob:none --sparse https://github.com/bcgit/bc-java bc
|
||||
(cd bc && git sparse-checkout set core/src/main/java tls/src/main/java util/src/main/java)
|
||||
mkdir -p bcsrc/com/minamp
|
||||
cp -r bc/core/src/main/java/org/bouncycastle bcsrc/com/minamp/bc
|
||||
cp -r --update=none bc/tls/src/main/java/org/bouncycastle/* bcsrc/com/minamp/bc/
|
||||
cp -r --update=none bc/util/src/main/java/org/bouncycastle/* bcsrc/com/minamp/bc/
|
||||
grep -rl 'org\.bouncycastle' bcsrc | xargs sed -i 's/org\.bouncycastle/com.minamp.bc/g'
|
||||
# Android 2.2 has no Long.compare / compareUnsigned / divideUnsigned / parseUnsignedLong
|
||||
python3 - bcsrc/com/minamp/bc/util/Longs.java <<'PY'
|
||||
import sys
|
||||
p = sys.argv[1]; s = open(p).read()
|
||||
s = s.replace("return Long.compare(x, y);", "return x < y ? -1 : (x == y ? 0 : 1);")
|
||||
s = s.replace("return Long.compareUnsigned(x, y);", "return compare(x + Long.MIN_VALUE, y + Long.MIN_VALUE);")
|
||||
s = s.replace("return Long.divideUnsigned(x, y);", """if (y < 0) { return compareUnsigned(x, y) < 0 ? 0L : 1L; }
|
||||
if (x >= 0) { return x / y; }
|
||||
long q = ((x >>> 1) / y) << 1;
|
||||
long r = x - q * y;
|
||||
return q + (compareUnsigned(r, y) >= 0 ? 1 : 0);""")
|
||||
s = s.replace("return Long.parseUnsignedLong(s);", "return new java.math.BigInteger(s).longValue();")
|
||||
open(p, "w").write(s)
|
||||
PY
|
||||
fi
|
||||
|
||||
echo "== signing key"
|
||||
if [ ! -f minamp.keystore ]; then
|
||||
if [ -f "$PROJ/signing/minamp.keystore" ]; then cp "$PROJ/signing/minamp.keystore" .; else
|
||||
keytool -genkeypair -keystore minamp.keystore -storepass minamp123 -keypass minamp123 -alias minamp \
|
||||
-keyalg RSA -keysize 2048 -sigalg SHA1withRSA -validity 20000 -dname "CN=Minamp, O=Minamp"; fi
|
||||
fi
|
||||
echo "Toolchain ready in $T — build with: TOOLS=$T ./build.sh"
|
||||
@@ -0,0 +1,51 @@
|
||||
"""zipalign replacement: rewrites an APK so every STORED entry's data starts on a 4-byte boundary.
|
||||
|
||||
Re-compressing deflated entries doesn't break the v1 (JAR) signature, which covers uncompressed
|
||||
contents only.
|
||||
"""
|
||||
import struct
|
||||
import sys
|
||||
import zipfile
|
||||
|
||||
ALIGN = 4
|
||||
|
||||
|
||||
def align(src, dst):
|
||||
zin = zipfile.ZipFile(src)
|
||||
with zipfile.ZipFile(dst, "w") as zout:
|
||||
for info in zin.infolist():
|
||||
data = zin.read(info.filename)
|
||||
zi = zipfile.ZipInfo(info.filename, date_time=info.date_time)
|
||||
zi.compress_type = info.compress_type
|
||||
zi.external_attr = info.external_attr
|
||||
zi.create_system = info.create_system
|
||||
extra = b""
|
||||
if info.compress_type == zipfile.ZIP_STORED:
|
||||
offset = zout.fp.tell()
|
||||
name_len = len(zi.filename.encode("utf-8"))
|
||||
base = offset + 30 + name_len + 6 # 6 = our extra-field header + alignment value
|
||||
pad = (ALIGN - base % ALIGN) % ALIGN
|
||||
# 0xD935 is the extra-field id Android's own zipalign uses for padding
|
||||
extra = struct.pack("<HHH", 0xD935, 2 + pad, ALIGN) + b"\0" * pad
|
||||
zi.extra = extra
|
||||
zout.writestr(zi, data)
|
||||
verify(dst)
|
||||
|
||||
|
||||
def verify(path):
|
||||
z = zipfile.ZipFile(path)
|
||||
raw = open(path, "rb").read()
|
||||
for info in z.infolist():
|
||||
if info.compress_type != zipfile.ZIP_STORED:
|
||||
continue
|
||||
o = info.header_offset
|
||||
n, e = struct.unpack("<HH", raw[o + 26:o + 30])
|
||||
start = o + 30 + n + e
|
||||
if start % ALIGN:
|
||||
raise SystemExit("misaligned: %s at %d" % (info.filename, start))
|
||||
if z.testzip() is not None:
|
||||
raise SystemExit("corrupt zip")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
align(sys.argv[1], sys.argv[2])
|
||||
Reference in new issue
Block a user