Skip to content

Commit 5bcb835

Browse files
committed
init
0 parents  commit 5bcb835

97 files changed

Lines changed: 11792 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/pull_request_template.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
## Summary
2+
3+
<!-- What changed, why, and what should reviewers observe? -->
4+
5+
## Risk
6+
7+
<!-- Note capture, lifecycle, OAuth, persistence, security, or compatibility risk. -->
8+
9+
- [ ] The change is focused and contains no credentials, generated output, or unrelated refactor.
10+
- [ ] Applicable source-of-truth docs were updated with the implementation.
11+
- [ ] Required CI checks pass on this commit.
12+
13+
## Manual regression
14+
15+
Check the behaviors affected by this pull request and record anything not run in **Exceptions**.
16+
17+
### App and configuration
18+
19+
- [ ] Fresh install opens Settings until a platform or manual destination is configured.
20+
- [ ] Preview and Settings navigation, system back, background/foreground, and process restart behave correctly.
21+
- [ ] Stream details, quality, orientation, wake lock, Preview audio, and pause-image choices persist.
22+
- [ ] Resolution, frame-rate, and bitrate dropdowns expose valid presets and persist selections.
23+
- [ ] Compact screens, rotation, font scaling, keyboard, insets, and accessibility labels remain usable.
24+
25+
### Preview and capture
26+
27+
- [ ] Screen and Camera controls select the intended source without a dropdown.
28+
- [ ] Preview controls use two rows in portrait and one usable row in landscape.
29+
- [ ] Microphone and System Audio can be selected independently before preparation.
30+
- [ ] Camera permission denial is recoverable; front/back flipping uses only available cameras.
31+
- [ ] Camera preview preserves aspect ratio and physical up/down with auto-rotate on or off, including after sleep/reopen.
32+
- [ ] Camera output chooses the physical portrait/landscape geometry at start and remains stable during the session.
33+
- [ ] Orientation lock applies before and during a session and restores correctly when disabled.
34+
- [ ] Screen capture requests fresh MediaProjection consent and renders its live preview, including recursion.
35+
- [ ] Microphone-only, playback-only, mixed, and silent modes behave as configured.
36+
37+
### Live session
38+
39+
- [ ] Go Live immediately shows preparation and cannot be triggered twice.
40+
- [ ] Screen ↔ camera and front ↔ back switching works while live without reconnecting healthy outputs.
41+
- [ ] Hide/Show Video preserves RTMP and audio with both default and custom images.
42+
- [ ] Settings stays hidden while preparing/live/video-hidden; Stop clears projection/audio and leaves any bound camera preview stable.
43+
- [ ] A second session works after stop, denial, failure, hide/show, and source switching.
44+
- [ ] One and multiple destinations receive the same valid H.264/AAC session; one failed output does not end healthy outputs.
45+
46+
### Destinations and accounts
47+
48+
- [ ] Manual RTMP add, validation, edit, enable/disable, delete confirmation, encryption, and restore work.
49+
- [ ] YouTube connect, browser redirect, refresh, broadcast creation, streaming, and disconnect/revocation work.
50+
- [ ] Twitch device authorization, polling, refresh, stream-key resolution, streaming, and disconnect/revocation work.
51+
- [ ] Cancellation and provider/network failures return to an actionable state without leaking URLs, keys, tokens, or personal data.
52+
53+
## Exceptions
54+
55+
<!-- List unchecked/not-applicable items and why. -->

.github/workflows/build.yml

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
name: Build and Test
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
permissions:
8+
contents: read
9+
10+
jobs:
11+
build:
12+
runs-on: ubuntu-latest
13+
env:
14+
APP_REPOSITORY_URL: ${{ github.server_url }}/${{ github.repository }}
15+
16+
steps:
17+
- uses: actions/checkout@v6
18+
19+
- name: Set up Java
20+
uses: actions/setup-java@v5
21+
with:
22+
distribution: 'zulu'
23+
java-version: '17'
24+
25+
- name: Setup Android SDK
26+
uses: android-actions/setup-android@v4
27+
28+
- name: Setup Gradle
29+
uses: gradle/actions/setup-gradle@v6
30+
31+
- name: Make gradlew executable
32+
run: chmod +x ./gradlew
33+
34+
- name: Accept Android SDK licenses
35+
run: yes | sdkmanager --licenses || true
36+
37+
- name: Test, lint, and build Android APK
38+
run: >-
39+
./gradlew
40+
:shared:jvmTest
41+
:androidApp:testDebugUnitTest
42+
:androidApp:lintDebug
43+
:androidApp:assembleDebug
44+
45+
- name: Upload APK Artifact
46+
uses: actions/upload-artifact@v7
47+
with:
48+
name: ogh-debug-apk
49+
path: androidApp/build/outputs/apk/debug/*.apk
50+
if-no-files-found: error
51+
52+
maestro:
53+
needs: build
54+
runs-on: ubuntu-latest
55+
env:
56+
MAESTRO_VERSION: 2.6.1
57+
MAESTRO_CLI_NO_ANALYTICS: "1"
58+
MAESTRO_CLI_ANALYSIS_NOTIFICATION_DISABLED: "true"
59+
steps:
60+
- uses: actions/checkout@v6
61+
- name: Enable KVM
62+
run: |
63+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
64+
sudo udevadm control --reload-rules
65+
sudo udevadm trigger --name-match=kvm
66+
- name: Set up Java
67+
uses: actions/setup-java@v5
68+
with:
69+
distribution: zulu
70+
java-version: '17'
71+
- name: Download tested APK
72+
uses: actions/download-artifact@v7
73+
with:
74+
name: ogh-debug-apk
75+
path: androidApp/build/outputs/apk/debug
76+
- name: Install pinned Maestro CLI
77+
run: |
78+
curl --fail --location --silent --show-error --output maestro.zip \
79+
"https://github.com/mobile-dev-inc/Maestro/releases/download/cli-${MAESTRO_VERSION}/maestro.zip"
80+
echo "3440825f514f537c6a96bcf5de995780c2a4a7f83a43208fdc95d4f1fecfad3b maestro.zip" | sha256sum --check
81+
unzip -q maestro.zip -d "$RUNNER_TEMP/maestro"
82+
echo "$RUNNER_TEMP/maestro/maestro/bin" >> "$GITHUB_PATH"
83+
- name: Run Maestro end-to-end suite
84+
uses: reactivecircus/android-emulator-runner@v2.37.0
85+
with:
86+
api-level: 35
87+
arch: x86_64
88+
target: google_apis
89+
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back emulated
90+
disable-animations: true
91+
script: |
92+
adb install -r androidApp/build/outputs/apk/debug/androidApp-debug.apk
93+
maestro test .maestro
94+
- name: Upload Maestro diagnostics
95+
if: always()
96+
uses: actions/upload-artifact@v7
97+
with:
98+
name: maestro-report
99+
path: build/reports/maestro
100+
if-no-files-found: ignore

.github/workflows/release.yml

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
workflow_dispatch:
8+
inputs:
9+
version:
10+
description: 'Semantic release tag (for example, v1.0.0)'
11+
required: true
12+
type: string
13+
14+
permissions:
15+
contents: write
16+
17+
jobs:
18+
release:
19+
runs-on: ubuntu-latest
20+
env:
21+
APP_REPOSITORY_URL: ${{ github.server_url }}/${{ github.repository }}
22+
23+
steps:
24+
- uses: actions/checkout@v6
25+
with:
26+
fetch-depth: 0
27+
28+
- name: Set up Java
29+
uses: actions/setup-java@v5
30+
with:
31+
distribution: 'zulu'
32+
java-version: '17'
33+
34+
- name: Setup Android SDK
35+
uses: android-actions/setup-android@v4
36+
37+
- name: Setup Gradle
38+
uses: gradle/actions/setup-gradle@v6
39+
40+
- name: Make gradlew executable
41+
run: chmod +x ./gradlew
42+
43+
- name: Accept Android SDK licenses
44+
run: yes | sdkmanager --licenses || true
45+
46+
- name: Set release version
47+
env:
48+
REQUESTED_VERSION: ${{ inputs.version }}
49+
run: |
50+
if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then
51+
RELEASE_TAG="$REQUESTED_VERSION"
52+
else
53+
RELEASE_TAG="$GITHUB_REF_NAME"
54+
fi
55+
if [[ ! "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
56+
echo "Release version must use a tag such as v1.0.0" >&2
57+
exit 1
58+
fi
59+
VERSION="${RELEASE_TAG#v}"
60+
echo "RELEASE_TAG=$RELEASE_TAG" >> "$GITHUB_ENV"
61+
echo "APP_VERSION_NAME=$VERSION" >> "$GITHUB_ENV"
62+
echo "APP_VERSION_CODE=$GITHUB_RUN_NUMBER" >> "$GITHUB_ENV"
63+
64+
- name: Prepare release signing key
65+
env:
66+
KEYSTORE_BASE64: ${{ secrets.ANDROID_RELEASE_KEYSTORE_BASE64 }}
67+
run: |
68+
if [ -z "$KEYSTORE_BASE64" ]; then
69+
echo "ANDROID_RELEASE_KEYSTORE_BASE64 secret is not configured" >&2
70+
exit 1
71+
fi
72+
KEYSTORE_PATH="$RUNNER_TEMP/ogh-release.jks"
73+
printf '%s' "$KEYSTORE_BASE64" | base64 --decode > "$KEYSTORE_PATH"
74+
chmod 600 "$KEYSTORE_PATH"
75+
echo "RELEASE_KEYSTORE_PATH=$KEYSTORE_PATH" >> "$GITHUB_ENV"
76+
77+
- name: Verify release keystore
78+
env:
79+
RELEASE_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_RELEASE_STORE_PASSWORD }}
80+
RELEASE_KEY_ALIAS: ${{ secrets.ANDROID_RELEASE_KEY_ALIAS }}
81+
RELEASE_KEY_PASSWORD: ${{ secrets.ANDROID_RELEASE_KEY_PASSWORD }}
82+
run: |
83+
if [ -z "$RELEASE_KEYSTORE_PASSWORD" ] || [ -z "$RELEASE_KEY_ALIAS" ] || [ -z "$RELEASE_KEY_PASSWORD" ]; then
84+
echo "Release signing secrets are incomplete" >&2
85+
exit 1
86+
fi
87+
keytool -list \
88+
-keystore "$RELEASE_KEYSTORE_PATH" \
89+
-storepass "$RELEASE_KEYSTORE_PASSWORD" \
90+
-alias "$RELEASE_KEY_ALIAS"
91+
VERIFY_KEYSTORE="$RUNNER_TEMP/ogh-key-entry-check.p12"
92+
VERIFY_PASSWORD=$(openssl rand -hex 24)
93+
keytool -importkeystore -noprompt \
94+
-srckeystore "$RELEASE_KEYSTORE_PATH" \
95+
-srcstorepass "$RELEASE_KEYSTORE_PASSWORD" \
96+
-srcalias "$RELEASE_KEY_ALIAS" \
97+
-srckeypass "$RELEASE_KEY_PASSWORD" \
98+
-destkeystore "$VERIFY_KEYSTORE" \
99+
-deststoretype PKCS12 \
100+
-deststorepass "$VERIFY_PASSWORD"
101+
rm -f "$VERIFY_KEYSTORE"
102+
103+
- name: Test, lint, and build signed release APK
104+
env:
105+
RELEASE_KEYSTORE_PATH: ${{ env.RELEASE_KEYSTORE_PATH }}
106+
RELEASE_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_RELEASE_STORE_PASSWORD }}
107+
RELEASE_KEY_ALIAS: ${{ secrets.ANDROID_RELEASE_KEY_ALIAS }}
108+
RELEASE_KEY_PASSWORD: ${{ secrets.ANDROID_RELEASE_KEY_PASSWORD }}
109+
run: >-
110+
./gradlew --no-daemon
111+
:shared:jvmTest
112+
:androidApp:testDebugUnitTest
113+
:androidApp:lintRelease
114+
:androidApp:assembleRelease
115+
116+
- name: Verify release signature
117+
run: |
118+
APK=$(find androidApp/build/outputs/apk/release -maxdepth 1 -type f -name '*.apk' -print -quit)
119+
if [ -z "$APK" ]; then
120+
echo "Release APK was not produced" >&2
121+
exit 1
122+
fi
123+
APKSIGNER=$(find -L "$ANDROID_HOME/build-tools" -maxdepth 2 \
124+
-type f -name apksigner -print | sort -V | tail -1)
125+
if [ -z "$APKSIGNER" ]; then
126+
echo "apksigner was not found in the Android SDK" >&2
127+
exit 1
128+
fi
129+
"$APKSIGNER" verify --verbose --print-certs "$APK"
130+
131+
- name: Export release version
132+
id: version
133+
run: echo "VERSION=$RELEASE_TAG" >> "$GITHUB_OUTPUT"
134+
135+
- name: Prepare release assets
136+
run: |
137+
mkdir -p release-assets
138+
cp androidApp/build/outputs/apk/release/*.apk \
139+
release-assets/ogh-${{ steps.version.outputs.VERSION }}.apk
140+
cd release-assets
141+
sha256sum ogh-${{ steps.version.outputs.VERSION }}.apk \
142+
> ogh-${{ steps.version.outputs.VERSION }}.sha256
143+
144+
- name: Generate changelog
145+
id: changelog
146+
run: |
147+
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
148+
DELIMITER="changes-$(openssl rand -hex 16)"
149+
{
150+
echo "CHANGES<<$DELIMITER"
151+
if [ -n "$PREV_TAG" ]; then
152+
git log --pretty=format:"- %s" "$PREV_TAG"..HEAD --no-merges
153+
else
154+
git log --pretty=format:"- %s" --no-merges -20
155+
fi
156+
echo
157+
echo "$DELIMITER"
158+
} >> "$GITHUB_OUTPUT"
159+
160+
- name: Create GitHub Release
161+
uses: softprops/action-gh-release@v3
162+
with:
163+
tag_name: ${{ env.RELEASE_TAG }}
164+
target_commitish: ${{ github.sha }}
165+
name: Ogh ${{ steps.version.outputs.VERSION }}
166+
body: |
167+
## What's Changed
168+
169+
${{ steps.changelog.outputs.CHANGES }}
170+
171+
## Assets
172+
173+
- `ogh-${{ steps.version.outputs.VERSION }}.apk` — Android application
174+
- `ogh-${{ steps.version.outputs.VERSION }}.sha256` — SHA256 checksum
175+
176+
## Install
177+
178+
Download the APK and install it on your Android device.
179+
Requires Android 7.0 (API 24) or higher.
180+
files: release-assets/*
181+
generate_release_notes: true

.gitignore

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Gradle
2+
.gradle/
3+
build/
4+
!gradle-wrapper.jar
5+
6+
# IDE
7+
.idea/
8+
*.iml
9+
*.iws
10+
*.ipr
11+
.DS_Store
12+
13+
# Kotlin
14+
.kotlin/
15+
16+
# Android
17+
local.properties
18+
*.apk
19+
*.aab
20+
*.ap_
21+
*.jks
22+
*.keystore
23+
release-assets/
24+
25+
# Nix
26+
result
27+
.direnv/
28+
29+
# OS
30+
*.swp
31+
*~
32+
.env
33+
.ref/
34+
updates.md

.maestro/config.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
flows:
2+
- "flows/*.yaml"
3+
testOutputDir: build/reports/maestro
4+
executionOrder:
5+
continueOnFailure: false
6+
flowsOrder:
7+
- app-navigation
8+
- destination-crud
9+
- youtube-authorization-page
10+
- twitch-authorization-page

0 commit comments

Comments
 (0)