refactor(stage-tamagotchi-kirie): use Kirie QR scanner

This commit is contained in:
LemonNeko
2026-10-21 17:56:59 +08:00
parent 68deca3481
commit 7bf01839d1
20 changed files with 689 additions and 235 deletions
+3
View File
@@ -17,3 +17,6 @@ obj/
*.aar
*.xcframework
addons/kirie_notifications/
addons/kirie_qr/
ios/plugins/kirie_qr/
ios/plugins/kirie_camera/
+3 -3
View File
@@ -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`.
+22 -8
View File
@@ -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.
+2 -1
View File
@@ -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: {
+6 -5
View File
@@ -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:",
+1 -1
View File
@@ -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();
@@ -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),
}
}
@@ -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'
}
+534 -69
View File
File diff suppressed because it is too large Load Diff
+8 -7
View File
@@ -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: