Skip to content
Closed
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
3f18813
Add Capacitor Android target and platform abstraction
zortos293 Apr 10, 2026
ec1dd5f
Fix Android review issues
zortos293 Apr 10, 2026
7b88b70
Unify Android package naming
zortos293 Apr 10, 2026
698591d
Add Android APK CI build
zortos293 Apr 10, 2026
787f173
Remove Capacitor HTTP plugin dependency
zortos293 Apr 10, 2026
3eb35a2
Match Android auth callback to desktop
zortos293 Apr 10, 2026
5b26e58
Fix Android localhost auth plugin compile
zortos293 Apr 10, 2026
07b3379
Fix Android localhost OAuth callback flow
zortos293 Apr 10, 2026
c24021b
Match Android OAuth redirect to desktop localhost flow
zortos293 Apr 10, 2026
528cbdc
Revert "Match Android OAuth redirect to desktop localhost flow"
zortos293 Apr 10, 2026
889d9d7
Harden Android localhost auth callback handling
zortos293 Apr 10, 2026
01c6b94
Merge dev into Android Capacitor target
zortos293 Apr 10, 2026
dc2ae3e
Use WebView-based Android OAuth interception
zortos293 Apr 10, 2026
f3703d6
Fix Android auth state after WebView login
zortos293 Apr 11, 2026
2e7bf3d
Align Android session creation with desktop payload
zortos293 Apr 11, 2026
14cb1fd
Merge branch 'dev' into capy/android-capacitor-target
Kief5555 Apr 11, 2026
4f1fdd1
Resolve Package lock issues
Kief5555 Apr 11, 2026
b839606
Fix linter errors
Kief5555 Apr 11, 2026
d7ff05a
Merge origin/dev into capy/android-capacitor-target
zortos293 Apr 17, 2026
75b94ad
Apply Android PR review fixes
zortos293 Apr 17, 2026
e6946d3
Fix Android session parity follow-ups
zortos293 Apr 17, 2026
915c4ff
Fix Android follow-up parity issues
zortos293 Apr 17, 2026
0b83461
Fix Android runtime follow-up regressions
zortos293 Apr 17, 2026
28034b9
Fix Android splash and media write follow-ups
zortos293 Apr 17, 2026
ad9e892
Fix Android polling and packaging follow-ups
zortos293 Apr 17, 2026
ca53b28
Fix Android session conflict and relay follow-ups
zortos293 Apr 17, 2026
f84b464
Fix Android wrapper and relay candidate replay
zortos293 Apr 17, 2026
777d462
Fix avatar fallback escaping and gating
zortos293 Apr 17, 2026
8017b40
Fix Android fullscreen back and ICE fallback
zortos293 Apr 17, 2026
438b2f6
Fix Android signaling endpoint selection
zortos293 Apr 17, 2026
a18064b
Preserve Android active session signaling hosts
zortos293 Apr 18, 2026
c22e682
Scope relay ICE replay to current offer
zortos293 Apr 18, 2026
2f68193
Align Android ad and signaling handling
zortos293 Apr 18, 2026
369fee4
Fix Android parity follow-up issues
zortos293 Apr 18, 2026
02abc96
Refine Android ICE scope handling
zortos293 Apr 18, 2026
0095b6c
Fix Android signaling and fullscreen follow-ups
zortos293 Apr 18, 2026
1c16b18
Fix Android active session and relay issues
zortos293 Apr 18, 2026
0cbfff8
Enhance Android authentication flow and update dependencies
Kief5555 Apr 22, 2026
8c20049
Add session management enhancements for Android, including new sessio…
Kief5555 Apr 25, 2026
b41bacb
Implement Android catalog caching and UI adjustments for improved per…
Kief5555 Apr 25, 2026
a3bf6bf
Bump version to 0.3.3 in package.json and package-lock.json
Kief5555 Apr 25, 2026
8688baf
Enhance Android build configuration and implement TCP ping functionality
Kief5555 Apr 25, 2026
b143819
Implement virtual gamepad support and enhance error handling UI
Kief5555 Apr 25, 2026
e10bc1f
Implement Android touch controls and mousepad functionality with UI a…
Kief5555 Apr 25, 2026
7569fc0
Add touch mouse tap support to AndroidMousePad and WebRTC client
Kief5555 Apr 26, 2026
3537fe2
Implement text input and key press handling for Android controls; enh…
Kief5555 Apr 27, 2026
eeb6b05
bump build count
Kief5555 Apr 29, 2026
2c6dfaa
Update Android version code to 30007; enhance numeric ID validation a…
Kief5555 May 3, 2026
83bb2fe
Enhance StreamView video handling; update className based on video fr…
Kief5555 May 3, 2026
180714d
Update Android version code to 30008; enhance error handling and logg…
Kief5555 May 3, 2026
2b926f2
Update Android version code to 30009; enhance session handling and er…
Kief5555 May 3, 2026
08b3c3d
Add OpenNowAndroid plugin for immersive fullscreen and pointer captur…
Kief5555 May 5, 2026
1a469be
feat: update Android version code to 30011
Kief5555 May 6, 2026
a791e0e
feat: enhance Android touch controls and gamepad input handling
Kief5555 May 7, 2026
09a5f36
fix: change onResume method visibility to public in MainActivity
Kief5555 May 7, 2026
da6e485
feat: implement pointer capture refresh mechanism in OpenNowAndroidPl…
Kief5555 May 7, 2026
e5ce482
feat: enhance Android compatibility and diagnostics with physical gam…
Kief5555 May 8, 2026
9651ba1
feat: optimize pointer capture handling in StreamView and OpenNowAndr…
Kief5555 May 9, 2026
9e6ce3b
fix: update androidVersionCode to 30013 in gradle.properties
Kief5555 May 9, 2026
ca2ed06
feat: enhance Android support with new features and settings adjustments
Kief5555 May 9, 2026
7ed4787
feat: enhance Android support with new settings and UI adjustments
Kief5555 May 9, 2026
41bf176
feat: update Android pointer capture handling and enhance performance…
Kief5555 May 9, 2026
6ef4f05
feat: implement dark mode support with background color adjustments a…
Kief5555 May 9, 2026
f156d75
feat: add Android performance info retrieval and low power touch cont…
Kief5555 May 9, 2026
661e485
feat(android): update version code and enhance mouse input support
Kief5555 May 9, 2026
ae57cbf
feat(android): enhance touch control support and add ultrawide resolu…
Kief5555 May 9, 2026
d635b61
feat(android): update version code to 30019
Kief5555 May 9, 2026
70e8f87
feat(android): enhance controller navigation and touch handling for A…
Kief5555 May 10, 2026
3f8aea6
feat: implement launch store picker modal and enhance store selection…
Kief5555 May 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/workflows/auto-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,53 @@ jobs:
opennow-stable/dist-release/*.AppImage
opennow-stable/dist-release/*.deb
if-no-files-found: error

android-apk:
name: android-apk
runs-on: ubuntu-24.04

defaults:
run:
working-directory: opennow-stable

env:
npm_config_audit: "false"
npm_config_fund: "false"

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: "**/package-lock.json"

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"
cache: gradle

- name: Setup Android SDK
uses: android-actions/setup-android@v3
Comment on lines +142 to +143
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[🟡 Medium] [🟡 Investigate]

android-actions/setup-android only installs the command-line tools and platform-tools by default, but this PR pins the Android project to API 36 in @opennow-stable/android/variables.gradle. That means the new job is only green as long as the hosted Ubuntu image happens to prebundle the required SDK platform/build-tools; on a fresh or rotated runner, ./gradlew assembleDebug will fail before compilation with a missing android-36/build-tools error. Explicitly installing the platform (and matching build-tools) in the workflow makes the Android build reproducible.

# .github/workflows/auto-build.yml
- name: Setup Android SDK
  uses: android-actions/setup-android@v3

- name: Install dependencies
Suggested change
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Setup Android SDK
uses: android-actions/setup-android@v3
with:
packages: "platforms;android-36 build-tools;35.0.0"


- name: Install dependencies
run: npm ci --prefer-offline --no-audit --progress=false

- name: Build web assets and sync Android project
run: npm run cap:sync:android

- name: Build debug APK
working-directory: opennow-stable/android
run: ./gradlew --no-daemon assembleDebug

- name: Upload Android APK artifact
uses: actions/upload-artifact@v4
with:
name: opennow-stable-android-apk
path: opennow-stable/android/app/build/outputs/apk/debug/app-debug.apk
if-no-files-found: error
43 changes: 37 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,14 @@

## Overview

OpenNOW is a community-built Electron app for playing GeForce NOW from an open-source desktop client. The active implementation lives in [`opennow-stable/`](opennow-stable) and uses Electron, React, and TypeScript across the main, preload, and renderer processes.
OpenNOW is a community-built GeForce NOW client. The active implementation lives in [`opennow-stable/`](opennow-stable) and now supports two runtime targets from the same React renderer: Electron for desktop and Capacitor for Android.

The project aims to give players a transparent, customizable alternative to the official client without hiding the technical parts from contributors.

## Highlights

- Open-source desktop client for Windows, macOS, and Linux
- Experimental Android target via Capacitor using the shared React renderer in a WebView
- Catalog and public game browsing with search and library-aware session handling
- Stream controls for codec, resolution, FPS, aspect ratio, region, and quality preferences
- In-stream diagnostics overlay with latency, packet loss, decode, and render stats
Expand All @@ -81,6 +82,8 @@ Current packaging targets:
| Linux x64 | `AppImage`, `deb` |
| Linux ARM64 | `AppImage`, `deb` |

For Android testing, GitHub Actions now builds an unsigned debug APK artifact on pull requests and manual workflow runs. Download it from the `auto-build` workflow artifacts; it is intended for testing, not release distribution.

### Develop Locally

From the repository root:
Expand Down Expand Up @@ -122,15 +125,43 @@ For a fuller setup guide, see [docs/development.md](docs/development.md).

## Architecture At A Glance

OpenNOW is split into three Electron layers:
OpenNOW uses a shared renderer with thin platform adapters:

| Layer | Tech | Responsibility |
| --- | --- | --- |
| Main | Electron + Node.js | OAuth, CloudMatch/session orchestration, signaling, caching, local file handling |
| Preload | Electron `contextBridge` | Safe IPC bridge between the app shell and UI |
| Renderer | React + TypeScript | Login flow, browsing, settings, WebRTC playback, diagnostics, controls |
| Main | Electron + Node.js | Desktop-only OAuth, IPC, local filesystem/media, cache, window management |
| Preload | Electron `contextBridge` | Desktop bridge exposing the existing OpenNOW API surface |
| Renderer | React + TypeScript | Shared login flow, browsing, settings, WebRTC playback, diagnostics, controls |
| Capacitor Android | Capacitor + WebView | Android shell, deep links, Preferences/filesystem storage, browser-based signaling |

The code lives under [`opennow-stable/src/`](opennow-stable/src), with shared TypeScript types and platform-neutral helpers in [`opennow-stable/src/shared/`](opennow-stable/src/shared). The renderer now consumes [`src/renderer/src/platform/`](opennow-stable/src/renderer/src/platform/) instead of hard-coding `window.openNow`.

## Android Status

The Android target is an initial pass intended to run the core OpenNOW flow inside a Capacitor WebView. Current Android support includes:

- auth session restore
- login via native Android WebView interception while preserving the exact desktop redirect URI contract (`http://localhost:<port>`)
- provider and region loading
- main/library/public game catalog fetches
- session create, poll, claim, and stop
- direct signaling from the WebView
- settings persistence
- screenshots and recordings stored in the app data directory

Known Android limitations in this pass:

- no desktop-style quit action
- no pointer-lock toggle semantics
- no Electron log export or cache deletion flow
- no show-in-folder integration for media
- screenshot export/save-as remains desktop-only
- some desktop shortcut UX is hidden or non-applicable on touch devices

Android auth now mirrors the desktop localhost redirect contract by opening NVIDIA login in a native Android WebView and intercepting the navigation to `http://localhost:<port>` inside the Android shell instead of running a real loopback callback server.

CI also produces a testable Android APK artifact from the Capacitor target so reviewers can install and validate the current branch without making a release build.

The code lives under [`opennow-stable/src/`](opennow-stable/src), with shared TypeScript types and IPC contracts in [`opennow-stable/src/shared/`](opennow-stable/src/shared).

## Contributing

Expand Down
49 changes: 47 additions & 2 deletions docs/development.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Development Guide

This guide covers the active Electron-based OpenNOW client in [`opennow-stable/`](../opennow-stable).
This guide covers the active OpenNOW app in [`opennow-stable/`](../opennow-stable), including the existing Electron desktop target and the new Capacitor Android target.

## Prerequisites

Expand Down Expand Up @@ -35,12 +35,20 @@ npm run dev
npm run preview
npm run typecheck
npm run build
npm run build:web
npm run cap:sync:android
npm run cap:open:android
npm run dist
npm run dist:signed
```

GitHub Actions also builds a testable unsigned Android debug APK artifact in the `auto-build` workflow for pull requests and manual dispatch runs.

## Workspace Layout

The Android shell lives in [`opennow-stable/android/`](../opennow-stable/android), Capacitor config lives in [`opennow-stable/capacitor.config.ts`](../opennow-stable/capacitor.config.ts), and the renderer platform abstraction lives in [`opennow-stable/src/renderer/src/platform/`](../opennow-stable/src/renderer/src/platform/).


```text
opennow-stable/
├── src/
Expand Down Expand Up @@ -91,7 +99,8 @@ The renderer is a React app responsible for:
- Browsing the catalog and public listings
- Managing stream launch state and session recovery
- Rendering the WebRTC stream
- Handling controller input, shortcuts, stats overlay, screenshots, recordings, and settings UI
- Handling controller input, stats overlay, screenshots, recordings, and settings UI
- Choosing the active runtime implementation through `src/renderer/src/platform/`

Key entry points:

Expand Down Expand Up @@ -155,10 +164,46 @@ Current build matrix:
| Linux x64 | `AppImage`, `deb` |
| Linux ARM64 | `AppImage`, `deb` |

Additional CI output:

| Target | Output |
| --- | --- |
| Android testing | Unsigned debug APK artifact uploaded from `auto-build` |

## Notes For Contributors

- The active app is the Electron client. If you see older references to previous implementations, prefer `opennow-stable/`.
- Root-level npm scripts are convenience wrappers around the `opennow-stable` workspace.
- Before opening a PR, run `npm run typecheck` and `npm run build`.

For contribution workflow details, see [`.github/CONTRIBUTING.md`](../.github/CONTRIBUTING.md).


## Android Workflow

Build and sync web assets into the Android project:

```bash
cd opennow-stable
npm run cap:sync:android
```

Build a local test APK:

```bash
cd opennow-stable
npm run cap:sync:android
cd android
./gradlew assembleDebug
```

Open the Android project in Android Studio:

```bash
cd opennow-stable
npm run cap:open:android
```

Current Android support is limited to the core cloud-gaming path. Android login now follows the same localhost redirect contract as desktop (`http://localhost:<port>` for both authorize and token exchange), but the Android shell intercepts that navigation inside a native WebView instead of hosting a real localhost callback server. Desktop-specific features such as quit app, pointer-lock toggles, log export, cache deletion, show-in-folder actions, and screenshot save-as are intentionally gated or unavailable on Android in this pass.

For CI-based testing, use the APK artifact uploaded by the `auto-build` workflow. It is a debug/testing package and is not release-signed.
101 changes: 101 additions & 0 deletions opennow-stable/android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore

# Built application files
*.apk
*.aar
*.ap_
*.aab

# Files for the ART/Dalvik VM
*.dex

# Java class files
*.class

# Generated files
bin/
gen/
out/
# Uncomment the following line in case you need and you don't have the release build type files in your app
# release/

# Gradle files
.gradle/
build/

# Local configuration file (sdk path, etc)
local.properties

# Proguard folder generated by Eclipse
proguard/

# Log Files
*.log

# Android Studio Navigation editor temp files
.navigation/

# Android Studio captures folder
captures/

# IntelliJ
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml

# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore

# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/

# Google Services (e.g. APIs or Firebase)
# google-services.json

# Freeline
freeline.py
freeline/
freeline_project_description.json

# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md

# Version control
vcs.xml

# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/

# Android Profiling
*.hprof

# Cordova plugins for Capacitor
capacitor-cordova-android-plugins

# Copied web assets
app/src/main/assets/public

# Generated Config files
app/src/main/assets/capacitor.config.json
app/src/main/assets/capacitor.plugins.json
app/src/main/res/xml/config.xml
2 changes: 2 additions & 0 deletions opennow-stable/android/app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/build/*
!/build/.npmkeep
54 changes: 54 additions & 0 deletions opennow-stable/android/app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
apply plugin: 'com.android.application'

android {
namespace = "com.opencloudgaming.opennow"
compileSdk = rootProject.ext.compileSdkVersion
defaultConfig {
applicationId "com.opencloudgaming.opennow"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[🟡 Medium] [🔵 Bug]

The new Android target hard-codes its install metadata instead of following the project version that the existing release workflow already updates in opennow-stable/package.json. gradle // opennow-stable/android/app/build.gradle minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" Because Android upgrades require a monotonically increasing versionCode, every future APK built from this config will still identify as build 1 regardless of the actual release being shipped, so the Android artifact will drift from the repo's release version and normal upgrade/install flows will break once a second real release exists. Derive versionName/versionCode from the same source the release pipeline already uses (for example via Gradle properties injected from package.json or CI) instead of keeping them fixed literals.

versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
// Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61
ignoreAssetsPattern = '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

repositories {
flatDir{
dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs'
}
}

dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"
implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion"
implementation project(':capacitor-android')
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
implementation project(':capacitor-cordova-android-plugins')
}

apply from: 'capacitor.build.gradle'

try {
def servicesJSON = file('google-services.json')
if (servicesJSON.text) {
apply plugin: 'com.google.gms.google-services'
}
} catch(Exception e) {
logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work")
}
24 changes: 24 additions & 0 deletions opennow-stable/android/app/capacitor.build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN

android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_21
targetCompatibility JavaVersion.VERSION_21
}
}

apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies {
implementation project(':capacitor-app')
implementation project(':capacitor-browser')
implementation project(':capacitor-device')
implementation project(':capacitor-filesystem')
implementation project(':capacitor-preferences')
implementation project(':capacitor-status-bar')

}


if (hasProperty('postBuildExtras')) {
postBuildExtras()
}
Loading
Loading