feat(android): use native webview instead of gioui framework
arcad/arcast/pipeline/head This commit looks good Details

This commit is contained in:
wpetit 2024-04-29 11:51:45 +02:00
parent 49c23e66de
commit 7c75b478a3
18 changed files with 330 additions and 342 deletions

View File

@ -1,5 +1,4 @@
{
"java.project.sourcePaths": [
"apps/main/src"
]
"java.project.sourcePaths": ["apps/main/src"],
"java.configuration.updateBuildConfiguration": "automatic"
}

View File

@ -1,3 +1,5 @@
export PATH := $(PATH):$(PWD)/tools/gomobile/bin:$(PWD)/tools/gobind/bin
LINT_ARGS ?= --timeout 5m
GORELEASER_VERSION ?= v1.25.1
GORELEASER_ARGS ?= release --snapshot --clean
@ -50,9 +52,13 @@ build-client: deps ## Build executable
-o ./bin/client \
./cmd/client
build-android: .mktools tools/gogio/bin/gogio deps ## Build executable
.PHONY: android/app/libs/arcast.aar
android/app/libs/arcast.aar: tools/gomobile/bin/gomobile tools/gobind/bin/gobind
mkdir -p android/app/libs
gomobile bind -v -javapkg com.cadoles.arcast -target android -o android/app/libs/arcast.aar ./pkg/android
build-android: .mktools android/app/libs/arcast.aar deps ## Build executable
mkdir -p dist
CGO_ENABLED=1 GOOS=android CGO_CFLAGS="-I${JDK_PATH}/include -I${JDK_PATH}/include/linux -w" tools/gogio/bin/gogio -target android -buildmode archive -o android/app/libs/mobile.aar -x ./cmd/mobile
( cd android && VERSION_CODE=$(VERSION_CODE) VERSION_NAME=$(MKT_PROJECT_VERSION)-debug ./gradlew assembleDebug )
$(ANDROID_KEYSTORE_FILE):
@ -64,9 +70,8 @@ $(ANDROID_KEYSTORE_FILE):
-validity $(ANDROID_KEYSTORE_KEY_VALIDITY) \
-storepass "$(ANDROID_KEYSTORE_PASS)"
release-android: $(ANDROID_KEYSTORE_FILE) tools/gogio/bin/gogio deps ## Build executable
release-android: deps .mktools android/app/libs/arcast.aar $(ANDROID_KEYSTORE_FILE) ## Build executable
mkdir -p dist
CGO_ENABLED=1 GOOS=android CGO_CFLAGS="-I${JDK_PATH}/include -I${JDK_PATH}/include/linux -w" tools/gogio/bin/gogio -target android -buildmode archive -o android/app/libs/mobile.aar -x ./cmd/mobile
( cd android && VERSION_CODE=$(VERSION_CODE) VERSION_NAME=$(MKT_PROJECT_VERSION) ./gradlew assemble )
rm -f android/app/build/outputs/apk/release/app-release-unsigned-aligned.apk
"$(ANDROID_HOME)/build-tools/$(ANDROID_BUILD_TOOLS_VERSION)/zipalign" -p 4 android/app/build/outputs/apk/release/app-release-unsigned.apk android/app/build/outputs/apk/release/app-release-unsigned-aligned.apk
@ -103,10 +108,6 @@ tools/modd/bin/modd:
mkdir -p tools/modd/bin
GOBIN=$(PWD)/tools/modd/bin go install github.com/cortesi/modd/cmd/modd@latest
tools/gogio/bin/gogio:
mkdir -p tools/gogio/bin
GOBIN=$(PWD)/tools/gogio/bin go install gioui.org/cmd/gogio@latest
release: gitea-release
gitea-release: .mktools build-apps goreleaser release-android
@ -134,6 +135,14 @@ gitea-release: .mktools build-apps goreleaser release-android
goreleaser: .env .mktools
( set -o allexport && source .env && set +o allexport && curl -sfL https://goreleaser.com/static/run | VERSION=$(GORELEASER_VERSION) GORELEASER_CURRENT_TAG="$(MKT_PROJECT_VERSION)" GOPATH=$(shell go env GOPATH) bash /dev/stdin $(GORELEASER_ARGS) )
tools/gomobile/bin/gomobile:
mkdir -p tools/gomobile/bin
GOBIN=$(PWD)/tools/gomobile/bin go install golang.org/x/mobile/cmd/gomobile@latest
tools/gobind/bin/gobind:
mkdir -p tools/gobind/bin
GOBIN=$(PWD)/tools/gobind/bin go install golang.org/x/mobile/cmd/gobind@latest
.PHONY: mktools
mktools:
rm -rf .mktools

View File

@ -63,7 +63,7 @@ dependencies {
implementation 'androidx.compose.ui:ui-graphics'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.material3:material3'
implementation files('libs/sys_android.jar')
implementation("androidx.webkit:webkit:1.10.0")
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'

View File

@ -1 +1,2 @@
/mobile.aar
/arcast*

Binary file not shown.

View File

@ -8,8 +8,6 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
@ -35,12 +33,6 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="org.gioui.GioActivity"
android:theme="@style/Theme.GioApp"
android:configChanges="orientation|keyboardHidden"
android:windowSoftInputMode="adjustResize"
android:keepScreenOn="true">
</activity>
</application>
</manifest>

View File

@ -0,0 +1,31 @@
package com.example.arcast_player
import android.webkit.WebView
import com.cadoles.arcast.android.Bridge
class ArcastBridge(private val webview: WebView) : Bridge {
private var title: String = ""
private var url: String = ""
override fun getTitle(): String {
return title
}
override fun getURL(): String {
return url
}
override fun loadURL(url: String) {
webview.post(Runnable {
webview.loadUrl(url)
})
}
fun setURL(url: String) {
this.url = url
}
fun setTitle(title: String) {
this.title = title
}
}

View File

@ -0,0 +1,18 @@
package com.example.arcast_player
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
class ArcastWebviewClient(private var bridge: ArcastBridge): WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
return false
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
bridge.title = view?.title.toString()
bridge.url = url.toString()
}
}

View File

@ -12,29 +12,26 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.cadoles.arcast_player.ui.theme.ArcastplayerTheme
import org.gioui.GioActivity
import android.view.WindowManager
import android.webkit.WebView
import com.cadoles.arcast.android.Android as Arcast
import com.example.arcast_player.ArcastBridge
import com.example.arcast_player.ArcastWebviewClient
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startActivity(Intent(this, GioActivity::class.java))
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
}
val wv = WebView(applicationContext)
val bridge: ArcastBridge = ArcastBridge(wv)
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
}
wv.webViewClient = ArcastWebviewClient(bridge)
Arcast.setBridge(bridge)
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
ArcastplayerTheme {
Greeting("Android")
val port = Arcast.startServer(filesDir.absolutePath)
wv.settings.javaScriptEnabled = true
wv.loadUrl("http://127.0.0.1:${port}")
setContentView(wv)
}
}

View File

@ -10,12 +10,13 @@
}
.video {
object-fit: cover;
width: 100%;
height: auto;
position: absolute;
top: 0;
left: 0;
bottom: 0;
object-fit: contain;
/* position: absolute; */
right: 0;
top: 0;
width: 100%;
flex-grow: 1;
width: 100%;
}

View File

@ -1,120 +0,0 @@
package main
import (
"context"
"crypto/tls"
"os"
"path/filepath"
"forge.cadoles.com/arcad/arcast"
"forge.cadoles.com/arcad/arcast/pkg/browser/gioui"
"forge.cadoles.com/arcad/arcast/pkg/config"
"forge.cadoles.com/arcad/arcast/pkg/server"
"gioui.org/app"
"gioui.org/io/system"
"gioui.org/layout"
"gioui.org/op"
"github.com/gioui-plugins/gio-plugins/plugin"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/logger"
)
const packageName = "com.cadoles.arcast_player"
func main() {
ctx := context.Background()
window := app.NewWindow(
app.Fullscreen.Option(),
app.AnyOrientation.Option(),
)
browser := gioui.NewBrowser(window)
go func() {
ops := new(op.Ops)
for {
evt := window.NextEvent()
plugin.Install(window, evt)
switch evt := evt.(type) {
case system.DestroyEvent:
os.Exit(0)
return
case system.FrameEvent:
gtx := layout.NewContext(ops, evt)
browser.Layout(gtx)
evt.Frame(ops)
}
}
}()
go func() {
for {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
conf := config.DefaultConfig()
configFiles := getConfigFiles(ctx)
for _, f := range configFiles {
logger.Info(ctx, "loading or creating configuration file", logger.F("filename", f))
if err := config.LoadOrCreate(ctx, f, conf, config.DefaultTransforms...); err != nil {
logger.Error(ctx, "could not load configuration file", logger.CapturedE(errors.WithStack(err)))
continue
}
break
}
cert, err := tls.X509KeyPair(conf.HTTPS.Cert, conf.HTTPS.Key)
if err != nil {
logger.Fatal(ctx, "could not parse x509 certificate", logger.CapturedE(errors.WithStack(err)))
}
server := server.New(
browser,
server.WithInstanceID(conf.InstanceID),
server.WithAppsEnabled(conf.Apps.Enabled),
server.WithDefaultApp(conf.Apps.DefaultApp),
server.WithApps(arcast.DefaultApps...),
server.WithTLSCertificate(&cert),
server.WithAddress(conf.HTTP.Address),
server.WithTLSAddress(conf.HTTPS.Address),
server.WithAllowedOrigins(conf.AllowedOrigins...),
)
if err := server.Start(); err != nil {
logger.Fatal(ctx, "could not start server", logger.CapturedE(errors.WithStack(err)))
}
defer func() {
if err := server.Stop(); err != nil {
logger.Error(ctx, "could not stop server", logger.CapturedE(errors.WithStack(err)))
}
}()
if err := server.Wait(); err != nil {
logger.Error(ctx, "could not wait for server", logger.CapturedE(errors.WithStack(err)))
}
}
}()
app.Main()
}
func getConfigFiles(ctx context.Context) []string {
configFiles := make([]string, 0)
sharedStorageConfigFile := filepath.Join("/storage/emulated/0/Android/data", packageName, "files/config.json")
configFiles = append(configFiles, sharedStorageConfigFile)
dataDir, err := app.DataDir()
if err != nil {
logger.Error(ctx, "could not retrieve app data dir", logger.CapturedE(errors.WithStack(err)))
} else {
appDataConfigFile := filepath.Join(dataDir, "config.json")
configFiles = append(configFiles, appDataConfigFile)
}
return configFiles
}

12
go.mod
View File

@ -3,9 +3,7 @@ module forge.cadoles.com/arcad/arcast
go 1.21.4
require (
gioui.org v0.4.1
github.com/dschmidt/go-layerfs v0.1.0
github.com/gioui-plugins/gio-plugins v0.0.0-20240323070753-3331d8c2df5d
github.com/go-chi/cors v1.2.1
github.com/gorilla/websocket v1.5.1
github.com/grandcat/zeroconf v1.0.1-0.20230119201135-e4f60f8407b1
@ -13,21 +11,17 @@ require (
github.com/pkg/errors v0.9.1
github.com/wlynxg/anet v0.0.1
github.com/zserge/lorca v0.1.10
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f
)
require (
cdr.dev/slog v1.6.1 // indirect
gioui.org/cpu v0.0.0-20220412190645-f1e9e8c3b1f7 // indirect
gioui.org/shader v1.0.8 // indirect
git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/charmbracelet/lipgloss v0.7.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372 // indirect
github.com/inkeliz/go_inkwasm v0.0.0-20220912074516-049d3472c98a // indirect
github.com/jedib0t/go-pretty/v6 v6.4.9 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
@ -42,12 +36,8 @@ require (
go.opentelemetry.io/otel v1.21.0 // indirect
go.opentelemetry.io/otel/trace v1.21.0 // indirect
golang.org/x/crypto v0.16.0 // indirect
golang.org/x/exp v0.0.0-20221012211006-4de253d81b95 // indirect
golang.org/x/exp/shiny v0.0.0-20220921164117-439092de6870 // indirect
golang.org/x/image v0.5.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/term v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.16.1 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
gopkg.in/go-playground/validator.v9 v9.29.1 // indirect

55
go.sum
View File

@ -8,19 +8,9 @@ cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5
cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M=
cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI=
cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc=
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d h1:ARo7NCVvN2NdhLlJE9xAbKweuI9L6UgfTbYb0YwPacY=
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA=
gioui.org v0.4.1 h1:sCTw5Fexg0xg9CxYmbrkKtHXcobf0JMbl5XpF2TC/zc=
gioui.org v0.4.1/go.mod h1:2atiYR4upH71/6ehnh6XsUELa7JZOrOHHNMDxGBZF0Q=
gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
gioui.org/cpu v0.0.0-20220412190645-f1e9e8c3b1f7 h1:tNJdnP5CgM39PRc+KWmBRRYX/zJ+rd5XaYxY5d5veqA=
gioui.org/cpu v0.0.0-20220412190645-f1e9e8c3b1f7/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
gioui.org/shader v1.0.8 h1:6ks0o/A+b0ne7RzEqRZK5f4Gboz2CfG+mVliciy6+qA=
gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0 h1:bGG/g4ypjrCJoSvFrP5hafr9PPB5aw8SjcOWWila7ZI=
git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0/go.mod h1:+axXBRUTIDlCeE73IKeD/os7LoEnTKdkp8/gQOFjqyo=
github.com/Bornholm/lorca v0.0.0-20240121134933-d5e83569cb4c h1:xG9xSpEqZYOvd1Bvx/36i3RBTA17d7XnuvoMIG+/ry8=
github.com/Bornholm/lorca v0.0.0-20240121134933-d5e83569cb4c/go.mod h1:TUOtEogaMwy0b+p9e4NxZC1jbPRUaNaEw5DnjQF//F8=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
@ -34,8 +24,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dschmidt/go-layerfs v0.1.0 h1:jE6aHDfjNzS/31DS48th6EkmELwTa1Uf+aO4jRkBs3U=
github.com/dschmidt/go-layerfs v0.1.0/go.mod h1:m62aff0hn23Q/tQBRiNSeLD7EUuimDvsuCvCpzBr3Gw=
github.com/gioui-plugins/gio-plugins v0.0.0-20240323070753-3331d8c2df5d h1:8b7owUJ8sNmgqEk+1d7ylr3TCH3vliCvY/6ycfize8o=
github.com/gioui-plugins/gio-plugins v0.0.0-20240323070753-3331d8c2df5d/go.mod h1:3XVleuCdPpdajFL+ASh2wmXZNskitXQQ4jhVss0VHZg=
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
@ -48,10 +36,6 @@ github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372 h1:FQivqchis6bE2/9uF70M2gmmLpe82esEm2QadL0TEJo=
github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372/go.mod h1:evDBbvNR/KaVFZ2ZlDSOWWXIUKq0wCOEtzLxRM8SG3k=
github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22 h1:LBQTFxP2MfsyEDqSKmUBZaDuDHN1vpqDyOZjcqS7MYI=
github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
@ -60,8 +44,6 @@ github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/grandcat/zeroconf v1.0.1-0.20230119201135-e4f60f8407b1 h1:cNb52t5fkWv8ZiicKWnc2eZnhsCCoH7WmRBMIbMp04Q=
github.com/grandcat/zeroconf v1.0.1-0.20230119201135-e4f60f8407b1/go.mod h1:I6CSXU4zCGL08JOk9NbcT0ofAgnIkS/fVXbYzfSoDic=
github.com/inkeliz/go_inkwasm v0.0.0-20220912074516-049d3472c98a h1:uZklbtdSPrDL/d1EUKd9s8a0Byla2TT01Wg/GZ4xj0w=
github.com/inkeliz/go_inkwasm v0.0.0-20220912074516-049d3472c98a/go.mod h1:LPI3Qojj7OgTyc2R4RPB6BuMSgjoOXCObwnDzz1SOVk=
github.com/jaevor/go-nanoid v1.3.0 h1:nD+iepesZS6pr3uOVf20vR9GdGgJW1HPaR46gtrxzkg=
github.com/jaevor/go-nanoid v1.3.0/go.mod h1:SI+jFaPuddYkqkVQoNGHs81navCtH388TcrH0RqFKgY=
github.com/jedib0t/go-pretty/v6 v6.4.9 h1:vZ6bjGg2eBSrJn365qlxGcaWu09Id+LHtrfDWlB2Usc=
@ -109,7 +91,6 @@ github.com/wlynxg/anet v0.0.1 h1:VbkEEgHxPSrRQSiyRd0pmrbcEQAEU2TTb8fb4DmSYoQ=
github.com/wlynxg/anet v0.0.1/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
gitlab.com/wpetit/goweb v0.0.0-20231215190137-4a8add1d3d07 h1:0V95X1cBpdj5zyOe6oGtn/BQHlRpV8WlL3eTs3jaxiA=
gitlab.com/wpetit/goweb v0.0.0-20231215190137-4a8add1d3d07/go.mod h1:Nfr7aZPiSN6biFumhiHbh9k8A3rKQRzR+o0bVtv78UY=
go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
@ -121,59 +102,59 @@ go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF
go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20221012211006-4de253d81b95 h1:sBdrWpxhGDdTAYNqbgBLAR+ULAPPhfgncLr1X0lyWtg=
golang.org/x/exp v0.0.0-20221012211006-4de253d81b95/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/exp/shiny v0.0.0-20220921164117-439092de6870 h1:GjCs9zNN8fojJskeK7QiiVecCaMk0dfGTyL6IUcmp0o=
golang.org/x/exp/shiny v0.0.0-20220921164117-439092de6870/go.mod h1:VjAR7z0ngyATZTELrBSkxOOHhhlnVUxDye4mcjx5h/8=
golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI=
golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f h1:kgfVkAEEQXXQ0qc6dH7n6y37NAYmTFmz0YRwrRjgxKw=
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e h1:xIXmWJ303kJCuogpj0bHq+dcjcZHU+XFyc1I0Yl9cRg=

86
pkg/android/binding.go Normal file
View File

@ -0,0 +1,86 @@
package android
import (
"context"
"crypto/tls"
"net"
"path/filepath"
"forge.cadoles.com/arcad/arcast"
"forge.cadoles.com/arcad/arcast/pkg/browser/proxy"
"forge.cadoles.com/arcad/arcast/pkg/config"
"forge.cadoles.com/arcad/arcast/pkg/server"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/logger"
)
var (
browser = proxy.NewBrowser()
)
func SetBridge(bridge Bridge) {
browser.SetLoadURL(bridge.LoadURL)
browser.SetGetTitle(bridge.GetTitle)
browser.SetGetURL(bridge.GetURL)
}
func StartServer(dataDir string) string {
ctx := context.Background()
conf := config.DefaultConfig()
filename := filepath.Join(dataDir, "config.json")
logger.Info(ctx, "loading or creating configuration file", logger.F("filename", filename))
if err := config.LoadOrCreate(ctx, filename, conf, config.DefaultTransforms...); err != nil {
logger.Error(ctx, "could not load configuration file", logger.CapturedE(errors.WithStack(err)))
}
go func() {
for {
runServer(ctx, conf)
}
}()
_, port, err := net.SplitHostPort(conf.HTTP.Address)
if err != nil {
panic(errors.Wrap(err, "could not parse server listening address"))
}
return port
}
func runServer(ctx context.Context, conf *config.Config) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
cert, err := tls.X509KeyPair(conf.HTTPS.Cert, conf.HTTPS.Key)
if err != nil {
logger.Fatal(ctx, "could not parse x509 certificate", logger.CapturedE(errors.WithStack(err)))
}
server := server.New(
browser,
server.WithInstanceID(conf.InstanceID),
server.WithAppsEnabled(conf.Apps.Enabled),
server.WithDefaultApp(conf.Apps.DefaultApp),
server.WithApps(arcast.DefaultApps...),
server.WithTLSCertificate(&cert),
server.WithAddress(conf.HTTP.Address),
server.WithTLSAddress(conf.HTTPS.Address),
server.WithAllowedOrigins(conf.AllowedOrigins...),
)
if err := server.Start(); err != nil {
logger.Fatal(ctx, "could not start server", logger.CapturedE(errors.WithStack(err)))
}
defer func() {
if err := server.Stop(); err != nil {
logger.Error(ctx, "could not stop server", logger.CapturedE(errors.WithStack(err)))
}
}()
if err := server.Wait(); err != nil {
logger.Error(ctx, "could not wait for server", logger.CapturedE(errors.WithStack(err)))
}
}

11
pkg/android/bridge.go Normal file
View File

@ -0,0 +1,11 @@
package android
import (
_ "golang.org/x/mobile/bind"
)
type Bridge interface {
LoadURL(url string)
GetTitle() string
GetURL() string
}

View File

@ -1,118 +0,0 @@
package gioui
import (
"context"
"sync/atomic"
"forge.cadoles.com/arcad/arcast/pkg/browser"
"gioui.org/app"
"gioui.org/f32"
"gioui.org/layout"
"github.com/gioui-plugins/gio-plugins/webviewer"
"gitlab.com/wpetit/goweb/logger"
)
type Browser struct {
window *app.Window
tag int
url *atomic.Value
changed *atomic.Bool
status *atomic.Value
title *atomic.Value
}
func (b *Browser) Layout(gtx layout.Context) layout.Dimensions {
events := gtx.Events(&b.tag)
for _, evt := range events {
switch ev := evt.(type) {
case webviewer.TitleEvent:
b.title.Store(ev.Title)
case webviewer.NavigationEvent:
b.url.Store(ev.URL)
}
}
ctx := context.Background()
logger.Debug(ctx, "drawing")
webviewer.WebViewOp{Tag: &b.tag}.Push(gtx.Ops)
webviewer.OffsetOp{
Point: f32.Point{
X: 0,
Y: 0,
},
}.Add(gtx.Ops)
webviewer.RectOp{
Size: f32.Point{
X: float32(gtx.Constraints.Max.X),
Y: float32(gtx.Constraints.Max.Y),
},
}.Add(gtx.Ops)
if b.changed.CompareAndSwap(true, false) {
url := b.url.Load().(string)
logger.Debug(ctx, "url changed", logger.F("url", url))
webviewer.NavigateOp{URL: url}.Add(gtx.Ops)
}
return layout.Dimensions{Size: gtx.Constraints.Max}
}
// Load implements browser.Browser.
func (b *Browser) Load(url string) error {
b.url.Store(url)
b.changed.Store(true)
b.status.Store(browser.StatusCasting)
b.window.Invalidate()
return nil
}
// Status implements browser.Browser.
func (b *Browser) Status() (browser.Status, error) {
return b.status.Load().(browser.Status), nil
}
// Title implements browser.Browser.
func (b *Browser) Title() (string, error) {
return b.title.Load().(string), nil
}
// URL implements browser.Browser.
func (b *Browser) URL() (string, error) {
return b.url.Load().(string), nil
}
// Reset implements browser.Browser.
func (b *Browser) Reset(url string) error {
b.url.Store(url)
b.changed.Store(true)
b.status.Store(browser.StatusIdle)
b.window.Invalidate()
return nil
}
func NewBrowser(window *app.Window) *Browser {
b := &Browser{
window: window,
url: &atomic.Value{},
changed: &atomic.Bool{},
status: &atomic.Value{},
title: &atomic.Value{},
}
b.url.Store("")
b.title.Store("")
b.changed.Store(false)
b.status.Store(browser.StatusIdle)
return b
}
var _ browser.Browser = &Browser{}

View File

@ -0,0 +1,110 @@
package proxy
import (
"sync"
"forge.cadoles.com/arcad/arcast/pkg/browser"
"github.com/pkg/errors"
)
var (
ErrNotInitialized = errors.New("not initialized")
)
type Browser struct {
loadURL func(url string)
getURL func() string
getTitle func() string
status browser.Status
mutex sync.RWMutex
}
// Load implements browser.Browser.
func (b *Browser) Load(url string) error {
b.mutex.Lock()
defer b.mutex.Unlock()
if b.loadURL == nil {
return errors.WithStack(ErrNotInitialized)
}
b.loadURL(url)
b.status = browser.StatusCasting
return nil
}
// Reset implements browser.Browser.
func (b *Browser) Reset(url string) error {
b.mutex.Lock()
defer b.mutex.Unlock()
if b.loadURL == nil {
return errors.WithStack(ErrNotInitialized)
}
b.loadURL(url)
b.status = browser.StatusIdle
return nil
}
// Status implements browser.Browser.
func (b *Browser) Status() (browser.Status, error) {
b.mutex.RLock()
defer b.mutex.RUnlock()
return b.status, nil
}
// Title implements browser.Browser.
func (b *Browser) Title() (string, error) {
b.mutex.RLock()
defer b.mutex.RUnlock()
if b.getTitle == nil {
return "", errors.WithStack(ErrNotInitialized)
}
return b.getTitle(), nil
}
// URL implements browser.Browser.
func (b *Browser) URL() (string, error) {
b.mutex.RLock()
defer b.mutex.RUnlock()
if b.getURL == nil {
return "", errors.WithStack(ErrNotInitialized)
}
return b.getURL(), nil
}
func (b *Browser) SetLoadURL(fn func(url string)) {
b.mutex.Lock()
defer b.mutex.Unlock()
b.loadURL = fn
}
func (b *Browser) SetGetTitle(fn func() string) {
b.mutex.Lock()
defer b.mutex.Unlock()
b.getTitle = fn
}
func (b *Browser) SetGetURL(fn func() string) {
b.mutex.Lock()
defer b.mutex.Unlock()
b.getURL = fn
}
func NewBrowser() *Browser {
return &Browser{}
}
var _ browser.Browser = &Browser{}