Android Native App#471
Conversation
- Created gradle-wrapper.properties to specify Gradle distribution details. - Added gradlew and gradlew.bat scripts for executing Gradle tasks. - Introduced settings.gradle.kts for managing plugin and dependency repositories.
- Adjusted poster size scale in SettingsStore for better visual consistency. - Integrated audio device module for improved audio handling in streaming. - Updated signaling client to include dynamic resolution based on stream settings. - Implemented mouse delta adjustments for enhanced control responsiveness. - Added new notification system for queue status updates on Android. - Introduced new drawable resources for UI elements including icons and buttons. - Expanded strings.xml with additional UI text for better user guidance. - Improved game variant handling to prevent duplication in public store data. - Refactored CSS styles for queue ad previews to enhance visual clarity and interaction.
- Added session proxy URL handling in Persistence.kt to trim whitespace. - Introduced touch mouse controls and diagnostics in Streaming.kt for improved input handling. - Updated strings.xml with new localization strings for session proxy settings and login prompts. - Implemented Queue Ad playback controls in QueueAdPreview.tsx, allowing users to mute and play/pause ads. - Created AndroidQueueAds.kt for managing session ad states and merging logic. - Added unit tests in AndroidQueueAdsTest.kt to validate ad state merging and playback requirements.
- Added QR code generation functionality in QrCode.kt. - Introduced new methods for encoding text and bytes into QR codes with error correction. - Enhanced streaming stats collection in NativeStreamClient, including real-time bitrate and FPS monitoring. - Updated StreamSettings to adjust for device display aspects. - Improved touch input handling with refined tap detection logic. - Added a new drawable resource for an upward arrow icon. - Updated StatsOverlay component to display current FPS alongside resolution. - Modified WebRTC client diagnostics to initialize decode and render FPS to zero. - Added a new string resource for scrolling to the top action.
…d enhance streaming resolution tests
- Updated color scheme for better visual consistency across the app. - Introduced a new accent color "Hot Pink" and adjusted existing colors. - Enhanced the device login screen with improved layout and focus handling. - Refactored DeviceLoginPanel into separate composables for better readability. - Updated stream resolution handling to support ultrawide aspect ratios. - Removed unused capResolution function from OpenNowViewModel. - Improved touch input handling in NativeStreamInputRouter for better responsiveness. - Added input protocol version handling in NativeStreamClient for better compatibility. - Updated unit tests for stream resolution to cover new mappings and edge cases.
- Added support for device code login specifically for NVIDIA accounts. - Updated session request handling to differentiate between zone and provider base URLs. - Improved session information retrieval by refining IP address extraction logic. - Enhanced UI components to better handle device login prompts and TV-specific login flows. - Introduced new drawable resources for save icons. - Added unit tests for LoginProvider and SdpTools to ensure correct functionality of device code login and SDP parsing.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fbd669dbeb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (storeOptions.length > 1) { | ||
| setLaunchStorePickerGame(game); | ||
| return; |
There was a problem hiding this comment.
Render the store picker before returning
For any game with more than one launch store, this branch sets launchStorePickerGame and returns, but the component never renders a picker from launchStorePickerGame/launchStoreOptions anywhere in the JSX. That makes the Play button a no-op for multi-store catalog entries, even after the user has selected an active store on the card, because storeOptions.length remains greater than 1.
Useful? React with 👍 / 👎.
| <trust-anchors> | ||
| <certificates src="system" /> | ||
| <certificates src="user" /> | ||
| </trust-anchors> |
There was a problem hiding this comment.
Limit user CA trust to non-sensitive traffic
With this base-config, the release Android app trusts user-installed certificate authorities for every HTTPS request, including NVIDIA OAuth/token, GraphQL, and session APIs. On devices with an enterprise or malicious user CA installed, those credentials and session tokens can be MITM'd; if local debugging/proxy support needs user CAs, scope this trust to debug builds or to the specific domains/flows that require it rather than the global base config.
Useful? React with 👍 / 👎.
| .url(validationUrl) | ||
| .headers(cloudMatchHeaders(token, clientId, deviceId, includeOrigin = false)) | ||
| .build() | ||
| val (_, validationText) = http.awaitText(validationRequest) |
There was a problem hiding this comment.
[🟠 High] [🔵 Bug]
claimSession() correctly builds a proxy-capable client for zone hosts (requestHttp) but then performs validation/claim/poll through the base client (http), which bypasses the configured session proxy and can fail session claim in proxied/provider-zone environments. kotlin // android/app/src/main/java/com/opencloudgaming/opennow/GfnApi.kt val requestHttp = sessionProxyHttpClient(settings, http) ... val (_, validationText) = http.awaitText(validationRequest) ... http.awaitText(claimRequest) This breaks launch reliability for users who require proxy routing. Route all session-op HTTP calls in this flow through the same host-aware client (requestHttp) to keep transport behavior consistent.
| result = catalog, | ||
| ) | ||
| Triple(main, library, catalog) | ||
| }.onSuccess { (main, library, catalog) -> |
There was a problem hiding this comment.
[🟠 High] [🔵 Bug]
refreshAfterAuth(session) launches async work that writes game/subscription state without verifying the triggering session is still active when the write occurs, so a logout/account switch can be followed by stale data being written back from an old in-flight job. kotlin // android/app/src/main/java/com/opencloudgaming/opennow/OpenNowViewModel.kt gamesJob = viewModelScope.launch { ... }.onSuccess { (main, library, catalog) -> _state.update { it.copy(games = main, libraryGames = library, catalogResult = catalog, ... ) } } This causes incorrect cross-account UI state and unpredictable session behavior; gate each update on current authSession.user.userId/provider matching the session captured at launch (or cancel all related jobs on logout/switch).
| if (event.actionMasked == MotionEvent.ACTION_DOWN) { | ||
| NativeInputDiagnostics.add("activity touch fallback size=${decorView.width}x${decorView.height}") | ||
| } | ||
| NativeStreamInputRouter.dispatchTouch(event, decorView.width, decorView.height) |
There was a problem hiding this comment.
[🟠 High] [🔵 Bug]
The activity can dispatch a touch once in the pre-view path and again in the fallback path when capture-all is off and super.dispatchTouchEvent() returns false, producing duplicate stream taps/mouse actions. ```kotlin
// android/app/src/main/java/com/opencloudgaming/opennow/MainActivity.kt
val forwarded = NativeStreamInputRouter.dispatchTouch(event, decorView.width, decorView.height)
...
return if (decorView != null) {
NativeStreamInputRouter.dispatchTouch(event, decorView.width, decorView.height)
}
| modifier = Modifier.align(Alignment.BottomCenter), | ||
| ) | ||
| } | ||
| if (!state.settings.hideStreamButtons) { |
There was a problem hiding this comment.
[🟠 High] [🔵 Bug]
Exit controls are rendered only inside if (!state.settings.hideStreamButtons), so enabling that setting suppresses both launcher and panel exit actions while BackHandler just toggles an internal controls flag that is no longer visible. kotlin // android/app/src/main/java/com/opencloudgaming/opennow/OpenNowScreens.kt if (!state.settings.hideStreamButtons) { StreamControlLauncher(... onExit = { exitConfirmOpen = true }) ... } This can trap users in-stream without an in-app exit path. Keep an always-available exit route (e.g., unconditional exit action or dedicated back-press exit confirmation) independent of hideStreamButtons.
| Modifier | ||
| .fillMaxSize() | ||
| .background(Color.Black.copy(alpha = 0.72f)) | ||
| .clickable(enabled = false) {}, |
There was a problem hiding this comment.
[🟡 Medium] [🔵 Bug]
The fullscreen modal backdrop is marked clickable(enabled = false), so outside taps are not consumed by the overlay and can leak to underlying UI while the modal is open. ```kotlin
// android/app/src/main/java/com/opencloudgaming/opennow/OpenNowScreens.kt
BoxWithConstraints(
Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.72f))
.clickable(enabled = false) {},
)
```suggestion
.clickable(enabled = true) {},
| } | ||
| val allCodewords = addErrorCorrection(data, version) | ||
| val builder = QrBuilder(version) | ||
| builder.drawFunctionPatterns() |
There was a problem hiding this comment.
[🟡 Medium] [🔵 Bug]
The encoder supports versions up to 9 but never writes version information bits (required by QR spec for version >= 7), only function patterns/format bits/codewords. kotlin // android/app/src/main/java/com/opencloudgaming/opennow/QrCode.kt val builder = QrBuilder(version) builder.drawFunctionPatterns() builder.drawFormatBits(mask = 0) builder.drawCodewords(allCodewords) For payloads that select versions 7-9, scanners can reject or misread codes. Add version-bit emission in the builder path for version >= 7 before final rendering.
| put("expiresAt", kotlinx.serialization.json.JsonPrimitive(now + CATALOG_CACHE_TTL_MS)) | ||
| put("data", OpenNowJson.encodeToJsonElement(data)) | ||
| } | ||
| prefs.edit().putString(storageKey(key), payload.toString()).apply() |
There was a problem hiding this comment.
[🟡 Medium] [🔵 Bug]
Catalog cache loads honor TTL but expired entries are left in SharedPreferences forever, and keys include user/search/sort/filter dimensions, so storage grows monotonically across sessions/queries. ```kotlin
// android/app/src/main/java/com/opencloudgaming/opennow/Persistence.kt
val expiresAt = obj["expiresAt"]?.jsonPrimitive?.longOrNull ?: return null
if (System.currentTimeMillis() > expiresAt) return null
...
prefs.edit().putString(storageKey(key), payload.toString()).apply()
…rn page - Added `streamReturnPage` to `OpenNowUiState` to manage navigation after streaming. - Implemented `switchAccount` method to allow users to switch between accounts seamlessly. - Updated `logout` method to refresh the UI state after logging out. - Introduced `setDefaultGameVariant` and `chooseStore` methods for better game variant management. - Enhanced `playVariant` and `play` methods to utilize default game variants. - Improved error handling and state management during streaming operations. feat: Update AuthStore to manage active sessions - Added `setActiveSession` method to set the currently active user session. feat: Refactor NativeStreamInputRouter for improved input handling - Introduced system back handler and stream UI active state management. - Enhanced key event handling for better navigation and input control. fix: Improve NativeStreamClient transport handling and reconnection logic - Refactored transport connection and disconnection logic for better reliability. - Added handling for ICE connection states and reconnection attempts. - Improved error handling during signaling and stream setup. chore: Update strings.xml for better user experience - Added new strings for store selection and launcher options. - Updated existing strings for clarity and consistency.
There was a problem hiding this comment.
Pull request overview
This PR adds a native Android target for OpenNOW alongside the existing Electron client, introducing Android build configuration, Kotlin runtime/session logic, JNI diagnostics, resources, and tests. It also includes smaller desktop-side adjustments for queue ad controls, stats display, catalog variant merging, and release workflow behavior.
Changes:
- Added a standalone Android Studio project with Gradle, Compose/WebRTC/Media3 dependencies, manifest/resources, JNI/CMake integration, and unit tests.
- Implemented Android Kotlin logic for auth/session launch flow, queue ads, notifications, persistence, input/WebRTC streaming, QR code, and models.
- Updated Electron renderer/main behavior around queue ad playback controls, FPS display, multi-store launches, public game variant merging, and package/release metadata.
Reviewed changes
Copilot reviewed 51 out of 61 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/release.yml |
Sets bash shell for package version sync step. |
.gitignore |
Allows Android tests while ignoring Android build outputs. |
android/.gitignore |
Adds Android project ignore rules. |
android/README.md |
Documents Android project build/runtime notes. |
android/app/build.gradle.kts |
Defines Android app build, native build, dependencies, and packaging. |
android/app/proguard-rules.pro |
Adds keep rules for WebRTC/JNI/serialization. |
android/app/release/output-metadata.json |
Adds release APK metadata. |
android/app/src/main/AndroidManifest.xml |
Declares Android app permissions, activity, and deep links. |
android/app/src/main/cpp/CMakeLists.txt |
Configures native shared library build. |
android/app/src/main/cpp/opennow_native.cpp |
Adds JNI runtime diagnostics summary. |
android/app/src/main/java/com/opencloudgaming/opennow/AndroidQueueAds.kt |
Adds queue ad merge/removal helpers. |
android/app/src/main/java/com/opencloudgaming/opennow/AndroidQueueStatusNotifier.kt |
Adds queue/session notification support. |
android/app/src/main/java/com/opencloudgaming/opennow/MainActivity.kt |
Adds Android entry activity and input/system UI handling. |
android/app/src/main/java/com/opencloudgaming/opennow/Models.kt |
Adds Android data models/settings/catalog/session types. |
android/app/src/main/java/com/opencloudgaming/opennow/OpenNowViewModel.kt |
Adds Android app state, auth, catalog, launch, queue, and stream orchestration. |
android/app/src/main/java/com/opencloudgaming/opennow/Persistence.kt |
Adds settings/auth/catalog persistence. |
android/app/src/main/java/com/opencloudgaming/opennow/QrCode.kt |
Adds QR code generation. |
android/app/src/main/java/com/opencloudgaming/opennow/QueueLaunchStatus.kt |
Adds queue status display helpers. |
android/app/src/main/java/com/opencloudgaming/opennow/Streaming.kt |
Adds Android WebRTC/signaling/input streaming implementation. |
android/app/src/main/res/drawable/ic_arrow_up.xml |
Adds vector icon resource. |
android/app/src/main/res/drawable/ic_clear.xml |
Adds vector icon resource. |
android/app/src/main/res/drawable/ic_mic.xml |
Adds vector icon resource. |
android/app/src/main/res/drawable/ic_save.xml |
Adds vector icon resource. |
android/app/src/main/res/drawable/ic_save_filled.xml |
Adds vector icon resource. |
android/app/src/main/res/drawable/ic_search.xml |
Adds vector icon resource. |
android/app/src/main/res/drawable/ic_tab_library.xml |
Adds navigation icon resource. |
android/app/src/main/res/drawable/ic_tab_settings.xml |
Adds navigation icon resource. |
android/app/src/main/res/drawable/ic_tab_store.xml |
Adds navigation icon resource. |
android/app/src/main/res/drawable/ic_tab_stream.xml |
Adds navigation icon resource. |
android/app/src/main/res/values/strings.xml |
Adds Android UI strings. |
android/app/src/main/res/values/styles.xml |
Adds Android theme style. |
android/app/src/main/res/xml/network_security_config.xml |
Configures cleartext loopback exceptions. |
android/app/src/test/java/com/opencloudgaming/opennow/AndroidQueueAdsTest.kt |
Tests queue ad merge/removal behavior. |
android/app/src/test/java/com/opencloudgaming/opennow/LoginProviderTest.kt |
Tests device-code login provider eligibility. |
android/app/src/test/java/com/opencloudgaming/opennow/SdpToolsTest.kt |
Tests SDP gamepad mask parsing. |
android/app/src/test/java/com/opencloudgaming/opennow/StreamResolutionTest.kt |
Tests stream resolution/aspect mapping. |
android/build.gradle.kts |
Adds Android root Gradle plugin versions. |
android/gradle.properties |
Adds Android/Gradle project properties. |
android/gradle/wrapper/gradle-wrapper.properties |
Adds Gradle wrapper configuration. |
android/gradlew |
Adds POSIX Gradle wrapper script. |
android/gradlew.bat |
Adds Windows Gradle wrapper script. |
android/settings.gradle.kts |
Adds Android settings and app module include. |
opennow-stable/package-lock.json |
Updates Electron package version metadata. |
opennow-stable/package.json |
Updates Electron package version. |
opennow-stable/src/main/gfn/games.test.ts |
Adds test coverage for combined public store variants. |
opennow-stable/src/main/gfn/games.ts |
Changes catalog/public game merging behavior. |
opennow-stable/src/main/gfn/publicGames.ts |
Adds combined store parsing/title matching helpers. |
opennow-stable/src/renderer/src/App.tsx |
Adds launch-store selection state and queue modal variant preservation. |
opennow-stable/src/renderer/src/components/QueueAdPreview.tsx |
Adds queue ad play/pause and mute controls. |
opennow-stable/src/renderer/src/components/StatsOverlay.tsx |
Shows placeholder FPS when runtime FPS is unavailable. |
opennow-stable/src/renderer/src/gfn/webrtcClient.ts |
Initializes diagnostic FPS values to zero. |
opennow-stable/src/renderer/src/styles.css |
Updates queue ad preview control styling. |
Files not reviewed (1)
- opennow-stable/package-lock.json: Language not supported
| const storeOptions = getLaunchStoreOptions(game, variantByGameId[game.id]); | ||
| if (storeOptions.length > 1) { | ||
| setLaunchStorePickerGame(game); | ||
| return; |
| { | ||
| "name": "opennow-stable", | ||
| "version": "0.4.0", | ||
| "version": "0.3.9", |
| "type": "SINGLE", | ||
| "filters": [], | ||
| "attributes": [], | ||
| "versionCode": 1, |
| .queue-ad-preview-overlay-action, | ||
| .queue-ad-preview-control { |
| current.copy( | ||
| streamSession = currentSession.copy( | ||
| adState = currentSession.adState.copy( | ||
| sessionAds = emptyList(), | ||
| ads = emptyList(), | ||
| serverSentEmptyAds = false, | ||
| ), | ||
| ), | ||
| queueAdActiveId = null, |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
| _state.update { it.copy(launchPhase = "", deviceLoginPrompt = null) } | ||
| } | ||
|
|
||
| fun logout() { |
There was a problem hiding this comment.
[🟡 Medium] [🔵 Bug]
logout()/logoutAll() clear UI/auth state but do not cancel the existing catalog fetch coroutine, so a previously started refresh can still complete and repopulate games/libraryGames after sign-out. kotlin // android/app/src/main/java/com/opencloudgaming/opennow/OpenNowViewModel.kt fun logout() { viewModelScope.launch { authRepository.logout() val nextSession = authStore.activeSession() This leaks stale post-logout data into the UI and breaks predictable auth-bound state transitions; cancel gamesJob (and related in-flight refresh work) before state reset in both logout paths.
| Modifier | ||
| .fillMaxSize() | ||
| .background(Color.Black.copy(alpha = 0.72f)) | ||
| .clickable(enabled = false) {}, |
There was a problem hiding this comment.
[🟡 Medium] [🔵 Bug]
The store-launch overlay backdrop is declared with a disabled clickable modifier, which does not consume pointer input, so taps can pass through to underlying UI while the modal is open. ```kotlin
// android/app/src/main/java/com/opencloudgaming/opennow/OpenNowScreens.kt
BoxWithConstraints(
Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.72f))
.clickable(enabled = false) {},
|
|
||
| val handledX = updateSyntheticDpadKey(lastHatXKeyCode, nextX, event) | ||
| val handledY = updateSyntheticDpadKey(lastHatYKeyCode, nextY, event) | ||
| lastHatXKeyCode = nextX |
There was a problem hiding this comment.
[🟡 Medium] [🔵 Bug]
The activity persists lastHatXKeyCode/lastHatYKeyCode across mode changes and only updates them from incoming hat motion, with no reset when leaving stream/navigation mode. ```kotlin
// android/app/src/main/java/com/opencloudgaming/opennow/MainActivity.kt
val handledX = updateSyntheticDpadKey(lastHatXKeyCode, nextX, event)
val handledY = updateSyntheticDpadKey(lastHatYKeyCode, nextY, event)
lastHatXKeyCode = nextX
lastHatYKeyCode = nextY
| filterIds: List<String>, | ||
| result: CatalogBrowseResult, | ||
| ) { | ||
| save(key("catalog", userId, providerStreamingBaseUrl, searchQuery, sortId, filterIds.sorted().joinToString(",")), result) |
There was a problem hiding this comment.
[🟡 Medium] [🔵 Bug]
Catalog entries are keyed by user/base/search/sort/filter and always written, but expired/old catalog_cache_* keys are never pruned, so SharedPreferences grows without bound as users search/filter over time. ```kotlin
// android/app/src/main/java/com/opencloudgaming/opennow/Persistence.kt
fun saveCatalog(..., result: CatalogBrowseResult) {
save(key("catalog", userId, providerStreamingBaseUrl, searchQuery, sortId, filterIds.sorted().joinToString(",")), result)
}
| <base-config cleartextTrafficPermitted="false"> | ||
| <trust-anchors> | ||
| <certificates src="system" /> | ||
| <certificates src="user" /> |
There was a problem hiding this comment.
[🟠 High] [🔵 Bug]
The base trust store includes src="user", so any user-installed CA can terminate TLS for all app domains using the base config. ```xml
| "type": "SINGLE", | ||
| "filters": [], | ||
| "attributes": [], | ||
| "versionCode": 1, |
There was a problem hiding this comment.
[🟡 Medium] [🔵 Bug]
The checked-in release metadata declares versionCode: 1 while app config sets versionCode = 2, so the packaged artifact metadata is stale/inconsistent with the source build configuration. ```json
// android/app/release/output-metadata.json
"versionCode": 1,
"versionName": "0.3.9-native",
"outputFile": "app-release.apk"
```suggestion
"versionCode": 2,
| const handleInitiatePlay = useCallback(async (game: GameInfo) => { | ||
| const storeOptions = getLaunchStoreOptions(game, variantByGameId[game.id]); | ||
| if (storeOptions.length > 1) { | ||
| setLaunchStorePickerGame(game); |
There was a problem hiding this comment.
[🟠 High] [🔵 Bug]
handleInitiatePlay now short-circuits to setLaunchStorePickerGame(game) when multiple store options exist, but the component render tree still only mounts QueueServerSelectModal and never renders any launch-store picker modal consuming launchStorePickerGame/launchStoreOptions. ```tsx
// opennow-stable/src/renderer/src/App.tsx
const handleInitiatePlay = useCallback(async (game: GameInfo) => {
const storeOptions = getLaunchStoreOptions(game, variantByGameId[game.id]);
if (storeOptions.length > 1) {
setLaunchStorePickerGame(game);
return;
… gating for available resolutions
…penNOW into android-native
|
Capy auto-review is paused for this organization because the usage-cycle auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews. |
…token management in GfnAuthRepository
…ws; add device adjustment tests for stream settings
- Added support for WebRTC decoders in CodecProbe, including hardware decoder checks and codec profiles. - Updated CodecCapability to include WebRTC decoder availability and names. - Improved input channel classification for reliable and partially reliable channels. - Enhanced StreamSettings to prefer AV1 codec when WebRTC decoder is available. - Added tests for input encoder mappings and launch ownership logic. - Introduced AppVersionPanel in Settings for displaying app version and build information. - Refactored external mouse capture logic to improve reliability and handling.
|
|
||
| private fun load(): AppSettings { | ||
| val raw = prefs.getString(KEY_SETTINGS, null) ?: return AppSettings() | ||
| return runCatching { OpenNowJson.decodeFromString<AppSettings>(raw) }.getOrElse { AppSettings() } |
There was a problem hiding this comment.
[🟠 High] [🔵 Bug]
OpenNowJson.decodeFromString<T>(...) is used in this file, but only kotlinx.serialization.encodeToString is imported; the reified decodeFromString extension is unresolved without importing kotlinx.serialization.decodeFromString, causing Android build/typecheck failure. Add the missing import (or switch calls to explicit serializer overloads).
// android/app/src/main/java/com/opencloudgaming/opennow/Persistence.kt
import kotlinx.serialization.encodeToString
...
return runCatching { OpenNowJson.decodeFromString<AppSettings>(raw) }.getOrElse { AppSettings() }| val modules = BooleanArray(size * size) | ||
| private val functionModules = BooleanArray(size * size) | ||
|
|
||
| fun drawFunctionPatterns() { |
There was a problem hiding this comment.
[🟡 Medium] [🔵 Bug]
The encoder can select versions up to 9, but drawFunctionPatterns() never reserves/writes the required version-information areas for versions >= 7; those cells are treated as data during placement, producing malformed QR symbols for longer payloads (notably login links). Either implement version-info patterns for v7+ or cap generated versions to <=6 until supported.
// android/app/src/main/java/com/opencloudgaming/opennow/QrCode.kt
fun drawFunctionPatterns() {
drawFinder(3, 3)
drawFinder(size - 4, 3)
drawFinder(3, size - 4)
for (i in 8 until size - 8) {|
|
||
| const handleInitiatePlay = useCallback(async (game: GameInfo) => { | ||
| const storeOptions = getLaunchStoreOptions(game, variantByGameId[game.id]); | ||
| if (storeOptions.length > 1) { |
There was a problem hiding this comment.
[🟠 High] [🔵 Bug]
handleInitiatePlay now exits early when multiple store options exist, but the render tree still does not mount any modal/component keyed by launchStorePickerGame, so multi-store titles hit a dead-end with no selection UI and cannot continue launch. Render and wire a store-picker modal (select/cancel) to launchStorePickerGame/launchStoreOptions before returning from this branch.
// opennow-stable/src/renderer/src/App.tsx
const storeOptions = getLaunchStoreOptions(game, variantByGameId[game.id]);
if (storeOptions.length > 1) {
setLaunchStorePickerGame(game);
return;
}| { | ||
| "name": "opennow-stable", | ||
| "version": "0.4.0", | ||
| "version": "0.3.9", |
There was a problem hiding this comment.
[🟡 Medium] [🔵 Bug]
This change lowers opennow-stable from 0.4.0 to 0.3.9 in package.json (and lockfile root metadata), which breaks monotonic versioning expected by packaging/update pipelines and can produce downgrade/mis-versioned artifacts. Keep the version non-decreasing unless a coordinated rollback is explicitly intended.
// opennow-stable/package.json
{
"name": "opennow-stable",
"version": "0.3.9",…ustments for low power Android TV
…ttings for improved performance
…Status - Implement tests for AndroidStreamKeepAliveNotifier to verify that a ready stream is kept alive and that queueing or exited streams are not. - Create tests for CellularNetworkStatus to ensure correct cellular generation labels based on network type and display overrides.
…, and fix gamepad focus - Redesign virtual touch controller layout to match GeForce NOW layout and optimize performance using draw-phase drawBehind modifier - Overhaul touch layout editor with independent per-element dragging - Move sensitive GFN auth session tokens and device ID from external storage into secure, internal-only Android SharedPreferences (opennow_auth_secure) - Implement automatic migration (loadAndMigrate) to seamlessly move legacy credentials from external storage to secure internal storage on launch and safely delete legacy files only on confirmed write success - Fix card icon overlap in square launcher cards and add visual D-pad focus indicators on sidebar items, Play button, Bookmark button, and Dismiss button - Hoist infinite transition/shimmer offset to GameGridSkeleton to reuse a single brush via LocalShimmerOffset CompositionLocal - Fix game title text color in stream loading dialog to use TextPrimary (white) instead of default black
Android native
…pad, diagnostics stats, cellular data usage, and game-exit handling
- Multi-style touch controls (V1 solid and V2 outline) with style switcher in settings & stream overlay controls.
- Stacked portrait/landscape bumper & trigger shapes with capsule triggers (LT/RT).
- Unified sliding touch pointer tracking for D-pad with unicode arrowhead labels and no double-blending shadows.
- Swapped scaling logic to correctly map stretch-to-fill and crop-to-aspect settings.
- Removed 3-finger gesture toggle for direct click and silenced touch overlay button tones.
- Implemented virtual touch button press haptic feedback (getar singkat) using android.view.HapticFeedbackConstants.
- Fixed high-contrast text color on dialog backgrounds.
- Prevented false-positive safe video fallbacks when WebRTC stream has already successfully rendered frames.
- Implemented automatic clean stream exit when the GFN session is closed by the server.
- Implemented cellular data usage preview in settings and real-time warnings.
- Fixed double mouse-down inputs in Direct Click touch mode.
- Restored Windows Desktop client identity for ultrawide (21:9) stream support.
- Implemented transient network timeout retries during queue polling to avoid launch timeouts when backgrounding.
- Added 'Dec / Enc' toggle to stream stats items settings to allow hiding decode and encode latency info.
- Added 'Battery Optimization' settings section to request ignoring Android battery optimization limits (always run in background) to prevent background connection timeouts.
- Added 'Loss' toggle to stream stats items settings to allow hiding packet loss percentage info.
- Added high-priority heads-up alert notification ('Queue ready alert' channel) when GFN queue finishes so users are alerted to return to the app before being detected AFK.
android native
… connector - Implemented a test for entitlement failure message in LaunchErrorsTest. - Added a test to ensure metadata enrichment preserves ownership in LaunchOwnershipTest. - Created a test to verify stream recovery timing for TV and mobile in StreamLivenessWatchdogTest. - Added a test for hardware H.265 codec preservation in low power Android TV in StreamSettingsDeviceAdjustmentTest. - Introduced LocalTvConnectorInstrumentedTest to validate local pairing and sign-in process. - Added AndroidDeviceRecommendation for recommending stream settings based on device capabilities. - Implemented LocalTvConnector for managing local TV connections and commands. - Created AndroidTvUiBehaviorTest to validate UI behavior for TV navigation and settings. - Added DiagnosticsSanitizationTest to ensure sensitive information is sanitized in diagnostic exports.
- Removed unused checkSessionJob from OpenNowViewModel. - Simplified stream state handling in recordNativeStreamState. - Enhanced StreamLivenessWatchdog to track media progress separately from transport connectivity. - Updated NativeStreamClient to manage transport stability and reconnect attempts more effectively. - Improved catalog artwork selection logic for mobile and TV cards. - Added tests for catalog artwork behavior and initial stream connection status translations. - Adjusted media stall restart timing constants for better performance.
…l effect/analog triggers, and decoder capability capping - Offload outgoing inputs to a high-priority background thread executor to prevent input thread lag (Issue #630). - Enhance Android TV / D-pad navigation with focus highlights, hover borders, and back button handlers. - Make close button, description disclosure, and checkbox options focusable/clickable on Android TV. - Add support for gradual analog and Hall Effect triggers in streaming input. - Fix virtual stick getting stuck when releasing touch. - Cap stream resolution and FPS dynamically based on device hardware decoder capabilities (Issue #645). - Allow safe fallback and low-power TV profiles to use non-strict resolution capping. - Check cancellationApplied flag in queue status notifications. - Include unit tests for decoder capability mapping and resolution checks.
Android Native
…ased outside stick area
…, and improve stream settings handling
…penNOW into android-native
- Use Guide-only (Home) for Steam overlay instead of Guide+A to prevent accidental A presses - Add controller mouse emulation mode (Left analog stick + A button) - Expose Mouse mode to all profiles and support touch gamepad mouse emulation - Enable D-pad Left/Right to adjust slider values in Settings and Stream Controls - Support Android TV remote back key for stream controls and enhance D-pad focus styles - Update touch settings defaults and layout reset to restore all default properties - Exit cleanly to library when signaling receives HTTP 503/404/410 session termination (excluding transient HTTP 503 / WS 1001 errors) - Swap Stretch to fill and Stretch to zoom labels to match actual rendering behavior - Map touch gamepad button B to right-click in left-stick mouse emulation - Detect 2-finger tap in trackpad/finger mouse mode to trigger right-click click - Resolve choppy/laggy physical gamepad analog mouse movement using a 60Hz loop - Fix physical gamepad mouse loop immediate termination race condition - Update Mouse mode (Left stick) hints to mention B button right-clicks - Add focus highlight borders and background styles to Settings and Stream Control sliders - Ensure controller KEYCODE_BACK escapes to game stream instead of local UI - Release both left and right mouse buttons when mouse emulation is disabled - Clear gamepad physical stick caches when physical controllers disconnect or reset - Initialize mouse emulation state from Settings database on session start
Android Native
- Removed unused Button imports in OpenNowSettingsControls.kt and OpenNowSettingsPanels.kt. - Updated DebugLogsPanel to conditionally display log upload options based on Android TV profile. - Introduced rememberDeviceHasBattery function to check for battery presence and adjusted BatteryOptimizationPanel visibility accordingly. - Enhanced SettingsScreen to improve controller navigation handling and focus management. - Added new Button composable for consistent focus treatment across TV and controller interactions. - Improved StreamSettingsDeviceAdjustmentTest to ensure codec handling remains consistent across resolutions. - Added unit tests for game details description handling and battery optimization visibility logic. - Updated GfnApiTest to validate HDR and SDR color metadata in claim requests.
- Bump versionCode to 45 and versionName to "0.9.0" in build.gradle.kts. - Add new resolutions and aspect ratios in StreamingProfileInstrumentedTest. - Implement manual authentication token parsing in GfnApi.kt. - Introduce back navigation handling for Android 13+ in MainActivity. - Add HDR streaming plan check in Models.kt. - Update HDR settings logic in OpenNowSettingsScreens.kt. - Enhance login tools visibility in OpenNowViewModel.kt. - Add RapidTapTracker for tap sequence tracking. - Create tests for ManualTokenSignIn and RapidTapTracker. - Modify strings.xml to reflect HDR settings changes.
- Introduced microphone settings in strings.xml with descriptions and permission handling. - Implemented Android bug report feature with metadata handling and file attachments. - Created SessionTimerAnchorStore for managing session start times. - Added tests for microphone support, bug report creation, and session timer functionality. - Updated existing tests to cover new features and ensure stability. - Refactored stream handling logic to improve session management and signaling failure responses.
- Updated the FPS cap from 360 to 240 in various components to align with performance optimizations. - Introduced a LowPowerStreamWarning composable to notify users about potential performance issues on low-powered devices. - Enhanced codec diagnostic reporting to include constrained runtime profiles. - Refactored input handling for gamepad controls to improve responsiveness and maintainability. - Added tests to validate new FPS settings and ensure proper handling of constrained runtimes. - Adjusted stream settings to ensure compatibility with low-power devices while preserving user experience.
…penNOW into android-native
…zoom functionality in StreamScreen
…fnApi and Streaming classes for improved identity handling and microphone track management; add tests for cloud match identity and microphone cleanup
- Introduced a new SessionReport class to encapsulate session metrics and findings. - Added detailed session report generation logic, including scoring and recommendations based on network performance. - Implemented tests for session report generation, ensuring accurate scoring and contextual recommendations. - Updated StreamSettings and StreamResolution tests to reflect changes in resolution handling and codec adjustments. - Enhanced StreamLivenessWatchdog tests to validate recovery thresholds and fallback mechanisms. - Improved SdpTools tests to ensure correct SDP generation for various resolutions and codecs. - Refactored existing tests for clarity and maintainability, ensuring comprehensive coverage of new features.
This pull request introduces a new native Android project for OpenNOW, adding a complete Android Studio project structure, build configuration, native code integration, and essential runtime logic. The changes lay the groundwork for building, running, and distributing the OpenNOW native Android app, including Kotlin/Compose UI, JNI/C++ media diagnostics, queue/ad/session management, and notification support.
Key changes include:
Project Initialization and Build System:
.gitignore,README.md, and Gradle build configuration for native Kotlin/Compose and JNI/C++ integration. This includes dependency setup for Compose, WebRTC, Media3, OkHttp, and Kotlin serialization, as well as external native build (CMake) and ProGuard rules. (android/.gitignore,android/README.md,android/app/build.gradle.kts,android/app/proguard-rules.pro) [1] [2] [3] [4]Native Code and Media Diagnostics:
opennow_nativeshared library, exposing a native runtime summary for diagnostics, and linking to Android's media and logging libraries. (android/app/src/main/cpp/CMakeLists.txt,android/app/src/main/cpp/opennow_native.cpp) [1] [2]Application Manifest and Permissions:
AndroidManifest.xmlspecifying required permissions (network, audio, notifications), hardware/software features, activity launch modes, and intent filters for deep linking and local server communication. (android/app/src/main/AndroidManifest.xml)Queue, Ads, and Notification Logic:
AndroidQueueAds.kt) and a notification manager for queue/session status updates (AndroidQueueStatusNotifier.kt), supporting ad playback, session state merging, and system notifications. (android/app/src/main/java/com/opencloudgaming/opennow/AndroidQueueAds.kt,android/app/src/main/java/com/opencloudgaming/opennow/AndroidQueueStatusNotifier.kt) [1] [2]Build Output Metadata:
android/app/release/output-metadata.json)