wails/v3/examples/android/frontend/main.js
Lea Anthony 4d8ec29feb feat: Add Android support for Wails v3
This commit adds comprehensive Android support for Wails v3, enabling
Go applications to run as native Android apps with WebView-based UI.

Key features:
- Android-specific application implementation with JNI bridge
- WebView integration via WebViewAssetLoader for serving assets
- JavaScript runtime injection and execution via JNI callbacks
- Binding call support with async result callbacks
- Event system support for Android platform
- Full example Android app with Gradle build system

Technical details:
- Uses CGO with Android NDK for cross-compilation
- Implements JNI callbacks for Go <-> Java communication
- Supports both ARM64 and x86_64 architectures
- WebView debugging support via Chrome DevTools Protocol
- Handles empty response body case in binding calls to prevent panic

Files added:
- v3/pkg/application/*_android.go - Android platform implementations
- v3/pkg/events/events_android.go - Android event definitions
- v3/internal/*/\*_android.go - Android-specific internal packages
- v3/examples/android/ - Complete example Android application
- v3/ANDROID_ARCHITECTURE.md - Architecture documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 21:06:59 +11:00

113 lines
3.4 KiB
JavaScript

import {GreetService} from "./bindings/changeme";
import * as Runtime from "@wailsio/runtime";
const resultElement = document.getElementById('result');
const timeElement = document.getElementById('time');
const deviceInfoElement = document.getElementById('deviceInfo');
const Events = Runtime.Events;
const IOS = Runtime.IOS; // May be undefined in published package; we guard usages below.
window.doGreet = () => {
let name = document.getElementById('name').value;
if (!name) {
name = 'anonymous';
}
GreetService.Greet(name).then((result) => {
resultElement.innerText = result;
}).catch((err) => {
console.log(err);
});
}
window.doHaptic = (style) => {
if (!IOS || !IOS.Haptics?.Impact) {
console.warn('IOS runtime not available in @wailsio/runtime. Skipping haptic call.');
return;
}
IOS.Haptics.Impact(style).catch((err) => {
console.error('Haptics error:', err);
});
}
window.getDeviceInfo = async () => {
if (!IOS || !IOS.Device?.Info) {
deviceInfoElement.innerText = 'iOS runtime not available; cannot fetch device info.';
return;
}
try {
const info = await IOS.Device.Info();
deviceInfoElement.innerText = JSON.stringify(info, null, 2);
} catch (e) {
deviceInfoElement.innerText = `Error: ${e?.message || e}`;
}
}
// Generic caller for IOS.<Group>.<Method>(args)
window.iosJsSet = async (methodPath, args) => {
if (!IOS) {
console.warn('IOS runtime not available in @wailsio/runtime.');
return;
}
try {
const [group, method] = methodPath.split('.');
const target = IOS?.[group];
const fn = target?.[method];
if (typeof fn !== 'function') {
console.warn('IOS method not found:', methodPath);
return;
}
await fn(args);
} catch (e) {
console.error('iosJsSet error for', methodPath, e);
}
}
// Emit events for Go handlers
window.emitGo = (eventName, data) => {
try {
Events.Emit(eventName, data);
} catch (e) {
console.error('emitGo error:', e);
}
}
// Toggle helpers for UI switches
window.setGoToggle = (eventName, enabled) => {
emitGo(eventName, { enabled: !!enabled });
}
window.setJsToggle = (methodPath, enabled) => {
iosJsSet(methodPath, { enabled: !!enabled });
}
Events.On('time', (payload) => {
// payload may be a plain value or an object with a `data` field depending on emitter/runtime
const value = (payload && typeof payload === 'object' && 'data' in payload) ? payload.data : payload;
console.log('[frontend] time event:', payload, '->', value);
timeElement.innerText = value;
});
// Simple pane switcher responding to native UITabBar
function showPaneByIndex(index) {
const panes = [
document.getElementById('screen-bindings'),
document.getElementById('screen-go'),
document.getElementById('screen-js'),
];
panes.forEach((el, i) => {
if (!el) return;
if (i === index) el.classList.add('active');
else el.classList.remove('active');
});
}
// Listen for native tab selection events posted by the iOS layer
window.addEventListener('nativeTabSelected', (e) => {
const idx = (e && e.detail && typeof e.detail.index === 'number') ? e.detail.index : 0;
showPaneByIndex(idx);
});
// Ensure default pane is visible on load (index 0)
window.addEventListener('DOMContentLoaded', () => {
showPaneByIndex(0);
});