Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
1fdc399
Add extension runtime and Emby/Jellyfin providers
Jul 22, 2026
9076764
feat(extension): harden runtime and add Android plugin transport
Jul 22, 2026
41df34d
feat(extension): secure external plugin lifecycle
Jul 22, 2026
c5904e7
feat(extension): add TV plugin lifecycle management
Jul 22, 2026
45bec53
feat(extension): integrate provider search contributions
Jul 22, 2026
422bc6e
feat(extension): import metadata and EPG contributions
Jul 22, 2026
5bdec97
feat(extension): secure declarative plugin settings
Jul 22, 2026
653360b
feat(extension): render declarative settings on phone and TV
Jul 22, 2026
387fa48
feat(extension): clear plugin data and export safe diagnostics
Jul 22, 2026
e8614ff
feat(extension): expose plugin data management controls
Jul 22, 2026
b22ce75
feat(extension): support explicit capability reauthorization
Jul 22, 2026
747c30c
feat(extension): harden authorization presentation
Jul 22, 2026
5cbe512
docs(extension): rewrite developer and maintainer guides
Jul 22, 2026
eb72a74
docs(extension): remove historical defensive guidance
Jul 22, 2026
3855ffb
docs(extension): restructure bilingual guides
Jul 22, 2026
0065fe3
docs(extension): align guides with current behavior
Jul 22, 2026
af29b9f
feat(extension): add typed Android SDK sample
Jul 22, 2026
cf40d87
fix(extension): isolate EPG contribution refreshes
Jul 22, 2026
479c795
fix(extension): preserve provider data during EPG refresh
Jul 22, 2026
963894f
fix(extension): harden external plugin lifecycle
Jul 22, 2026
3c22d26
docs(extension): replace theory with task-focused guides
Jul 22, 2026
7d80d06
feat: harden extension platform and provider plugins
Jul 22, 2026
d3b3a59
feat: add liquid glass navigation on Android 17 toolchain
Jul 23, 2026
5ccd53a
feat: harden extension platform for release
Jul 24, 2026
9828ba3
feat: improve accessibility rtl and localization
Jul 28, 2026
60e1ff5
feat: harden extension provider accessibility
Jul 28, 2026
9de6d39
feat: enforce extension invocation budgets
Jul 28, 2026
f5b6ef8
feat: harden extension UI accessibility and localization
Jul 28, 2026
d6a45d2
Polish extension management across Android surfaces
Jul 28, 2026
a026eca
feat: harden extension settings and adaptive UI
Jul 29, 2026
6788d44
test: stabilize playlist management device flow
Jul 29, 2026
d2ba72a
fix: use contextual chrome for nested settings
Jul 29, 2026
4c052ed
fix: preserve provider form bidi semantics
Jul 29, 2026
4eb228f
fix: harden extension setting text direction
Jul 29, 2026
7887bcd
fix: localize playlist presentation semantics
Jul 29, 2026
862b2fc
fix: separate light floating navigation chrome
Jul 29, 2026
f4fb334
fix: make remote controls locale safe
Jul 29, 2026
d268272
fix: complete remote control translations
Jul 29, 2026
24d9431
test: cover remote controls in rtl large text
Jul 29, 2026
ebf64d8
fix: harden extension detail presentation
Jul 29, 2026
05e7622
docs: record final phone extension matrix
Jul 29, 2026
c4b598c
fix: bind invocations to runtime registrations
Jul 29, 2026
fdd514d
fix: scope extension settings snapshots by hook
Jul 29, 2026
8f8fc2e
fix: bind extension settings to registration leases
Jul 29, 2026
c5b7e96
docs: explain registration-bound extension state
Jul 29, 2026
7c1948c
fix: preserve extension identifiers in rtl
Jul 29, 2026
ec08fd4
feat: expose extension network access state
Jul 29, 2026
9e8d9a1
ci: add manual fast APK build
Jul 29, 2026
1226efc
feat: localize extension network origin states
Jul 29, 2026
b533771
feat: surface extension network origin approvals
Jul 29, 2026
53e171a
test: stabilize extension accessibility flows
Jul 29, 2026
99d9755
feat: seed smartphone debug playback samples
Jul 29, 2026
a6c89cd
test: cover adaptive tablet navigation
Jul 29, 2026
69dc8db
docs: align extension guides with verified behavior
Jul 29, 2026
9f5f24d
feat: harden typed extension contracts
Jul 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
79 changes: 79 additions & 0 deletions .github/workflows/android-fast.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: Android Fast Build

on:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: android-fast-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 45

steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive

- name: Setup JDK
uses: actions/setup-java@v4
with:
java-version: 17
distribution: zulu

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
with:
gradle-home-cache-cleanup: true

- name: Verify fast contracts
run: |
./gradlew \
:extension:api:jvmTest \
:extension:runtime:test \
:i18n:testDebugUnitTest

- name: Build debug APKs
run: |
./gradlew \
:app:smartphone:assembleDebug \
:app:tv:assembleDebug \
:testing:extension-reference:assembleDebug

- name: Verify 16 KB native compatibility
run: |
testing/bin/verify-android-16kb.sh \
app/smartphone/build/outputs/apk/debug/*.apk \
app/tv/build/outputs/apk/debug/*.apk \
testing/extension-reference/build/outputs/apk/debug/*.apk

- name: Upload
uses: actions/upload-artifact@v4
with:
name: android-debug-apks
if-no-files-found: error
retention-days: 14
path: |
app/smartphone/build/outputs/apk/debug/*.apk
app/tv/build/outputs/apk/debug/*.apk
testing/extension-reference/build/outputs/apk/debug/*.apk

- name: Upload To Telegram
uses: xireiki/channel-post@890c1d09635ae629aeffbed4f9716a5473eac896 # v1.0.10
with:
bot_token: ${{ secrets.BOT_TOKEN }}
chat_id: ${{ secrets.CHAT_ID }}
api_id: ${{ secrets.API_ID }}
api_hash: ${{ secrets.API_HASH }}
large_file: true
method: sendFile
path: |
app/smartphone/build/outputs/apk/debug/*.apk
app/tv/build/outputs/apk/debug/*.apk
testing/extension-reference/build/outputs/apk/debug/*.apk
49 changes: 39 additions & 10 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@ on:
paths-ignore:
- '**.md'
- '**.txt'
- '.github/**'
- '.idea/**'
- 'fastlane/**'
- '!.github/workflows/**'
pull_request:
branches: [ "master" ]
workflow_dispatch:

concurrency:
group: ${{ github.ref }}
group: android-release-${{ github.ref }}
cancel-in-progress: true

jobs:
Expand Down Expand Up @@ -44,23 +42,53 @@ jobs:
- name: Clean GMD
run: ./gradlew cleanManagedDevices --unused-only

- name: Build production app
- name: Verify extension contracts
run: |
./gradlew \
:extension:api:jvmTest \
:extension:runtime:test \
:extension:sdk-android:testDebugUnitTest \
:extension:transport-android:testDebugUnitTest

- name: Verify localization contracts
run: ./gradlew :i18n:testDebugUnitTest

- name: Validate connected UI harnesses
run: |
bash -n testing/bin/run-smartphone-provider-ui-matrix.sh
./gradlew \
:data:assembleDebugAndroidTest \
:app:smartphone:assembleDebugAndroidTest \
:app:tv:assembleDebugAndroidTest

- name: Build smartphone release APK
run: ./gradlew :app:smartphone:assembleRelease

- name: Build production TV app
- name: Build TV release APK
run: ./gradlew :app:tv:assembleRelease

- name: Build reference extension release APK
run: ./gradlew :testing:extension-reference:assembleRelease

- name: Verify 16 KB native compatibility
run: |
testing/bin/verify-android-16kb.sh \
app/smartphone/build/outputs/published-apk/release/*.apk \
app/tv/build/outputs/published-apk/release/*.apk \
testing/extension-reference/build/outputs/apk/release/*.apk

- name: Upload
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
path: |
app/smartphone/build/outputs/apk/release/*.apk
app/tv/build/outputs/apk/release/*.apk
app/smartphone/build/outputs/published-apk/release/*.apk
app/tv/build/outputs/published-apk/release/*.apk
testing/extension-reference/build/outputs/apk/release/*.apk

- name: Upload To Telegram
if: github.event_name != 'pull_request'
uses: xireiki/channel-post@v1.0.10
uses: xireiki/channel-post@890c1d09635ae629aeffbed4f9716a5473eac896 # v1.0.10
with:
bot_token: ${{ secrets.BOT_TOKEN }}
chat_id: ${{ secrets.CHAT_ID }}
Expand All @@ -69,5 +97,6 @@ jobs:
large_file: true
method: sendFile
path: |
app/smartphone/build/outputs/apk/release/*.apk
app/tv/build/outputs/apk/release/*.apk
app/smartphone/build/outputs/published-apk/release/*.apk
app/tv/build/outputs/published-apk/release/*.apk
testing/extension-reference/build/outputs/apk/release/*.apk
33 changes: 27 additions & 6 deletions .github/workflows/baseline-profiles.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ on:
push:
branches: [ "master" ]
paths:
- 'build.gradle.kts'
- 'gradle/libs.versions.toml'
- 'gradle/wrapper/**'
- 'app/smartphone/build.gradle.kts'
- 'app/tv/build.gradle.kts'
- 'baselineprofile/**'
- '.github/workflows/baseline-profiles.yml'

permissions:
Expand Down Expand Up @@ -40,15 +44,18 @@ jobs:
if [ "$base" = "0000000000000000000000000000000000000000" ]; then
base="$(git rev-list --max-parents=0 HEAD)"
fi
if git diff --name-only "$base" HEAD -- .github/workflows/baseline-profiles.yml | grep -q .; then
if git diff --name-only "$base" HEAD -- \
.github/workflows/baseline-profiles.yml \
build.gradle.kts \
gradle/libs.versions.toml \
gradle/wrapper \
app/smartphone/build.gradle.kts \
app/tv/build.gradle.kts \
baselineprofile | grep -q .; then
echo "run=true" >> "$GITHUB_OUTPUT"
exit 0
fi
if git diff "$base" HEAD -- app/smartphone/build.gradle.kts app/tv/build.gradle.kts | grep -E '^[-+][[:space:]]*version(Code|Name)[[:space:]]*='; then
echo "run=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "No app version change detected."
echo "No baseline profile inputs changed."
echo "run=false" >> "$GITHUB_OUTPUT"

- name: Setup JDK
Expand All @@ -75,6 +82,15 @@ jobs:
sudo chmod 660 /dev/kvm
ls -l /dev/kvm

- name: Remove stale baseline profile output
if: steps.baseline-trigger.outputs.run == 'true'
run: |
rm -f \
app/smartphone/src/main/generated/baselineProfiles/baseline-prof.txt \
app/smartphone/src/main/generated/baselineProfiles/startup-prof.txt \
app/tv/src/main/generated/baselineProfiles/baseline-prof.txt \
app/tv/src/main/generated/baselineProfiles/startup-prof.txt

- name: Generate smartphone baseline profile
if: steps.baseline-trigger.outputs.run == 'true'
run: |
Expand All @@ -97,6 +113,11 @@ jobs:
if: steps.baseline-trigger.outputs.run == 'true'
run: |
set -euo pipefail
test -s app/smartphone/src/main/generated/baselineProfiles/baseline-prof.txt
test -s app/smartphone/src/main/generated/baselineProfiles/startup-prof.txt
test -s app/tv/src/main/generated/baselineProfiles/baseline-prof.txt
test -s app/tv/src/main/generated/baselineProfiles/startup-prof.txt
test ! -d app/smartphone/src/release/generated/baselineProfiles
test ! -d app/tv/src/release/generated/baselineProfiles

- name: Create baseline profile update pull request
Expand Down
78 changes: 62 additions & 16 deletions .github/workflows/native-packs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,24 +53,70 @@ jobs:
exit 1
fi

- name: Verify native load source protocol
- name: Verify 16 KB native compatibility
run: |
set -euo pipefail
if git grep -n 'm3u\.native-load-instrumentation\|NATIVE_PACK_COMMIT\|nativeLoadCommit' -- \
native-load-gradle-plugin/src \
app \
data/src \
data/build.gradle.kts \
docs \
native-load.yml \
.github \
':!.github/workflows/native-packs.yml'; then
exit 1
fi
if find native-packs -name '*.json' -print0 \
| xargs -0 grep -n 'sourceCommit\|snapshotPath\|https://raw.githubusercontent.com\|native-packs/.*/release/'; then
exit 1
fi
pack_id="$(awk '$1 == "id:" { print $2; exit }' native-load.yml)"
testing/bin/verify-android-16kb.sh "native-packs/$pack_id"/*.zip

- name: Verify native pack identity pinning
run: |
set -euo pipefail
./gradlew :data:generateReleaseBuildConfig --no-configuration-cache
python3 - <<'PY'
import hashlib
import re
import zipfile
from pathlib import Path

config = Path(
"data/build/generated/source/buildConfig/release/com/m3u/data/BuildConfig.java"
).read_text()

def constant(name):
match = re.search(
rf'public static final String {name} = "([^"]*)";',
config,
)
if not match:
raise SystemExit(f"Missing release identity constant: {name}")
return match.group(1)

def entries(name):
encoded = constant(name)
return dict(entry.split("=", 1) for entry in encoded.split(";") if entry)

pack_id = constant("NATIVE_PACK_ID")
pack_dir = Path("native-packs") / pack_id
manifest = pack_dir / f"m3u-codec-{pack_id}.json"
expected_manifest = hashlib.sha256(manifest.read_bytes()).hexdigest()
if constant("NATIVE_PACK_EXPECTED_MANIFEST_SHA256") != expected_manifest:
raise SystemExit("Release manifest identity is stale")

expected_assets = entries("NATIVE_PACK_EXPECTED_ASSET_SHA256")
actual_assets = {
archive.name: hashlib.sha256(archive.read_bytes()).hexdigest()
for archive in sorted(pack_dir.glob("*.zip"))
}
if expected_assets != actual_assets:
raise SystemExit("Release asset identities are stale")

actual_libraries = {}
for archive in sorted(pack_dir.glob("*.zip")):
with zipfile.ZipFile(archive) as zipped:
for name in sorted(zipped.namelist()):
if not name.endswith("/"):
actual_libraries[f"{archive.name}/{name}"] = hashlib.sha256(
zipped.read(name)
).hexdigest()
if entries("NATIVE_PACK_EXPECTED_LIBRARY_SHA256") != actual_libraries:
raise SystemExit("Release library identities are stale")
PY
git grep -q 'expectedManifestSha256' -- data/src/main/java/com/m3u/data/codec/CodecPackRepository.kt
git grep -q 'expectedAssetSha256' -- data/src/main/java/com/m3u/data/codec/CodecPackRepository.kt
git grep -q 'expectedLibrarySha256' -- \
data/src/main/java/com/m3u/data/codec/CodecPackRepository.kt \
data/src/main/java/com/m3u/data/codec/CodecNativeLoader.kt

- name: Verify committed native packs
run: |
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ jks.txt
__pycache__/
*.pyc
testing/mock-server/bin/
testing/mock-server/build/
testing/extension-reference/build/
samples/*/build/
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ It is made for users who want a clean, practical, ad-free IPTV app that works we
- [Nightly Build](https://nightly.link/oxyroid/M3UAndroid/workflows/android/master/artifact.zip)
- [Telegram Channel](https://t.me/m3u_android)

## Extensions

- [Extension documentation (English)](docs/extensions/README.md)
- [插件文档(简体中文)](docs/extensions/README.zh-CN.md)

## License

M3UAndroid is an open-source project licensed under the [GNU General Public License v3.0](LICENSE).
1 change: 0 additions & 1 deletion app/extension/.gitignore

This file was deleted.

Loading
Loading