mirror of
https://github.com/moeru-ai/airi.git
synced 2026-10-29 18:48:55 +08:00
refactor(stage-tamagotchi-kirie): use Kirie QR scanner
This commit is contained in:
@@ -17,3 +17,6 @@ obj/
|
||||
*.aar
|
||||
*.xcframework
|
||||
addons/kirie_notifications/
|
||||
addons/kirie_qr/
|
||||
ios/plugins/kirie_qr/
|
||||
ios/plugins/kirie_camera/
|
||||
|
||||
@@ -173,10 +173,10 @@ Update acceptance requires download, integrity checks, installation, relaunch, a
|
||||
|
||||
## Dependency baseline
|
||||
|
||||
The notification migration uses Kirie npm, NuGet, and the Godot addon at 0.13.0.
|
||||
The [README](README.md#notifications) records the notification plugin setup.
|
||||
The notification and QR integrations use Kirie npm, NuGet, and the Godot addon at 0.15.0.
|
||||
The [README](README.md#qr-scanning) records the QR and notification plugin setup.
|
||||
This integration has no new device acceptance evidence yet.
|
||||
Godot and Godot.NET.Sdk use 4.7.2. Godot CEF uses 2.0.0.
|
||||
Godot and Godot.NET.Sdk use 4.7.2. The core plugin selects Godot CEF 2.1.0.
|
||||
The [0.8.0 release](https://github.com/moeru-ai/godot-kirie/releases/tag/v0.8.0) supplies official artifacts.
|
||||
|
||||
The previous 0.8.0 addon SHA-256 is `e5063840514ea85b199e98755accc0b08661ed3b19f39c786cf740bcb51e7a2a`.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
This application runs the Stage Tamagotchi Vue renderer in a Godot host.
|
||||
The shared `src-web` renderer supports desktop and Android exports.
|
||||
Kirie supplies the WebView, IPC transport, and notifications. AIRI owns application services and native surfaces.
|
||||
Kirie supplies the WebView, IPC transport, notifications, and QR scanning. AIRI owns application services and native surfaces.
|
||||
|
||||
Use this application for Kirie desktop and Android development.
|
||||
The Electron application remains supported and supplies the behavior reference.
|
||||
@@ -43,13 +43,14 @@ Install the Kirie addon that matches the npm and NuGet packages:
|
||||
```sh
|
||||
mise x -- pnpm kirie doctor --fix plugin:core
|
||||
mise x -- pnpm kirie doctor --fix plugin:notifications
|
||||
mise x -- pnpm kirie doctor --fix plugin:qr
|
||||
```
|
||||
|
||||
The core plugin installs the bundled `0.13.0` addon and checks its declared version.
|
||||
The core plugin installs the bundled `0.15.0` addon and checks its declared version.
|
||||
The addon supplies `addons/kirie/`, including `KirieClient.cs`. Git ignores this installed dependency.
|
||||
After a Kirie version change, run the command again.
|
||||
The 0.13.0 npm bundle omits the Android AAR.
|
||||
For Android, extract `addons/kirie/libraries/android/Kirie-release.aar` from the official `v0.13.0` release archive into this application.
|
||||
The core npm bundle omits the Android AAR.
|
||||
For Android, extract `addons/kirie/libraries/android/Kirie-release.aar` from the official `v0.15.0` release archive into this application.
|
||||
|
||||
Inspect the installed environment:
|
||||
|
||||
@@ -66,8 +67,8 @@ If CEF is absent or stale, install the configured backend:
|
||||
mise x -- pnpm kirie doctor --fix godot-cef
|
||||
```
|
||||
|
||||
The [installer](https://github.com/moeru-ai/godot-kirie/blob/v0.13.0/packages/cli/src/commands/doctor/addons.ts) verifies the Godot CEF archive and records its checksum under `.godot/kirie/`.
|
||||
The [doctor](https://github.com/moeru-ai/godot-kirie/blob/v0.13.0/packages/cli/src/commands/doctor/index.ts) checks required addon files, the Kirie version, and the CEF checksum marker.
|
||||
The [installer](https://github.com/gd-kirie/gd-kirie/blob/v0.15.0/packages/cli/src/commands/doctor/addons.ts) verifies the Godot CEF archive and records its checksum under `.godot/kirie/`.
|
||||
The [doctor](https://github.com/gd-kirie/gd-kirie/blob/v0.15.0/packages/cli/src/commands/doctor/index.ts) checks required addon files, the Kirie version, and the CEF checksum marker.
|
||||
It does not hash installed native files or inspect macOS signatures.
|
||||
|
||||
After a CEF version change, repeat installation.
|
||||
@@ -83,13 +84,13 @@ mise x -- pnpm kirie dev
|
||||
```
|
||||
|
||||
`kirie dev` starts Vite, Godot, and CEF renderers.
|
||||
The [dev command](https://github.com/moeru-ai/godot-kirie/blob/v0.13.0/packages/cli/src/commands/dev.ts) reuses the last C# build.
|
||||
The [dev command](https://github.com/gd-kirie/gd-kirie/blob/v0.15.0/packages/cli/src/commands/dev.ts) reuses the last C# build.
|
||||
After a C# change, repeat both commands.
|
||||
Spotlight opens through its global shortcut and has no in-app entry point.
|
||||
|
||||
## Notifications
|
||||
|
||||
Notifications use the published Kirie 0.13.0 npm and NuGet packages.
|
||||
Notifications use the published Kirie 0.15.0 npm and NuGet packages.
|
||||
Restore C# dependencies with `dotnet restore StageTamagotchiKirie.csproj`.
|
||||
|
||||
The `@gd-kirie/notifications` plugin supplies the renderer API and mobile export dependencies.
|
||||
@@ -102,6 +103,19 @@ The former five-second schedule, AIRI alarm storage, boot receivers, and pending
|
||||
Kirie reports live clicks through `notificationActivated`. It does not replay clicks after a cold start.
|
||||
Spotlight uses that event to open Chat for notifications sent by its current renderer.
|
||||
|
||||
## QR scanning
|
||||
|
||||
The connection settings page uses `@gd-kirie/qr` through the renderer's existing Eventa context.
|
||||
The main Godot scene registers `GdKirie.Qr` and attaches its host to the scene's `KirieNode`.
|
||||
Kirie core 0.15.0 forwards WebView visibility so the native camera preview can appear.
|
||||
|
||||
A successful scan supplies text to AIRI's server-channel parser and connection probe.
|
||||
Cancellation returns without changing the connection or showing an error toast.
|
||||
While Kirie hides the WebView for capture, Android Back reaches Godot to cancel the scan.
|
||||
Permission denial and native errors use the existing error toast.
|
||||
Kirie owns camera permission, capture, preview, and native export dependencies.
|
||||
The former AIRI barcode Eventa contract and embedded Ionic scanner are removed.
|
||||
|
||||
## Checks and build
|
||||
|
||||
From this application directory, run:
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
<Compile Remove="android/**/*.cs" />
|
||||
<Compile Remove="node_modules/**/*.cs" />
|
||||
<Compile Remove="tests/**/*.cs" />
|
||||
<PackageReference Include="GdKirie.EventaAdapter" Version="0.13.0" />
|
||||
<PackageReference Include="GdKirie.Notifications" Version="0.13.0" />
|
||||
<PackageReference Include="GdKirie.Platform" Version="0.13.0" />
|
||||
<PackageReference Include="GdKirie.EventaAdapter" Version="0.15.0" />
|
||||
<PackageReference Include="GdKirie.Notifications" Version="0.15.0" />
|
||||
<PackageReference Include="GdKirie.Platform" Version="0.15.0" />
|
||||
<PackageReference Include="GdKirie.Qr" Version="0.15.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -96,9 +96,6 @@ import androidx.webkit.JavaScriptReplyProxy;
|
||||
import androidx.webkit.WebMessageCompat;
|
||||
import androidx.webkit.WebViewCompat;
|
||||
|
||||
import com.outsystems.plugins.barcode.controller.OSBARCController;
|
||||
import com.outsystems.plugins.barcode.model.OSBARCScanParameters;
|
||||
import com.outsystems.plugins.barcode.model.OSBARCScannerHint;
|
||||
|
||||
import com.godot.game.R;
|
||||
|
||||
@@ -155,8 +152,6 @@ public final class AiriAndroidPlugin extends GodotPlugin {
|
||||
"eventa:invoke:airi:android:permission:request-send";
|
||||
private static final String OPEN_PERMISSION_SETTINGS_EVENT =
|
||||
"eventa:invoke:airi:android:permission:open-settings-send";
|
||||
private static final String SCAN_BARCODE_EVENT =
|
||||
"eventa:invoke:airi:android:barcode:scan-send";
|
||||
private static final String OPEN_AUTHORIZATION_EVENT =
|
||||
"eventa:invoke:airi:android:authentication:open-send";
|
||||
private static final String CONSUME_PENDING_URL_OPEN_EVENT =
|
||||
@@ -173,7 +168,6 @@ public final class AiriAndroidPlugin extends GodotPlugin {
|
||||
private static final int NOTIFICATION_PERMISSION_REQUEST = 4101;
|
||||
private static final int MICROPHONE_PERMISSION_REQUEST = 4102;
|
||||
private static final int FILE_CHOOSER_REQUEST = 4103;
|
||||
private static final int BARCODE_SCAN_REQUEST = 112;
|
||||
private static final int NORMAL_WEB_SOCKET_CLOSE_CODE = 1000;
|
||||
private static final SignalInfo PERMISSION_RESULT =
|
||||
new SignalInfo("permission_result", String.class, Boolean.class);
|
||||
@@ -193,10 +187,8 @@ public final class AiriAndroidPlugin extends GodotPlugin {
|
||||
private int browserInsetsPaddingRight;
|
||||
private int browserInsetsPaddingBottom;
|
||||
private HostWebSocketBridge hostWebSocketBridge;
|
||||
private final OSBARCController barcodeController = new OSBARCController();
|
||||
private final Map<String, PendingEventaRequest> pendingPermissionRequests =
|
||||
new HashMap<>();
|
||||
private PendingEventaRequest pendingBarcodeScan;
|
||||
// The picker and both capture modes share the last listener, including after completion.
|
||||
private ActivityResultListener activityListener;
|
||||
private JavaScriptReplyProxy eventaReplyProxy;
|
||||
@@ -247,6 +239,17 @@ public final class AiriAndroidPlugin extends GodotPlugin {
|
||||
backPressedCallback = new OnBackPressedCallback(true) {
|
||||
@Override
|
||||
public void handleOnBackPressed() {
|
||||
// The QR host hides the WebView while Godot owns the camera preview.
|
||||
// Forward Back to Godot so that the active scan can cancel.
|
||||
if (browserWebView != null && browserWebView.getVisibility() != View.VISIBLE) {
|
||||
setEnabled(false);
|
||||
try {
|
||||
((ComponentActivity) activity).getOnBackPressedDispatcher().onBackPressed();
|
||||
} finally {
|
||||
setEnabled(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
navigateBrowserBack();
|
||||
}
|
||||
};
|
||||
@@ -295,7 +298,6 @@ public final class AiriAndroidPlugin extends GodotPlugin {
|
||||
browserWebView = null;
|
||||
eventaReplyProxy = null;
|
||||
pendingPermissionRequests.clear();
|
||||
pendingBarcodeScan = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -305,41 +307,6 @@ public final class AiriAndroidPlugin extends GodotPlugin {
|
||||
return;
|
||||
}
|
||||
|
||||
if (requestCode != BARCODE_SCAN_REQUEST || pendingBarcodeScan == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PendingEventaRequest pending = pendingBarcodeScan;
|
||||
pendingBarcodeScan = null;
|
||||
barcodeController.handleActivityResult(
|
||||
requestCode,
|
||||
resultCode,
|
||||
data,
|
||||
scanResult -> {
|
||||
try {
|
||||
sendEventaResponse(
|
||||
pending,
|
||||
new JSONObject()
|
||||
.put("ScanResult", scanResult.getText())
|
||||
.put("format", scanResult.getFormat().ordinal())
|
||||
);
|
||||
} catch (JSONException error) {
|
||||
Log.e("AiriAndroid", "Cannot create barcode response", error);
|
||||
}
|
||||
return kotlin.Unit.INSTANCE;
|
||||
},
|
||||
scanError -> {
|
||||
try {
|
||||
sendEventaResponse(
|
||||
pending,
|
||||
new JSONObject().put("error", scanError.getDescription())
|
||||
);
|
||||
} catch (JSONException error) {
|
||||
Log.e("AiriAndroid", "Cannot create barcode error response", error);
|
||||
}
|
||||
return kotlin.Unit.INSTANCE;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1030,13 +997,6 @@ public final class AiriAndroidPlugin extends GodotPlugin {
|
||||
String invokeId = body.getString("invokeId");
|
||||
JSONObject content = body.getJSONObject("content");
|
||||
|
||||
if (SCAN_BARCODE_EVENT.equals(event)) {
|
||||
startBarcodeScanner(
|
||||
content.getString("scanInstructions"),
|
||||
new PendingEventaRequest(event, invokeId, replyProxy)
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (OPEN_AUTHORIZATION_EVENT.equals(event)) {
|
||||
openAuthorization(
|
||||
content.optString("url", ""),
|
||||
@@ -1176,31 +1136,6 @@ public final class AiriAndroidPlugin extends GodotPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
private void startBarcodeScanner(
|
||||
String scanInstructions,
|
||||
PendingEventaRequest pending
|
||||
) {
|
||||
Activity activity = getActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
pendingBarcodeScan = pending;
|
||||
OSBARCScanParameters parameters = new OSBARCScanParameters(
|
||||
scanInstructions,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
"",
|
||||
OSBARCScannerHint.QR_CODE,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
activity.runOnUiThread(() -> barcodeController.scanCode(activity, parameters));
|
||||
}
|
||||
|
||||
private void sendEventaResponse(PendingEventaRequest request, Object content) {
|
||||
try {
|
||||
String responseEvent = request.event.replace("-send", "-receive")
|
||||
@@ -1610,19 +1545,8 @@ func _supports_platform(platform: EditorExportPlatform) -> bool:
|
||||
func _get_android_dependencies(_platform: EditorExportPlatform, _debug: bool) -> PackedStringArray:
|
||||
return PackedStringArray([
|
||||
"androidx.core:core-splashscreen:1.2.0",
|
||||
"io.ionic.libs:ionbarcode-android:2.1.1@aar",
|
||||
"androidx.appcompat:appcompat:1.7.1",
|
||||
"androidx.activity:activity-ktx:1.10.1",
|
||||
"org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.2",
|
||||
"org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2",
|
||||
"com.google.zxing:core:3.5.3",
|
||||
"com.google.mlkit:barcode-scanning:17.3.0",
|
||||
"androidx.camera:camera-camera2:1.5.1",
|
||||
"androidx.camera:camera-lifecycle:1.5.1",
|
||||
"androidx.camera:camera-view:1.5.1",
|
||||
"androidx.activity:activity-compose:1.10.1",
|
||||
"androidx.compose.material3:material3:1.4.0",
|
||||
"androidx.compose.material3:material3-window-size-class:1.4.0",
|
||||
"com.squareup.okhttp3:okhttp:4.12.0",
|
||||
])
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ Stage-pocket remains the behavior reference. Its Web build is never packaged ins
|
||||
|
||||
The Kirie Android plugin owns the WebView and its binary IPC transport.
|
||||
The AIRI Android plugin finds that WebView and adds Android host behavior.
|
||||
It configures system bars, permissions, file selection, external navigation, notifications, keyboard insets, and lifecycle callbacks.
|
||||
It configures system bars, permissions, file selection, external navigation, keyboard insets, and lifecycle callbacks.
|
||||
Kirie notification and QR plugins own notification delivery and camera scanning.
|
||||
|
||||
The Godot scene creates the WebView after it registers AIRI and Kirie Eventa handlers.
|
||||
The packaged renderer therefore uses the same host-context API on every supported platform.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { join, resolve } from 'node:path'
|
||||
|
||||
import notifications from '@gd-kirie/notifications'
|
||||
import qr from '@gd-kirie/qr'
|
||||
import VueI18n from '@intlify/unplugin-vue-i18n/vite'
|
||||
import templateCompilerOptions from '@tresjs/core/template-compiler-options'
|
||||
import Vue from '@vitejs/plugin-vue'
|
||||
@@ -23,7 +24,7 @@ const sharedCacheDir = resolve(join(import.meta.dirname, '..', '..', '.cache'))
|
||||
const rendererRoot = resolve(join(import.meta.dirname, 'src-web', 'src', 'renderer'))
|
||||
|
||||
export default defineKirieConfig({
|
||||
plugins: [core, notifications],
|
||||
plugins: [core, notifications, qr],
|
||||
godot: { csproj: 'StageTamagotchiKirie.csproj' },
|
||||
web: {
|
||||
vite: {
|
||||
|
||||
@@ -32,10 +32,11 @@
|
||||
"@fontsource/kiwi-maru": "catalog:",
|
||||
"@fontsource/m-plus-rounded-1c": "catalog:",
|
||||
"@formkit/auto-animate": "catalog:",
|
||||
"@gd-kirie/ipc": "0.13.0",
|
||||
"@gd-kirie/ipc-eventa": "0.13.0",
|
||||
"@gd-kirie/notifications": "0.13.0",
|
||||
"@gd-kirie/platform": "0.13.0",
|
||||
"@gd-kirie/ipc": "0.15.0",
|
||||
"@gd-kirie/ipc-eventa": "0.15.0",
|
||||
"@gd-kirie/notifications": "0.15.0",
|
||||
"@gd-kirie/platform": "0.15.0",
|
||||
"@gd-kirie/qr": "0.15.0",
|
||||
"@guiiai/logg": "catalog:",
|
||||
"@huggingface/transformers": "catalog:",
|
||||
"@intlify/core": "catalog:",
|
||||
@@ -172,7 +173,7 @@
|
||||
"csstype": "catalog:",
|
||||
"drizzle-orm": "catalog:",
|
||||
"electron": "catalog:",
|
||||
"kirie": "0.13.0",
|
||||
"kirie": "0.15.0",
|
||||
"less": "catalog:",
|
||||
"std-env": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
|
||||
@@ -34,7 +34,7 @@ project/assembly_name="StageTamagotchiKirie"
|
||||
|
||||
[editor_plugins]
|
||||
|
||||
enabled=PackedStringArray("res://addons/airi-android/plugin.cfg", "res://addons/kirie/plugin.cfg", "res://addons/kirie_notifications/plugin.cfg")
|
||||
enabled=PackedStringArray("res://addons/airi-android/plugin.cfg", "res://addons/kirie/plugin.cfg", "res://addons/kirie_notifications/plugin.cfg", "res://addons/kirie_qr/plugin.cfg")
|
||||
|
||||
[godot_cef]
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using Eventa;
|
||||
using GdKirie.EventaAdapter;
|
||||
using GdKirie.Notifications;
|
||||
using GdKirie.Platform;
|
||||
using GdKirie.Qr;
|
||||
using Godot;
|
||||
|
||||
public partial class Main : Node
|
||||
@@ -13,6 +14,7 @@ public partial class Main : Node
|
||||
private KirieEventaContextHandle? _eventa;
|
||||
private GdKiriePlatformHost? _platform;
|
||||
private GdKirieNotificationsHost? _notifications;
|
||||
private GdKirieQrHost? _qr;
|
||||
private OnboardingWindowManager? _onboarding;
|
||||
private SettingsWindowManager? _settings;
|
||||
private ChatWindowManager? _chat;
|
||||
@@ -47,11 +49,15 @@ public partial class Main : Node
|
||||
return;
|
||||
}
|
||||
|
||||
var registry = AiriDesktopContracts.Register(
|
||||
GdKirieNotifications.Register(GdKiriePlatform.Register(new KirieEventaJsonRegistry())));
|
||||
var registry = new KirieEventaJsonRegistry();
|
||||
GdKiriePlatform.Register(registry);
|
||||
GdKirieNotifications.Register(registry);
|
||||
GdKirieQr.Register(registry);
|
||||
AiriDesktopContracts.Register(registry);
|
||||
_eventa = _kirie.CreateEventaContext(registry);
|
||||
_platform = GdKiriePlatform.Attach(_eventa.Context, window);
|
||||
_notifications = GdKirieNotifications.Attach(_eventa.Context, window);
|
||||
_qr = GdKirieQr.Attach(_eventa.Context, this, GetNode<Control>("KirieNode"));
|
||||
_displaySnapshotRegistration = CurrentDisplaySnapshotService.Attach(
|
||||
_eventa.Context,
|
||||
window);
|
||||
@@ -164,6 +170,7 @@ public partial class Main : Node
|
||||
_microphonePermissions?.Dispose();
|
||||
_microphonePermissionRegistration?.Dispose();
|
||||
_androidPermissions?.Dispose();
|
||||
_qr?.Dispose();
|
||||
_notifications?.Dispose();
|
||||
_platform?.Dispose();
|
||||
_eventa?.Dispose();
|
||||
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
import type { AiriAndroidBarcodeScanResult } from '../../shared/eventa'
|
||||
|
||||
import { airiAndroidBarcodeEventIds } from '../../shared/eventa'
|
||||
import { invokeAndroidEventa } from './android-permissions'
|
||||
|
||||
export async function scanAndroidBarcode(scanInstructions: string) {
|
||||
const result = await invokeAndroidEventa<AiriAndroidBarcodeScanResult>(
|
||||
airiAndroidBarcodeEventIds.scan,
|
||||
{ scanInstructions },
|
||||
)
|
||||
|
||||
if (result.error)
|
||||
throw new Error(result.error)
|
||||
if (!result.ScanResult)
|
||||
throw new Error('The barcode scanner returned no result')
|
||||
|
||||
return result.ScanResult
|
||||
}
|
||||
@@ -1,22 +1,25 @@
|
||||
import type { KirieEventaContext, KirieEventaContextHandle } from '@gd-kirie/ipc-eventa'
|
||||
import type { NotificationsClient } from '@gd-kirie/notifications'
|
||||
import type { PlatformClient } from '@gd-kirie/platform'
|
||||
import type { QrClient } from '@gd-kirie/qr'
|
||||
import type { InvokeEventa } from '@moeru/eventa'
|
||||
import type { ShallowRef } from 'vue'
|
||||
|
||||
import { createContext as createKirieContext } from '@gd-kirie/ipc-eventa'
|
||||
import { createNotificationsClient } from '@gd-kirie/notifications'
|
||||
import { createPlatformClient } from '@gd-kirie/platform'
|
||||
import { createQrClient } from '@gd-kirie/qr'
|
||||
import { defineInvoke } from '@moeru/eventa'
|
||||
import { shallowRef } from 'vue'
|
||||
|
||||
import '@gd-kirie/ipc'
|
||||
|
||||
/** Owns the renderer's Eventa transport, Platform client, and notification client. */
|
||||
/** Owns this renderer's Eventa transport and Kirie clients. */
|
||||
export interface HostContextOwner {
|
||||
context: KirieEventaContext
|
||||
notifications: NotificationsClient
|
||||
platform: PlatformClient
|
||||
qr: QrClient
|
||||
}
|
||||
|
||||
// Adapters borrow one context per renderer. The renderer entry disposes its
|
||||
@@ -29,6 +32,7 @@ function createHostContextOwner(): HostContextOwner & KirieEventaContextHandle {
|
||||
...eventa,
|
||||
notifications: createNotificationsClient(eventa.context),
|
||||
platform: createPlatformClient(eventa.context),
|
||||
qr: createQrClient(eventa.context),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -8,9 +8,10 @@ import { shallowRef } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { toast } from 'vue-sonner'
|
||||
|
||||
import { scanAndroidBarcode } from '../../../host-context/android-barcode-scanner'
|
||||
import { initializeHostContext } from '../../../host-context/owner'
|
||||
|
||||
const { t } = useI18n()
|
||||
const { qr } = initializeHostContext()
|
||||
const serverChannelStore = useModsServerChannelStore()
|
||||
const scanning = shallowRef(false)
|
||||
const errorMessage = shallowRef('')
|
||||
@@ -20,7 +21,9 @@ async function scanServerChannelQrCode() {
|
||||
errorMessage.value = ''
|
||||
|
||||
try {
|
||||
const scanResult = await scanAndroidBarcode(t('settings.pages.connection.qr-scan.instructions'))
|
||||
const scanResult = await qr.scan()
|
||||
if (scanResult === null)
|
||||
return
|
||||
const payload = parseServerChannelQrPayload(scanResult)
|
||||
const url = await probeServerChannelQrPayload(payload)
|
||||
|
||||
|
||||
@@ -95,20 +95,6 @@ export const airiAndroidPermissionEventIds = {
|
||||
request: 'eventa:invoke:airi:android:permission:request',
|
||||
} as const
|
||||
|
||||
export interface AiriAndroidBarcodeScanPayload {
|
||||
scanInstructions: string
|
||||
}
|
||||
|
||||
export interface AiriAndroidBarcodeScanResult {
|
||||
ScanResult?: string
|
||||
error?: string
|
||||
format?: number
|
||||
}
|
||||
|
||||
export const airiAndroidBarcodeEventIds = {
|
||||
scan: 'eventa:invoke:airi:android:barcode:scan',
|
||||
} as const
|
||||
|
||||
export const airiAndroidAuthenticationEventIds = {
|
||||
consumeUrlOpen: 'eventa:invoke:airi:android:app:url-open:consume',
|
||||
open: 'eventa:invoke:airi:android:authentication:open',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Android host JVM fixtures
|
||||
|
||||
These fixtures compile unchanged methods extracted from the production Java source in `addons/airi-android/export-plugin.gd`.
|
||||
Only Android activity, URI, native reply transport, and WebSocket callback boundaries use fixture objects.
|
||||
Only Android activity, view visibility, Back dispatch, URI, native reply transport, and WebSocket callback boundaries use fixture objects.
|
||||
The JSON implementation is Pocket's existing `org.json:json:20240303` test dependency.
|
||||
No Android device, Gradle watcher, Vite build, or Godot build is required.
|
||||
|
||||
@@ -15,7 +15,7 @@ Authentication expectations come from Pocket's `WebAuthenticationPlugin.kt`.
|
||||
Missing handlers reject with `BROWSER_UNAVAILABLE`. Other `RuntimeException` failures use the same rejection.
|
||||
A healthy browser still opens once and resolves once.
|
||||
The browser-channel Vitest fixture also exercises the actual Eventa rejection path and restores an Error with Pocket's code and message.
|
||||
Existing string errors from notification scheduling retain their envelope shape.
|
||||
Other bridge errors retain their existing response envelope.
|
||||
|
||||
The generated Java files exist only in a temporary directory.
|
||||
The `.gdignore` marker excludes these fixture sources from Godot assets.
|
||||
@@ -30,6 +30,17 @@ A null Throwable message gives the error event its fallback text and omits the c
|
||||
Empty messages remain empty. An available HTTP response supplies the close code.
|
||||
All cases retain error-before-close ordering and remove the opened session.
|
||||
|
||||
## QR preview Back dispatch
|
||||
|
||||
```sh
|
||||
python3 run-fixtures.py back --json-jar "$JSON_JAR"
|
||||
```
|
||||
|
||||
The fixture executes the production Back callback with external view and dispatcher boundaries.
|
||||
A visible WebView retains browser navigation. A hidden WebView yields Back to Godot's native preview.
|
||||
The callback becomes enabled again after native dispatch, including when the native callback throws.
|
||||
This fixture checks dispatch ownership. It does not establish device camera cancellation.
|
||||
|
||||
## Native dialog theme
|
||||
|
||||
From the repository root, run the fixture with Godot 4.7.2:
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
final class BackFixture {
|
||||
private static final class View {
|
||||
static final int VISIBLE = 0;
|
||||
static final int INVISIBLE = 4;
|
||||
private final int visibility;
|
||||
View(int visibility) { this.visibility = visibility; }
|
||||
int getVisibility() { return visibility; }
|
||||
}
|
||||
private final class Dispatcher {
|
||||
int nativeBackRequests;
|
||||
boolean fail;
|
||||
void onBackPressed() {
|
||||
if (enabled) throw new AssertionError("The AIRI callback must yield before dispatch");
|
||||
nativeBackRequests++;
|
||||
if (fail) throw new IllegalStateException("Native callback failed");
|
||||
}
|
||||
}
|
||||
private final class ComponentActivity {
|
||||
final Dispatcher dispatcher = new Dispatcher();
|
||||
Dispatcher getOnBackPressedDispatcher() { return dispatcher; }
|
||||
}
|
||||
private final ComponentActivity activity = new ComponentActivity();
|
||||
private View browserWebView;
|
||||
private boolean enabled = true;
|
||||
private int browserBackRequests;
|
||||
private void setEnabled(boolean value) { enabled = value; }
|
||||
private void navigateBrowserBack() { browserBackRequests++; }
|
||||
|
||||
/* PRODUCTION_METHODS */
|
||||
|
||||
public static void main(String[] args) {
|
||||
BackFixture fixture = new BackFixture();
|
||||
fixture.browserWebView = new View(View.VISIBLE);
|
||||
fixture.handleOnBackPressed();
|
||||
require(fixture.browserBackRequests == 1, "Visible WebView must own Back");
|
||||
require(fixture.activity.dispatcher.nativeBackRequests == 0, "Visible WebView must retain native Back");
|
||||
fixture.browserWebView = new View(View.INVISIBLE);
|
||||
fixture.handleOnBackPressed();
|
||||
require(fixture.activity.dispatcher.nativeBackRequests == 1, "Hidden WebView must yield Back to the native preview");
|
||||
require(fixture.browserBackRequests == 1, "Native preview must not navigate the renderer");
|
||||
require(fixture.enabled, "The AIRI callback must be enabled after native dispatch");
|
||||
fixture.activity.dispatcher.fail = true;
|
||||
try {
|
||||
fixture.handleOnBackPressed();
|
||||
throw new AssertionError("Native errors must propagate");
|
||||
} catch (IllegalStateException expected) {
|
||||
require(fixture.enabled, "The AIRI callback must recover after native failure");
|
||||
}
|
||||
fixture.browserWebView = new View(View.VISIBLE);
|
||||
fixture.handleOnBackPressed();
|
||||
require(fixture.browserBackRequests == 2, "Renderer Back must resume after the preview");
|
||||
System.out.println("back: PASS preview ownership and callback recovery");
|
||||
}
|
||||
private static void require(boolean value, String message) {
|
||||
if (!value) throw new AssertionError(message);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ def method(source, signature):
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('case', choices=['authentication', 'websocket'])
|
||||
parser.add_argument('case', choices=['authentication', 'websocket', 'back'])
|
||||
parser.add_argument('--json-jar', type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
app = Path(__file__).resolve().parents[2]
|
||||
@@ -31,6 +31,9 @@ if args.case == 'authentication':
|
||||
'private void sendEventaResponse(',
|
||||
'private void sendEventaError(',
|
||||
]
|
||||
elif args.case == 'back':
|
||||
fixture_name = 'BackFixture'
|
||||
signatures = ['public void handleOnBackPressed()']
|
||||
else:
|
||||
fixture_name = 'WebSocketFixture'
|
||||
signatures = [
|
||||
|
||||
@@ -39,19 +39,9 @@ dependencies {
|
||||
implementation 'androidx.webkit:webkit:1.16.0'
|
||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib:2.1.21'
|
||||
implementation 'androidx.fragment:fragment:1.8.6'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2'
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
|
||||
implementation 'io.ionic.libs:ionbarcode-android:2.1.1@aar'
|
||||
implementation 'androidx.appcompat:appcompat:1.7.1'
|
||||
implementation 'androidx.activity:activity-ktx:1.10.1'
|
||||
implementation 'com.google.zxing:core:3.5.3'
|
||||
implementation 'com.google.mlkit:barcode-scanning:17.3.0'
|
||||
implementation 'androidx.camera:camera-camera2:1.5.1'
|
||||
implementation 'androidx.camera:camera-lifecycle:1.5.1'
|
||||
implementation 'androidx.camera:camera-view:1.5.1'
|
||||
implementation 'androidx.activity:activity-compose:1.10.1'
|
||||
implementation 'androidx.compose.material3:material3:1.4.0'
|
||||
implementation 'androidx.compose.material3:material3-window-size-class:1.4.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.7.0'
|
||||
}
|
||||
|
||||
Generated
+534
-69
File diff suppressed because it is too large
Load Diff
+8
-7
@@ -29,16 +29,17 @@ minimumReleaseAgeExclude:
|
||||
- electron-builder-squirrel-windows@26.16.1
|
||||
- electron-builder@26.16.1
|
||||
# NOTICE:
|
||||
# Kirie 0.13.0 supplies the plugin API required by notifications.
|
||||
# Kirie 0.15.0 supplies native WebView visibility required by QR capture.
|
||||
# pnpm blocks releases within the configured minimum age.
|
||||
# Source: apps/stage-tamagotchi-kirie/kirie.config.ts.
|
||||
# Remove these exceptions when this release exceeds the minimum age.
|
||||
- '@gd-kirie/build@0.13.0'
|
||||
- '@gd-kirie/ipc-eventa@0.13.0'
|
||||
- '@gd-kirie/ipc@0.13.0'
|
||||
- '@gd-kirie/notifications@0.13.0'
|
||||
- '@gd-kirie/platform@0.13.0'
|
||||
- kirie@0.13.0
|
||||
- '@gd-kirie/build@0.15.0'
|
||||
- '@gd-kirie/ipc-eventa@0.15.0'
|
||||
- '@gd-kirie/ipc@0.15.0'
|
||||
- '@gd-kirie/notifications@0.15.0'
|
||||
- '@gd-kirie/platform@0.15.0'
|
||||
- '@gd-kirie/qr@0.15.0'
|
||||
- kirie@0.15.0
|
||||
- '@moeru/*'
|
||||
- '@proj-airi/*'
|
||||
# NOTICE:
|
||||
|
||||
Reference in New Issue
Block a user