-
Notifications
You must be signed in to change notification settings - Fork 20
ENG-74: Add GetConfiguration() to Unity AirConsole plugin #154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
marc-n-dream
wants to merge
12
commits into
master
Choose a base branch
from
feature/ENG-74-platform-capability-configuration
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
2e4603c
f B: Add GetConfiguration() to Unity AirConsole plugin
marc-n-dream e25334a
d B: Improve GetConfiguration() XML doc with screen-only note (ENG-74…
marc-n-dream f465f43
t B: Add 3 Unity EditMode tests for GetConfiguration() (M3)
marc-n-dream d9d4f86
chore(settings): bump RequiredMinimumVersion to 1.11.0 and update HTM…
marc-n-dream 7549a0e
feat(examples): add GetConfiguration example scene
marc-n-dream 86e8594
ai: Update AGENTS.md
marc-n-dream e7c5b8e
feat: Add support for Client Configuration API
marc-n-dream 63d4b3d
docs: Add Changelog information
marc-n-dream 840cd76
fix: address PR review feedback on GetConfiguration PR
marc-n-dream b035914
r: Update to latest API surface
marc-n-dream d6f8d24
r: Update GetGameConfiguration to final API shape
marc-n-dream f71c17d
r: Address outdated GetConfiguration name
marc-n-dream File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,82 +1,100 @@ | ||
| # AGENTS GUIDE — Assets/AirConsole Focus | ||
|
|
||
| The `airconsole-unity-plugin` project targets Unity 2022.3.x and ships the full AirConsole runtime, editor tooling, Android TV helpers, and WebGL controller templates. This guide is tailored to the contents under `Assets/AirConsole`. | ||
|
|
||
| ## What Lives in Assets/AirConsole | ||
|
|
||
| - `scripts/Runtime`: Core API surface (`AirConsole.cs`), websocket/webview bridges (`WebsocketListener.cs`, `WebViewManager.cs`), runtime settings (`Settings.cs`, `AirconsoleRuntimeSettings.cs`, `NativeGameSizingSettings.cs`), logging (`AirConsoleLogger.cs`, `DebugLevel.cs`), platform runtime configurators, and Android plugin wrappers under `Runtime/Plugin/Android`. | ||
| - `scripts/Editor`: Custom inspectors/windows (`Inspector.cs`, `SettingWindow.cs`, `PlayMode.cs`, `SafeAreaTester.cs`, `WebListener.cs`), build automation (`BuildAutomation/*`), project maintenance checks (`ProjectMaintenance/*`), and plugin developer helpers (`PluginDevelopment/*`). | ||
| - `scripts/SupportCheck`: Unity version/platform validator ASMDEF used in-editor. | ||
| - `scripts/Tests`: ASMDEF-scoped EditMode, PlayMode, and Editor tests. | ||
| - `examples`: Sample scenes (`basic`, `pong`, `platformer`, `swipe`, `game-states`, `safe-area`, `translations`) for manual validation. | ||
| - `extras`: Controller template HTML and `HighScoreHelper.cs`. | ||
| - `plugins`: Managed dependencies (`Newtonsoft.Json.dll`, `websocket-sharp.dll`) plus Android native plugin payload under `plugins/Android`. | ||
| - `unity-webview`: Embedded WebView plugin with runtime/editor asmdefs and native binaries under `Plugins`. | ||
| - `unity-webview` is an imported external dependency. You are not allowed to alter it. | ||
| - Docs and misc: `Documentation_1.7.pdf`, `Documentation for AndroidTV.pdf`, `Upgrade_Plugin_Version.md`, `README.txt`, `airconsole.prefs`, and the distributable `airconsole-code.unitypackage`. | ||
|
|
||
| ## Runtime Essentials | ||
|
|
||
| - **API + bridge**: `AirConsole.cs` exposes the public API; `WebsocketListener` routes JSON actions/events; `WebViewManager` coordinates the embedded webview. Add new events in both the listener and the API surface. | ||
| - **Config assets**: `AirconsoleRuntimeSettings.asset` and `NativeGameSizingSettings.asset` (in `scripts/Runtime/Resources`) hold defaults for ports, safe-area sizing, etc. Adjust via the editor UI instead of hardcoding. | ||
| - **Platform setup**: Implementations of `IRuntimeConfigurator` (`AndroidRuntimeConfigurator`, `WebGLRuntimeConfigurator`, `EditorRuntimeConfigurator`) handle platform-specific initialization. Extend these rather than branching deep in the API. | ||
| - **Android plugin wrappers**: `PluginManager`, `AndroidUnityUtils`, and callback helpers (`UnityPluginExecutionCallback`, `UnityPluginStringCallback`, `GenericUnityPluginCallback`, `UnityAndroidObjectProvider`) encapsulate Java bridge calls. Keep new native hooks consistent with these wrappers. | ||
| - **Logging & diagnostics**: Use `AirConsoleLogger` with `DebugLevel` enums to respect runtime debug settings. | ||
|
|
||
| ## Editor & Build Tooling | ||
|
|
||
| - **UI/inspectors**: `SettingWindow`, `Inspector`, `PlayMode`, `SafeAreaTester`, and `WebListener` provide editor UI and simulator hooks. Prefer extending these instead of adding ad-hoc windows. | ||
| - **Build automation** (`scripts/Editor/BuildAutomation`): | ||
| - `PreBuildProcessing` and `PostBuildProcess` wrap pre/post hooks; post steps validate WebGL template AirConsole JS versions. | ||
| - `AndroidManifestProcessor`, `AndroidGradleProcessor`, `AndroidProjectProcessor`, `AndroidManifest`, and `AndroidXMLDocument` mutate generated Android projects/manifests. Use these helpers instead of manual IO. | ||
| - **Project maintenance** (`ProjectMaintenance`): `ProjectDependencyCheck`, `SemVerCheck`, and `ProjectConfigurationCheck` enforce Unity version and project settings; `ProjectPreferenceManager/ProjectPreferences` manage stored preferences; `ProjectUpgradeEditor` supports upgrade flows. | ||
| - **Plugin development helpers** (`PluginDevelopment`): `BuildHelper` hosts build/packaging entry points (Web/Android) and can auto-zip or auto-commit; `DevelopmentTools` includes additional packager utilities. | ||
|
|
||
| ## Tests, Samples, Validation | ||
|
|
||
| - Tests live under `scripts/Tests` (EditMode/PlayMode/Editor asmdefs). Run via Unity Test Runner; some PlayMode tests expect Android as the active build target. | ||
| - Manual checks: leverage sample scenes in `examples` to verify controller flows, safe-area behavior, translations, and Android TV input. | ||
| - WebGL validation: keep `PostBuildProcess` version checks aligned with the AirConsole JS used in `Assets/WebGLTemplates/AirConsole-*`. | ||
|
|
||
| ## Dependencies & Third-Party | ||
|
|
||
| - Managed DLLs: `Newtonsoft.Json.dll` and `websocket-sharp.dll` are under `plugins/` and referenced by runtime asmdefs. | ||
| - Embedded webview: `unity-webview` includes runtime/editor code plus native libs under `unity-webview/Plugins`. | ||
| - Android native pieces are under `plugins/Android`; ensure gradle/manifest processors remain compatible when updating them. | ||
|
|
||
| ## Common Workflows | ||
|
|
||
| - **Add/extend an API event**: Update `WebsocketListener` to parse/route the action, expose it in `AirConsole`, and wire through logging; add tests where possible. | ||
| - **Adjust runtime defaults**: Modify `AirconsoleRuntimeSettings.asset` or `NativeGameSizingSettings.asset` (via editor windows). Avoid hardcoded constants in code. | ||
| - **Android manifest/Gradle edits**: Implement changes in `AndroidManifestProcessor`/`AndroidGradleProcessor`/`AndroidProjectProcessor`; ensure `useCustomMainManifest` stays enabled. | ||
| - **WebGL template/JS version bumps**: Update constants/regex in `PostBuildProcess`, refresh `Settings.RequiredMinimumVersion` if needed, and sync HTML in `Assets/WebGLTemplates/AirConsole-*`. | ||
| - **Packaging/exports**: Use `BuildHelper.BuildWeb()`, `BuildHelper.BuildAndroid()`, or `BuildHelper.BuildAndroidInternal()`; they run configuration checks and can zip outputs. | ||
|
|
||
| ## Guardrails & Gotchas | ||
|
|
||
| - Conditional compilation is heavily used: `#if !DISABLE_AIRCONSOLE`, `UNITY_ANDROID`, `UNITY_EDITOR`, `UNITY_WEBGL`. Keep new code behind appropriate defines and asmdefs. | ||
| - Respect asmdef boundaries (`AirConsole.Runtime`, `AirConsole.Editor`, `AirConsole.SupportCheck`, `AirConsole.Examples`, `unity-webview`); place new files in matching folders. | ||
| - Embedded websocket/webview server underpins editor play; avoid breaking `WebsocketListener`/`WebViewManager` interactions (ports come from runtime settings). | ||
| - Safe-area handling is central for Android TV/Automotive—maintain `OnSafeAreaChanged` flows and avoid state changes before safe-area readiness. | ||
| - Build helper may auto-commit with `build: TIMESTAMP` when uncommitted changes exist; be mindful when running locally. | ||
|
|
||
| ## Event Queue Architecture (Thread-Safe Callbacks) | ||
|
|
||
| The WebView plugin uses a thread-safe event queue to handle callbacks from native code (Android only at this point): | ||
|
|
||
| ### Event Processing Lifecycle | ||
|
|
||
| Events are processed at multiple points to ensure timely delivery: | ||
|
|
||
| 1. **`Update()`**: Primary drain point (every frame) | ||
| 2. **`LateUpdate()`**: Secondary drain (end of frame) | ||
| 3. **`FixedUpdate()`**: Tertiary drain (physics frame / fixed timestep / before rendering) | ||
| 4. **`OnApplicationPause(false)`**: Flush on resume | ||
| 5. **`OnDisable()`**: Flush before component teardown | ||
| 6. **`OnApplicationQuit()`**: Final flush before app exit | ||
|
|
||
| ## Additional References | ||
|
|
||
| - `Documentation_1.7.pdf` (setup/examples), `Documentation for AndroidTV.pdf`, and `Upgrade_Plugin_Version.md` for upgrade flows. | ||
| - Controller HTML template: `extras/controller-template.html`. | ||
| - Quick intro: `Assets/AirConsole/README.txt`; preferences file `airconsole.prefs`. | ||
| # airconsole-unity-plugin | ||
| ## OVERVIEW | ||
| AirConsole SDK for Unity. | ||
| C# wrapper around the AirConsole browser API. | ||
| Android native bridge for Android builds. | ||
| WebGL JavaScript generation for browser builds. | ||
| Main targets: Android and WebGL. | ||
| Bridge languages: C#, Java or Kotlin, JavaScript. | ||
| Two message patterns exist. | ||
| Old pattern: polling through `GetMessage()`. | ||
| New pattern: queue based event delivery. | ||
| Prefer queue based delivery for new Android bridge behavior. | ||
| ## ENTRY POINT | ||
| Main SDK entry: `Assets/AirConsole/scripts/Runtime/AirConsole.cs`. | ||
| `AirConsole.cs` owns the singleton. | ||
| The singleton is the main public API surface. | ||
| Unity scenes call this API for connection state, messages, device data, and platform actions. | ||
| Android bridge entry uses `AndroidJavaProxy`. | ||
| Native callbacks enter Unity through proxy handlers. | ||
| WebGL entry uses generated JavaScript. | ||
| Generated JS exposes the runtime browser interface for Unity WebGL builds. | ||
| ## ARCHITECTURE | ||
| Old Android message path: | ||
| 1. Native side stores incoming messages. | ||
| 2. Unity polls with `GetMessage()`. | ||
| 3. `UnitySendMessageDispatcher` forwards payloads into Unity. | ||
| 4. C# parses payloads and raises SDK callbacks. | ||
| New Android message path: | ||
| 1. Native side calls an `AndroidJavaProxy` callback. | ||
| 2. Proxy receives payloads off the Unity main thread. | ||
| 3. Proxy writes work items into a `ConcurrentQueue`. | ||
| 4. Unity main thread drains the queue. | ||
| 5. SDK events fire during main thread processing. | ||
| WebGL message path: | ||
| 1. Build postprocess generates JS from C# reflection. | ||
| 2. Runtime JS binds Unity calls to AirConsole browser API calls. | ||
| 3. Browser callbacks route back into Unity. | ||
| ## ANDROID BRIDGE | ||
| Android bridge code lives under `Assets/AirConsole/plugins/Android/`. | ||
| `PluginManager` initializes native bridge state. | ||
|
marc-n-dream marked this conversation as resolved.
|
||
| `PluginManager` connects Unity runtime and Android plugin code. | ||
| `AndroidJavaProxy` handles messages from native code. | ||
| Proxy handlers must stay small. | ||
| Proxy handlers must not call Unity APIs directly from background threads. | ||
| Use the main thread queue for message delivery. | ||
| Use `ConcurrentQueue` for thread safe handoff. | ||
| Drain queued actions or messages from Unity main thread update flow. | ||
| Preserve payload shape when moving between native and C#. | ||
| ## WEBGL EXPORT | ||
| Build postprocess generates `airconsole-unity-plugin.js`. | ||
| Generation uses C# reflection. | ||
| Generated JS mirrors the callable SDK surface needed by WebGL builds. | ||
| Runtime JS interface talks to the AirConsole browser API. | ||
| Unity WebGL calls route through generated JS functions. | ||
| Browser events route back into Unity objects. | ||
| Change the generator or C# source instead of hand editing generated JS. | ||
| ## STRUCTURE | ||
| `Assets/AirConsole/` contains the main SDK. | ||
| `Assets/AirConsole/scripts/Runtime/AirConsole.cs` contains the singleton and main public API. | ||
| `Assets/AirConsole/plugins/Android/` contains native bridge code and Android plugin assets. | ||
| `Assets/AirConsole/scripts/Editor/` contains editor tooling and build postprocessing. | ||
| `Assets/AirConsole/scripts/Editor/BuildAutomation/` is the build generation area. | ||
| Sample scenes live under the AirConsole assets tree. | ||
| ## WHERE TO LOOK | ||
| Main SDK: `Assets/AirConsole/scripts/Runtime/AirConsole.cs`. | ||
| Android bridge: `Assets/AirConsole/plugins/Android/`. | ||
| Build generation: `Assets/AirConsole/scripts/Editor/BuildAutomation/`. | ||
| Generated WebGL interface and sample integration: `airconsole-unity-plugin.js`, sample scenes. | ||
| ## BUILD POSTPROCESSING | ||
| Unity build postprocess auto runs after supported builds. | ||
| It generates Android manifests when needed. | ||
| It handles AndroidX setup. | ||
| It copies Android AAR files into the build output. | ||
| It links iOS frameworks for supported exports. | ||
| It generates WebGL JavaScript interface files. | ||
| It can export `.unitypackage` distribution packages. | ||
| ## TESTING | ||
| Automated tests are limited. | ||
| Use Unity Test Runner when relevant. | ||
| Most confidence comes from integration testing. | ||
| Run sample scenes for message flow checks. | ||
| Test Android bridge behavior on Android builds. | ||
| Test WebGL behavior in exported WebGL builds. | ||
| Verify old polling behavior when touching legacy paths. | ||
| Verify queue based behavior when touching new bridge paths. | ||
| ## EXPORTS | ||
| Distribution output is a `.unitypackage`. | ||
| The package includes AirConsole `Assets` and all required `Plugins` content. | ||
| The package must include Android bridge assets and WebGL runtime generation support. | ||
| ## COMMANDS | ||
| Unity build: run from Unity Editor or configured Unity batchmode build. | ||
| PostprocessBuild auto runs during Unity build. | ||
| Export package: use Unity export package workflow for `.unitypackage`. | ||
| Run tests: use Unity Test Runner for EditMode and PlayMode suites. | ||
| ## FORBIDDEN | ||
| NEVER hardcode device IDs. | ||
| NO platform specific code outside `plugins/`. | ||
| Do not bypass the main thread queue for Android callbacks. | ||
| Do not call Unity APIs directly from Android background callbacks. | ||
| Do not edit generated WebGL JS when generator changes are required. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # Assets/AirConsole | ||
|
|
||
| This subtree contains the shipped Unity runtime, editor tooling, examples, and native wrappers. | ||
|
|
||
| ## Local Focus Areas | ||
|
|
||
| - Runtime API and bridges: `scripts/Runtime/` | ||
| - Editor tooling and build processors: `scripts/Editor/` | ||
| - Automated tests: `scripts/Tests/` | ||
| - Examples and manual smoke scenes: `examples/` | ||
|
|
||
| ## Local Invariants | ||
|
|
||
| - Extend platform-specific behavior through the existing configurators and Android wrapper classes instead of scattering `#if UNITY_*` checks. | ||
| - Keep asmdef boundaries intact (`AirConsole.Runtime`, `AirConsole.Editor`, `AirConsole.SupportCheck`, examples/tests). | ||
| - Preserve safe-area and main-thread callback behavior when touching Android or WebView flows. | ||
| - Treat `unity-webview/` as vendored unless the task explicitly belongs there. | ||
|
|
||
| ## Common Checks | ||
|
|
||
| - Add matching runtime/editor tests where feasible. | ||
| - For manifest or Gradle output changes, use the existing build processors in `scripts/Editor/BuildAutomation/`. | ||
|
|
||
| ## Learnings | ||
|
|
||
| - _None yet._ |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,24 +1,25 @@ | ||
| { | ||
| "name": "AirConsole.Examples", | ||
| "rootNamespace": "NDream.AirConsole.Examples", | ||
| "references": [ | ||
| "AirConsole.Runtime" | ||
| ], | ||
| "includePlatforms": [ | ||
| "Android", | ||
| "Editor", | ||
| "WebGL" | ||
| ], | ||
| "excludePlatforms": [], | ||
| "allowUnsafeCode": false, | ||
| "overrideReferences": true, | ||
| "precompiledReferences": [ | ||
| "Newtonsoft.Json.dll" | ||
| ], | ||
| "autoReferenced": true, | ||
| "defineConstraints": [ | ||
| "UNITY_2022_3_OR_NEWER" | ||
| ], | ||
| "versionDefines": [], | ||
| "noEngineReferences": false | ||
| "name": "AirConsole.Examples", | ||
| "rootNamespace": "NDream.AirConsole.Examples", | ||
| "references": [ | ||
| "AirConsole.Runtime", | ||
| "Unity.TextMeshPro" | ||
| ], | ||
| "includePlatforms": [ | ||
| "Android", | ||
| "Editor", | ||
| "WebGL" | ||
| ], | ||
| "excludePlatforms": [], | ||
| "allowUnsafeCode": false, | ||
| "overrideReferences": true, | ||
| "precompiledReferences": [ | ||
| "Newtonsoft.Json.dll" | ||
| ], | ||
| "autoReferenced": true, | ||
| "defineConstraints": [ | ||
| "UNITY_2022_3_OR_NEWER" | ||
| ], | ||
| "versionDefines": [], | ||
| "noEngineReferences": false | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
54 changes: 54 additions & 0 deletions
54
Assets/AirConsole/examples/configuration/ExampleConfigurationLogic.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| namespace NDream.AirConsole.Examples { | ||
| using UnityEngine; | ||
| using Newtonsoft.Json.Linq; | ||
|
|
||
| public class ExampleConfigurationLogic : MonoBehaviour { | ||
| public TMPro.TMP_Text logWindow; | ||
|
|
||
| #if !DISABLE_AIRCONSOLE | ||
| private void Awake() { | ||
| AirConsole.instance.onReady += OnReady; | ||
| AirConsole.instance.onConnect += OnConnect; | ||
| logWindow.text = "Waiting for AirConsole...\n"; | ||
| } | ||
|
|
||
| private void OnReady(string code) { | ||
| logWindow.text = "AirConsole ready!\n\n"; | ||
|
|
||
| JToken config = AirConsole.instance.GetGameConfiguration(); | ||
| if (config == null) { | ||
| logWindow.text += "Configuration: not provided by platform\n"; | ||
| return; | ||
| } | ||
|
|
||
| logWindow.text += "=== Platform Configuration ===\n"; | ||
|
|
||
| // Video capability flags | ||
| JToken transparentVideoSupport = config["transparentVideoSupport"]; | ||
| if (transparentVideoSupport != null) { | ||
| logWindow.text += "Supports transparent video: " + (bool)transparentVideoSupport + "\n"; | ||
| } | ||
|
|
||
| JToken unityVideoSupport = config["unityVideoSupport"]; | ||
| if (unityVideoSupport != null) { | ||
| logWindow.text += "Supports video playback in Unity: " + (bool)unityVideoSupport + "\n"; | ||
| } | ||
|
|
||
| // Demonstrate safe access for future/optional fields | ||
| JToken touchScreen = config["touchScreen"]; | ||
| logWindow.text += "Touch Screen: " + (touchScreen != null ? touchScreen.ToString() : "N/A") + "\n"; | ||
| } | ||
|
|
||
| private void OnConnect(int deviceId) { | ||
| logWindow.text += "Device " + deviceId + " connected\n"; | ||
| } | ||
|
|
||
| private void OnDestroy() { | ||
| if (AirConsole.instance != null) { | ||
| AirConsole.instance.onReady -= OnReady; | ||
| AirConsole.instance.onConnect -= OnConnect; | ||
| } | ||
| } | ||
| #endif | ||
| } | ||
| } |
11 changes: 11 additions & 0 deletions
11
Assets/AirConsole/examples/configuration/ExampleConfigurationLogic.cs.meta
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
41 changes: 41 additions & 0 deletions
41
Assets/AirConsole/examples/configuration/configuration-controller.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
|
|
||
| <html> | ||
| <head> | ||
| <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0"/> | ||
| <script src="https://www.airconsole.com/api/airconsole-1.11.0.js"></script> | ||
|
|
||
| <style type=text/css> | ||
| html, body { | ||
| margin: 0; | ||
| padding: 0; | ||
| width: 100%; | ||
| height: 100%; | ||
| background: #1a1a1a; | ||
| } | ||
|
|
||
| #content { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| height: 100%; | ||
| font-family: Arial, sans-serif; | ||
| font-size: 24px; | ||
| color: white; | ||
| text-align: center; | ||
| padding: 20px; | ||
| box-sizing: border-box; | ||
| } | ||
| </style> | ||
|
|
||
| <script type="text/javascript"> | ||
| function App() { | ||
| var me = this; | ||
| me.airconsole = new AirConsole({"orientation": "portrait"}); | ||
| } | ||
| </script> | ||
|
|
||
| </head> | ||
| <body onload="window.app = new App()"> | ||
| <div id="content">Look at the screen!</div> | ||
| </body> | ||
| </html> |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.