mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
Fixes five regressions in Android support: 1. Restore events.Android struct (IDs 1259-1270) and JS event mappings that were removed from events.go, breaking events_common_android.go 2. Pass flags to runtime.Core() in nativeOnPageFinished to match the updated API signature used by all other platforms 3. Call setupCommonEvents() in the CGO build's run() method (the non-CGO build already did this) 4. Replace the stub handleMessageForAndroid() with real MessageProcessor routing so JS-to-Go runtime calls (bound methods, clipboard, etc.) actually work 5. Handle non-JSON string messages (e.g. "wails:runtime:ready") that the JS bridge sends before attempting JSON parse Fixes #5020 Co-authored-by: Varun Chawla <varun_6april@hotmail.com>
274 lines
6.6 KiB
Go
274 lines
6.6 KiB
Go
//go:build android && !cgo && !server
|
|
|
|
package application
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log/slog"
|
|
"sync"
|
|
"unsafe"
|
|
)
|
|
|
|
var (
|
|
// Global reference to the app for JNI callbacks
|
|
globalApp *App
|
|
globalAppLock sync.RWMutex
|
|
|
|
// JNI environment and class references
|
|
javaVM unsafe.Pointer
|
|
bridgeObject unsafe.Pointer
|
|
)
|
|
|
|
func init() {
|
|
androidLogf("info", "🤖 [application_android.go] init() called")
|
|
}
|
|
|
|
func androidLogf(level string, format string, a ...interface{}) {
|
|
msg := fmt.Sprintf(format, a...)
|
|
// For now, just use println - we'll connect to Android's Log.* later
|
|
println(fmt.Sprintf("[Android/%s] %s", level, msg))
|
|
}
|
|
|
|
func (a *App) platformRun() {
|
|
androidLogf("info", "🤖 [application_android.go] platformRun() called")
|
|
|
|
// Store global reference for JNI callbacks
|
|
globalAppLock.Lock()
|
|
globalApp = a
|
|
globalAppLock.Unlock()
|
|
|
|
// Create MessageProcessor so message callbacks can route runtime calls
|
|
messageProc := NewMessageProcessor(slog.Default())
|
|
globalMessageProcLock.Lock()
|
|
globalMessageProc = messageProc
|
|
globalMessageProcLock.Unlock()
|
|
|
|
androidLogf("info", "🤖 [application_android.go] Waiting for Android lifecycle...")
|
|
|
|
// Block forever - Android manages the app lifecycle via JNI callbacks
|
|
select {}
|
|
}
|
|
|
|
func (a *App) platformQuit() {
|
|
androidLogf("info", "🤖 [application_android.go] platformQuit() called")
|
|
// Android will handle app termination
|
|
}
|
|
|
|
func (a *App) isDarkMode() bool {
|
|
// TODO: Query Android for dark mode status
|
|
return false
|
|
}
|
|
|
|
func (a *App) isWindows() bool {
|
|
return false
|
|
}
|
|
|
|
// Platform-specific app implementation for Android
|
|
type androidApp struct {
|
|
parent *App
|
|
}
|
|
|
|
func newPlatformApp(app *App) *androidApp {
|
|
androidLogf("info", "🤖 [application_android.go] newPlatformApp() called")
|
|
return &androidApp{
|
|
parent: app,
|
|
}
|
|
}
|
|
|
|
func (a *androidApp) run() error {
|
|
androidLogf("info", "🤖 [application_android.go] androidApp.run() called")
|
|
|
|
// Wire common events
|
|
a.setupCommonEvents()
|
|
|
|
// Emit application started event
|
|
a.parent.Event.Emit("ApplicationStarted")
|
|
|
|
a.parent.platformRun()
|
|
return nil
|
|
}
|
|
|
|
func (a *androidApp) destroy() {
|
|
androidLogf("info", "🤖 [application_android.go] androidApp.destroy() called")
|
|
}
|
|
|
|
func (a *androidApp) setIcon(_ []byte) {
|
|
// Android app icon is set through AndroidManifest.xml
|
|
}
|
|
|
|
func (a *androidApp) name() string {
|
|
return a.parent.options.Name
|
|
}
|
|
|
|
func (a *androidApp) GetFlags(options Options) map[string]any {
|
|
return nil
|
|
}
|
|
|
|
func (a *androidApp) getAccentColor() string {
|
|
return ""
|
|
}
|
|
|
|
func (a *androidApp) getCurrentWindowID() uint {
|
|
return 0
|
|
}
|
|
|
|
func (a *androidApp) hide() {
|
|
// Android manages app visibility
|
|
}
|
|
|
|
func (a *androidApp) isDarkMode() bool {
|
|
return a.parent.isDarkMode()
|
|
}
|
|
|
|
func (a *androidApp) on(_ uint) {
|
|
// Android event handling
|
|
}
|
|
|
|
func (a *androidApp) setApplicationMenu(_ *Menu) {
|
|
// Android doesn't have application menus in the same way
|
|
}
|
|
|
|
func (a *androidApp) show() {
|
|
// Android manages app visibility
|
|
}
|
|
|
|
func (a *androidApp) showAboutDialog(_ string, _ string, _ []byte) {
|
|
// TODO: Implement Android about dialog
|
|
}
|
|
|
|
func (a *androidApp) getPrimaryScreen() (*Screen, error) {
|
|
screens, err := getScreens()
|
|
if err != nil || len(screens) == 0 {
|
|
return nil, err
|
|
}
|
|
return screens[0], nil
|
|
}
|
|
|
|
func (a *androidApp) getScreens() ([]*Screen, error) {
|
|
return getScreens()
|
|
}
|
|
|
|
func (a *App) logPlatformInfo() {
|
|
// Log Android platform info
|
|
androidLogf("info", "Platform: Android")
|
|
}
|
|
|
|
func (a *App) platformEnvironment() map[string]any {
|
|
return map[string]any{
|
|
"platform": "android",
|
|
}
|
|
}
|
|
|
|
func fatalHandler(errFunc func(error)) {
|
|
// Android fatal handler
|
|
}
|
|
|
|
// Helper functions
|
|
|
|
func serveAssetForAndroid(app *App, path string) ([]byte, error) {
|
|
// Normalize path
|
|
if path == "" || path == "/" {
|
|
path = "/index.html"
|
|
}
|
|
|
|
// TODO: Use the actual asset server to serve the file
|
|
// For now, return a placeholder
|
|
return nil, fmt.Errorf("asset serving not yet implemented: %s", path)
|
|
}
|
|
|
|
// Global MessageProcessor for message callbacks
|
|
var (
|
|
globalMessageProc *MessageProcessor
|
|
globalMessageProcLock sync.RWMutex
|
|
)
|
|
|
|
func handleMessageForAndroid(app *App, message string) string {
|
|
// Some messages are plain strings (e.g. "wails:runtime:ready"), not JSON.
|
|
if len(message) == 0 || message[0] != '{' {
|
|
androidLogf("debug", "🤖 [handleMessageForAndroid] Non-JSON message: %s", message)
|
|
return `{"success":true}`
|
|
}
|
|
|
|
var req RuntimeRequest
|
|
if err := json.Unmarshal([]byte(message), &req); err != nil {
|
|
androidLogf("error", "🤖 [handleMessageForAndroid] Failed to parse: %v", err)
|
|
return fmt.Sprintf(`{"error":"%s"}`, err.Error())
|
|
}
|
|
|
|
if req.WebviewWindowID == 0 && req.WebviewWindowName == "" {
|
|
windows := app.Window.GetAll()
|
|
if len(windows) > 0 {
|
|
req.WebviewWindowID = uint32(windows[0].ID())
|
|
}
|
|
}
|
|
|
|
globalMessageProcLock.RLock()
|
|
messageProc := globalMessageProc
|
|
globalMessageProcLock.RUnlock()
|
|
|
|
if messageProc == nil {
|
|
androidLogf("error", "🤖 [handleMessageForAndroid] MessageProcessor not initialized")
|
|
return `{"error":"MessageProcessor not initialized"}`
|
|
}
|
|
|
|
ctx := context.Background()
|
|
result, err := messageProc.HandleRuntimeCallWithIDs(ctx, &req)
|
|
if err != nil {
|
|
androidLogf("error", "🤖 [handleMessageForAndroid] Error: %v", err)
|
|
return fmt.Sprintf(`{"error":"%s"}`, err.Error())
|
|
}
|
|
|
|
if result == nil {
|
|
return `{"success":true}`
|
|
}
|
|
|
|
resp, err := json.Marshal(result)
|
|
if err != nil {
|
|
androidLogf("error", "🤖 [handleMessageForAndroid] Marshal error: %v", err)
|
|
return fmt.Sprintf(`{"error":"%s"}`, err.Error())
|
|
}
|
|
return string(resp)
|
|
}
|
|
|
|
func getMimeTypeForPath(path string) string {
|
|
// Simple MIME type detection based on extension
|
|
switch {
|
|
case endsWith(path, ".html"), endsWith(path, ".htm"):
|
|
return "text/html"
|
|
case endsWith(path, ".js"), endsWith(path, ".mjs"):
|
|
return "application/javascript"
|
|
case endsWith(path, ".css"):
|
|
return "text/css"
|
|
case endsWith(path, ".json"):
|
|
return "application/json"
|
|
case endsWith(path, ".png"):
|
|
return "image/png"
|
|
case endsWith(path, ".jpg"), endsWith(path, ".jpeg"):
|
|
return "image/jpeg"
|
|
case endsWith(path, ".gif"):
|
|
return "image/gif"
|
|
case endsWith(path, ".svg"):
|
|
return "image/svg+xml"
|
|
case endsWith(path, ".ico"):
|
|
return "image/x-icon"
|
|
case endsWith(path, ".woff"):
|
|
return "font/woff"
|
|
case endsWith(path, ".woff2"):
|
|
return "font/woff2"
|
|
case endsWith(path, ".ttf"):
|
|
return "font/ttf"
|
|
default:
|
|
return "application/octet-stream"
|
|
}
|
|
}
|
|
|
|
func endsWith(s, suffix string) bool {
|
|
return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
|
|
}
|
|
|
|
// executeJavaScript is a stub for non-cgo builds
|
|
func executeJavaScript(js string) {
|
|
androidLogf("warn", "executeJavaScript called but cgo is not enabled")
|
|
}
|