Goo.Android
0.31.0
dotnet add package Goo.Android --version 0.31.0
NuGet\Install-Package Goo.Android -Version 0.31.0
<PackageReference Include="Goo.Android" Version="0.31.0" />
<PackageVersion Include="Goo.Android" Version="0.31.0" />
<PackageReference Include="Goo.Android" />
paket add Goo.Android --version 0.31.0
#r "nuget: Goo.Android, 0.31.0"
#:package Goo.Android@0.31.0
#addin nuget:?package=Goo.Android&version=0.31.0
#tool nuget:?package=Goo.Android&version=0.31.0
Android integration
Android starter
dotnet new install Goo.Templates
dotnet new goo-android --name HelloGoo --application-id com.example.hellogoo
dotnet build HelloGoo/HelloGoo.gsproj -c Release
- Requires the .NET Android workload, its supported Android SDK and JDK.
- Includes an Activity, a shared G# library, Vulkan manifest rules and ARM64/x64 RIDs.
- Use
--goo-versionto select the matching Goo and Goo.Android release. - Set
AndroidSdkDirectoryandJavaSdkDirectoryif tool discovery fails.
Shared application
Put application Cells and a Window factory in a shared net10.0 G# library.
Desktop calls Application.Run(window). Android returns the same Window from
GooActivity.CreateWindow():
using Android.App;
using Android.Content.PM;
using Goo.Android;
[Activity(MainLauncher = true, Exported = true,
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize
| ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize
| ConfigChanges.Density | ConfigChanges.KeyboardHidden)]
public sealed class MainActivity : GooActivity
{
protected override Goo.Window CreateWindow() => MyApplication.CreateWindow();
}
Reference Goo.Android from a net10.0-android app. Set
SupportedOSPlatformVersion to 33. Both hosts share the same control tree,
editor, renderer and assets.
Activation
private static bool subscribed;
protected override Goo.Window CreateWindow()
{
if (!subscribed)
{
Goo.Application.Activated += activation =>
Android.Util.Log.Info("Goo", activation.Kind.ToString());
subscribed = true;
}
return MyApplication.CreateWindow();
}
| Android input | Goo activation | Data |
|---|---|---|
Goo.Android.Notification action |
Notification |
Goo.NotificationId, Goo.ActionId, Goo.Payload extras |
| Launcher | Launch |
None |
Data URI other than content: or file: |
OpenUri |
Target |
content:, file:, or ClipData URIs |
OpenDocuments |
Documents, in source order |
- Register once before
CreateWindow()returns. - Cold launch and
OnNewIntentuse the same UI-thread event. IsInitialmarks the first process launch. Activity recreation does not replay the old Intent.- Override
OnNewIntentonly if needed. Call base, then readLastActivationStatusfor queue or size failures. - The starter uses SingleTop. Add your app's URI filters and MIME types to its manifest.
- Open document URIs with
ContentResolver, not file paths. - Android owns URI grants. Use
TakePersistableUriPermissiononly when the sender offers it. - Copy data before a temporary URI grant expires.
Device requirements
- Android 13 / API 33 or newer, with a Vulkan 1.3 implementation and Goo's required Vulkan features and identity presentation transform. Android API level alone does not establish GPU support.
- ARM64 phones and tablets use
android-arm64. x64 emulators useandroid-x64. - A Vulkan-capable emulator is required. Query its actual device support with
adb shell cmd gpu vkjsonbefore testing. - Declare the required Vulkan version in the app manifest:
<uses-feature android:name="android.hardware.vulkan.version"
android:version="0x00403000" android:required="true" />
<uses-feature android:name="android.hardware.vulkan.level"
android:version="1" android:required="true" />
The API requirement follows the Android Vulkan support table. Native payloads support 16 KB page alignment.
View and lifecycle ownership
GooActivity owns a GooView and forwards pause, resume, and destruction.
Native layout applies system bar, display cutout, and keyboard insets before
sizing the SurfaceView. Framebuffer and input coordinates share that content rectangle.
In a native activity, construct new GooView(context, window) on the Android
main thread. Add it to the native layout and call Pause(), Resume(), and
Dispose() from the matching activity callbacks. Size it inside safe and keyboard
insets. Do not call Application.Run(window) or Window.Pump() or attach the Window to another view.
Surface replacement keeps the Window and mounted Cells. Activity destruction closes the Window. Store process-recreation data in app state.
Activity state restoration
private string draft = "";
protected override Goo.Window CreateWindow()
{
draft = SavedInstanceState?.GetString("draft") ?? "";
return new Goo.Window
{
Root = new Goo.TextEntry { Value = draft, OnChange = value => draft = value }
};
}
protected override void OnSaveInstanceState(Android.OS.Bundle outState)
{
outState.PutString("draft", draft);
base.OnSaveInstanceState(outState);
}
SavedInstanceStateholds Android's Bundle duringCreateWindow(). It is null on a fresh launch.- The app owns keys, serialization, versioning, and state restoration.
- Call base from
OnSaveInstanceState. Goo does not serialize Cells or native resources. - Store large or durable data outside the Bundle.
- Handled orientation and size changes keep the current tree.
Frame scheduling
Choreographer schedules only on frame demand or the next animation/timer deadline.
Pause cancels frames and transient input. Resume resets the clock.
Use HasFrameDemand and FrameDemandChanged on the Android UI thread for host
frame-rate policy. The event reports demand changes, not each frame.
Documents and URI streams
var picked = await Documents.ShowPickerAsync(Goo.FileDialogKind.OpenFile);
if (picked.Status != Goo.FileDialogStatus.Success) return;
var opened = await Documents.OpenReadAsync(picked.Uris[0]);
if (opened.Status != DocumentStatus.Success) return;
await using var stream = opened.Stream!;
Use GooActivity.Documents on the Activity's main thread to launch a picker.
Documents stay as content:// URIs, not filesystem paths.
| Operation | Contract |
|---|---|
| Open | MIME filters and optional multiple selection. The default requests Read access. |
| Save | Creates one new document. Supply one concrete MIME type and Write access. It is not an overwrite dialog. |
| Folder | Returns one tree URI. Open a child document URI to read its bytes. |
| Streams | OpenReadAsync and OpenWriteAsync open off the UI thread. The caller owns each successful stream. Writes replace and truncate contents. Later I/O uses normal .NET stream semantics. |
| Grants | GrantedAccess and CanPersist report returned flags. Call PersistAccess or ReleaseAccess on the main thread. Store URI values in app state. Persistence is never automatic. |
| Lifetime | Pause and surface loss keep the picker open. Cancellation or Activity destruction completes it with Cancelled. A replacement Activity does not restore an old task. |
| Failures | Stream and grant results distinguish Unsupported, Denied, NotFound, Cancelled and Failed. A failed stream result has no stream. |
Input and keyboard policy
Touch IDs, mouse wheel, physical keys, focus, clipboard, UTF-16 selection, composition, and semantic editor commands use shared Goo input. Stale IME connections cannot edit a different focused field. Password fields suppress surrounding and extracted text returned to the IME.
Each Android move event updates all contact positions before gesture recognition. An accepted gesture receives one coherent update per move event.
TextEntry{
InputOptions: TextInputOptions{
Purpose: InputPurpose.Email,
EnterAction: EnterAction.Next,
AutofillHints: []string{"emailAddress"},
},
}
- Works with
TextEntry.InputOptionsandTextEditor.InputOptions. - Android maps hints to
EditorInfoand focused-field autofill. - Autofill replaces the focused value. This is not a multi-field autofill form.
- Hints do not validate input.
- Without options, entries request Done and multiline editors request None.
InputPurpose.Passwordmasks TextEntry likePassword: true. Use TextEntry for protected values.- Desktop ignores other keyboard and autofill hints.
GooView.DismissKeyboardOnSubmit defaults to true. IME Done sends Submit and
hides the keyboard. Set it to false to keep the keyboard open after Submit.
Focus changes and lifecycle handling still control text input. Next and Previous
move focus.
Android owns physical key repeat timing. GooView dispatches each native key-down, including repeats, and releases its mapped Goo key before returning. A missing key-up cannot leave Goo repeat active. Mapped key-up events are consumed without another edit. Each key-down commits printable characters unless Ctrl or Alt is pressed.
Accessibility
TextEntry{ Accessibility: Accessibility{ Name: "Name" } }
GooViewexposes Goo semantics as Android virtual accessibility nodes.- The bridge runs when an Android accessibility service is active.
- Give custom controls a role, name, state and supported actions through
Accessibility. - Text, password masks, selection and editing use the shared Goo action path.
GooViewownsWindow.AccessibilityAdapter. Leave the slot empty before construction.- Enable TalkBack in Android settings. No app permission or extra package is needed.
- Reading focus does not take keyboard focus or open the keyboard.
- Touch exploration uses Goo stacking, transforms and clipping.
- Surface replacement keeps virtual IDs and clears reading focus.
- The virtual subtree is available after the replacement presentation updates.
- Text, selection, scroll and state changes send events for the changed nodes.
- Root, native geometry and Surface changes refresh the host subtree.
Accessibility.Livesets polite or assertive live-region behavior.- Atomic regions send their full current text. Resume does not replay old announcements.
Before release, record the Android and TalkBack versions and check:
| Check | Expected result |
|---|---|
| Explore by touch and swipe | Correct control name, role, state and order. No canvas stop. |
| Double-tap controls | One action. Text entry opens the keyboard. |
| Read and select mixed-direction text | Whole characters and correct selection. |
| Edit a password | No document text in speech. |
| Change a live region | Correct polite or assertive speech. Atomic regions read the whole update. |
| Scroll, rotate, background and resume | Correct bounds. No stale focus or repeated live update. |
| Disable or remove controls, then replace the Surface | No stale nodes or focus. Navigation, editing, and activation still work. |
Native popup surfaces
using let popup = PopupWindow(window, TooltipCell{}, PopupKind.Tooltip)
let result = popup.Open(anchor.BorderBox, maximum: LayoutSize(240, 120))
- Uses Android
PopupWindowwith a separate GooView and Vulkan surface. - A menu receives focus and dismisses on Back or outside input. A tooltip receives no input.
- Coordinates use owner-content layout units. Size follows the owner's UiScale and display density.
- Omit
sizeto measure the root once, withinmaximumand the visible display area.UpdatePlacementcan measure it again. - A measured root percentage uses the smaller of
maximumand the visible display area as its basis. The popup takes the resolved size, so 50% uses half that basis and 100% fills it. - The native window may extend beyond its owner view. Android clips it to the app's display bounds.
- Pause, surface loss, view detach and owner disposal dismiss the popup chain.
- A missing live owner view returns
Unsupported. Native window failure returnsFailed. - Portal and widgets Menu keep their in-window behavior.
Transparent surfaces
Set Window.Transparent before constructing GooView. It places a translucent
SurfaceView above native views, which show through transparent pixels.
Vulkan prefers premultiplied composite alpha. Android also allows inherited alpha
when exposed, because GooView configures premultiplied transparency.
Other embedded hosts default EmbeddedWindowHost.AllowInheritedCompositeAlpha()
to false. Override it only for the same compositor contract. Swapchain creation
fails without premultiplied or explicitly permitted inherited alpha.
Assets and native payloads
Use FontSource and premultiplied RGBA ImageSource. Shared libraries can embed
font bytes with EmbeddedResource and register them on either platform.
Android sans-serif uses Roboto with generic Noto Sans fallback.
Goo.dll embeds Vulkan shaders, including in APKs. No extraction directory is needed.
Goo contains HarfBuzz and hb-gpu for both Android RIDs. Goo.Android source builds
create a native archive for project references. NuGet omits it and uses Goo's
payloads to avoid duplicate libraries. The renderer adds no SDL, Skia, OpenGL,
FreeType, or C++ runtime payload.
Notifications
Reference Goo.Android and use AndroidNotifications.Open(activity, options)
on the owner's UI thread. Supply ChannelId, ChannelName and a drawable
SmallIconResource. The adapter supplies POST_NOTIFICATIONS.
- Request permission from an explicit user action.
ShowAsyncnever requests it. - Android controls channel importance and notification settings.
- Default clicks target the concrete
GooActivityand use its shared activation event. - Activity destruction closes the service and ends pending permission work.
- Accepted notifications and their
PendingIntentroutes survive service disposal. - See notification outcomes.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0-android36.0 is compatible. |
-
net10.0-android36.0
- Goo (>= 0.31.0)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Goo.Android:
| Package | Downloads |
|---|---|
|
Goo.NativeMenus
Optional operating-system menu bars and context menus for Goo. |
|
|
Goo.NativeDialogs
Owned native message boxes for Goo windows. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.31.0 | 7 | 11/14/2026 |
| 0.30.0 | 102 | 11/11/2026 |
| 0.29.1 | 95 | 11/8/2026 |
| 0.29.0 | 89 | 11/8/2026 |
| 0.28.7 | 95 | 11/7/2026 |
| 0.28.6 | 96 | 11/7/2026 |
| 0.28.5 | 92 | 11/7/2026 |
| 0.28.4 | 94 | 11/7/2026 |
| 0.28.3 | 98 | 11/6/2026 |
| 0.28.2 | 96 | 11/6/2026 |
| 0.28.1 | 93 | 11/5/2026 |
| 0.27.11 | 100 | 11/5/2026 |
| 0.27.10 | 97 | 11/4/2026 |
| 0.27.9 | 101 | 11/4/2026 |
| 0.27.8 | 98 | 11/4/2026 |
| 0.27.7 | 102 | 11/3/2026 |
| 0.27.6 | 93 | 11/3/2026 |
| 0.27.5 | 91 | 11/3/2026 |
| 0.27.4 | 95 | 11/3/2026 |
| 0.27.3 | 94 | 11/3/2026 |