Build, verify, sign, and install the Open IPTV Android APK. Use for Gradle setup, wrapper builds, debug and release APKs, keystore signing, apksigner/aapt2 verification, adb install, logcat, and Windows-specific Android SDK paths. Includes the verified toolchain baseline and failure troubleshooting.
Resources
1Install
npx skillscat add lsunol/my-iptv-player/android-apk Install via the SkillsCat registry.
Android APK delivery
The build must be reproducible with the Gradle wrapper and JDK 17. Do not
require a global Gradle, Android Studio, or a specific IDE.
Baseline (build-verified 2026-10-31)
| Tool | Version |
|---|---|
| Gradle wrapper | 8.14.6 |
| AGP | 8.13.2 |
| Kotlin | 2.2.21 |
| Compose BOM | 2026.06.01 (UI 1.11.4, Material3 1.4.0) |
| Media3 | 1.11.1 |
| compileSdk / targetSdk | 36 |
| minSdk | 24 |
| JDK | 17 |
| Build tools | 36.0.0 |
| Android SDK | %LOCALAPPDATA%\Android\Sdk |
Why not AGP 9 / Compose 1.12: Compose 1.12 requires compileSdk 37 and AGP 9.1+.
AGP 9 uses built-in Kotlin and pins KGP 2.2.10, complicating Compose compiler
alignment. Only migrate with explicit approval and after reading theagp-9-upgrade skill. Platform 37 is available as platforms;android-37.0 if
that migration is approved.
Dependency versions that require compileSdk 37 / AGP 9.1+ and must not be used
with this baseline: core-ktx 1.19.x, lifecycle 2.11.x, navigation 2.10.x,
Compose UI 1.12.x. Use core-ktx 1.18.0, lifecycle 2.10.0, navigation 2.9.8,
Compose BOM 2026.06.01.
First-time setup
# from the repository root
java -version # must be 17.x
echo "$JAVA_HOME" # Temurin 17 path
adb devices -l # phone connected + authorized?
# SDK path for Gradle (git-ignored)
cat > local.properties <<'EOF'
sdk.dir=C:/Android/Sdk
EOF
# wrapper (one-time; commit gradle/wrapper/* and gradlew*)
gradle wrapper --gradle-version 8.14.6If Gradle is not installed, generate the wrapper with Android Studio or copy the
wrapper files from a known project, then ./gradlew --version.
On PowerShell, write the path as:
sdk.dir=C:/Android/SdkDo not commit local.properties.
Build
./gradlew :app:assembleDebugOutput:
app/build/outputs/apk/debug/app-debug.apkUseful focused tasks:
./gradlew :app:compileDebugKotlin
./gradlew :app:testDebugUnitTest
./gradlew :app:lintDebug
./gradlew :app:assembleDebug --stacktraceDo not run clean as a reflex. It hides incremental-build bugs and wastes time.
Use --rerun-tasks only when a task is genuinely stale.
Verify the artifact
APK=app/build/outputs/apk/debug/app-debug.apk
ls -lh "$APK"
sha256sum "$APK"
SDK="$LOCALAPPDATA/Android/Sdk"
"$SDK/build-tools/36.0.0/apksigner.bat" verify --print-certs "$APK"
"$SDK/build-tools/36.0.0/aapt2.exe" dump badging "$APK" | head -20aapt2 dump badging must show:
package: name='app.opentv'sdkVersion:'24'targetSdkVersion:'36'
If the APK is unsigned (debug builds are signed with the debug key),apksigner verify still works and prints the debug certificate.
Install and launch
adb devices -l
adb install -r -d app/build/outputs/apk/debug/app-debug.apk
adb shell am start -n app.opentv/.MainActivity
adb logcat --pid=$(adb shell pidof -s app.opentv)If adb devices is empty:
- Enable Developer options → USB debugging on the phone.
- Accept the RSA authorization prompt.
- Try another USB cable/port (data cable, not charge-only).
adb kill-server && adb start-server.- For wireless debugging, use
adb pair+adb connect.
If no phone is available, create an emulator:
SDK="$LOCALAPPDATA/Android/Sdk"
"$SDK/cmdline-tools/latest/bin/sdkmanager.bat" "system-images;android-36;google_apis;x86_64"
"$SDK/cmdline-tools/latest/bin/avdmanager.bat" create avd -n opentv36 -k "system-images;android-36;google_apis;x86_64" -d pixel_6
"$SDK/emulator/emulator.exe" -avd opentv36Release signing (only with user approval)
Generate a keystore outside the repository or in signing/ (git-ignored):
keytool -genkeypair -v \
-keystore signing/opentv-release.jks \
-alias opentv \
-keyalg RSA -keysize 4096 -validity 10000keystore.properties (git-ignored):
storeFile=../signing/opentv-release.jks
storePassword=CHANGE_ME
keyAlias=opentv
keyPassword=CHANGE_MEapp/build.gradle.kts reads it only if the file exists. A release build without
it must fail with a clear message; never fall back to the debug key for a
release.
./gradlew :app:assembleRelease
"$SDK/build-tools/36.0.0/apksigner.bat" verify --print-certs app/build/outputs/apk/release/app-release.apkNever commit *.jks, *.keystore, keystore.properties, or passwords. Do not
publish a personal build signed with a debug key.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
SDK location not found |
missing/incorrect local.properties |
write sdk.dir with escaped \ and : |
Unsupported class file major version |
wrong JDK | set JAVA_HOME to JDK 17 and org.gradle.java.home if needed |
Android Gradle plugin requires Java 17 |
JDK 11/8 | switch to JDK 17 |
Dependency ... requires compileSdk 37 |
Compose 1.12 / core 1.19 / lifecycle 2.11 / nav 2.10 | use the baseline versions |
Failed to apply plugin 'org.jetbrains.kotlin.android' |
AGP 9 built-in Kotlin | do not upgrade; or follow agp-9-upgrade |
Duplicate class ... |
overlapping dependencies | align versions; exclude the duplicate |
R8 missing class |
missing keep rule | add a targeted -keep; do not disable R8 |
INSTALL_FAILED_UPDATE_INCOMPATIBLE |
different signing key | adb uninstall app.opentv then install |
INSTALL_FAILED_VERSION_DOWNGRADE |
lower versionCode | bump versionCode or use -d for debug |
INSTALL_FAILED_INSUFFICIENT_STORAGE |
phone full | free space |
CLEARTEXT communication not permitted |
manifest flag missing | android:usesCleartextTraffic="true" |
Resources$NotFoundException |
missing/renamed resource | check strings.xml/drawables |
| Gradle daemon OOM | default heap | org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8 |
| Slow dependency downloads | network/proxy | check proxy, use --offline only after a full sync |
No connected devices |
USB debugging/auth | see install section |
| App opens then closes | runtime exception | adb logcat around AndroidRuntime |
| Black video, audio OK | codec/surface | try non-HEVC variant; check MediaCodec logs |
PlayerView crash on exit |
player released while attached | release in onCleared, detach surface first |
Build-file expectations
- Version catalog
gradle/libs.versions.tomlis preferred over hard-coded
versions inbuild.gradle.kts. gradle.properties:org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8,android.useAndroidX=true,kotlin.code.style=official.app/build.gradle.kts:compileSdk = 36,minSdk = 24,targetSdk = 36,buildFeatures { compose = true }, Compose compiler plugin 2.2.21.- No
kotlin-androidplugin if AGP 9 is ever used; with AGP 8.13.2 it is
required. android:usesCleartextTraffic="true"in the manifest.- Keep
INTERNETas the only dangerous-ish permission; no storage/location.
Definition of done
-
./gradlew :app:assembleDebugsucceeds from a clean checkout. - APK path, size, SHA-256, package, minSdk, targetSdk verified.
-
adb install -rsucceeds on a real phone or emulator. - App launches and reaches the playlist screen.
-
adb logcatshows noFATAL EXCEPTIONon startup. - APK is available to the user with the exact install command.
- No secret is committed or printed.