WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content
Draft
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
217 changes: 217 additions & 0 deletions drivers/bluetooth/bluetoothAdapter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
//go:build android

package bluetooth

/*
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static inline void copyToError(char* errorMsgv, char** error){
*error = (char*)malloc((strlen(errorMsg) + 1) * sizeof(char));
strcpy(*error, errorMsg);
}

jobject getBluetoothAdapter(uintptr_t env, char** errorMsg) {
JNIEnv* envPtr = (JNIEnv*)env;

// Get the BluetoothAdapter class
jclass bluetoothAdapterClass = (*envPtr)->FindClass(envPtr, "android/bluetooth/BluetoothAdapter");
if (bluetoothAdapterClass == NULL) {
copyToError("Failed to find BluetoothAdapter class", errorMsg);
return NULL;
}

// Get the getDefaultAdapter static method
jmethodID getDefaultAdapterMethod = (*envPtr)->GetStaticMethodID(envPtr, bluetoothAdapterClass, "getDefaultAdapter", "()Landroid/bluetooth/BluetoothAdapter;");
if (getDefaultAdapterMethod == NULL) {
(*envPtr)->DeleteLocalRef(envPtr, bluetoothAdapterClass);
copyToError("Failed to find getDefaultAdapter method", errorMsg);
return NULL;
}

// Get the BluetoothAdapter object
jobject bluetoothAdapter = (*envPtr)->CallStaticObjectMethod(envPtr, bluetoothAdapterClass, getDefaultAdapterMethod);
if (bluetoothAdapter == NULL) {
(*envPtr)->DeleteLocalRef(envPtr, bluetoothAdapterClass);
copyToError("Failed to invoke getDefaultAdapter method", errorMsg);
return NULL;
}

// Release memory
(*envPtr)->DeleteLocalRef(envPtr, bluetoothAdapterClass);
return bluetoothAdapter;
}

char* getBluetoothName(uintptr_t env, jobject bluetoothAdapter, char** errorMsg) {
JNIEnv* envPtr = (JNIEnv*)env;

// Get BluetoothAdapter class
jclass bluetoothAdapterClass = (*envPtr)->GetObjectClass(envPtr, bluetoothAdapter);
if (bluetoothAdapterClass == NULL) {
copyToError("Failed to find BluetoothAdapter class", errorMsg);
return NULL;
}

// Get the getName method
jmethodID getNameMethod = (*envPtr)->GetMethodID(envPtr, bluetoothAdapterClass, "getName", "()Ljava/lang/String;");
// If the method is not available, return NULL
if (getNameMethod == NULL) {
(*envPtr)->DeleteLocalRef(envPtr, bluetoothAdapterClass);
copyToError("Failed to get getName method", errorMsg);
return NULL;
}

// Call the getName method
jstring bluetoothName = (jstring)(*envPtr)->CallObjectMethod(envPtr, bluetoothAdapter, getNameMethod);
if (bluetoothName == NULL) {
(*envPtr)->DeleteLocalRef(envPtr, bluetoothAdapterClass);
copyToError("Bluetooth name is NULL", errorMsg);
return NULL;
}

// Convert jstring to char*
const char* nameChars = (*envPtr)->GetStringUTFChars(envPtr, bluetoothName, NULL);
if (bluetoothName == NULL) {
(*envPtr)->DeleteLocalRef(envPtr, bluetoothAdapterClass);
copyToError("Failed convert jstring to char*", errorMsg);
return NULL;
}
size_t length = strlen(nameChars);
char* heapString = (char*)malloc((length + 1) * sizeof(char));
strcpy(heapString, nameChars);

// Release memory
(*envPtr)->ReleaseStringUTFChars(envPtr, bluetoothName, nameChars);
(*envPtr)->DeleteLocalRef(envPtr, bluetoothAdapterClass);
(*envPtr)->DeleteLocalRef(envPtr, bluetoothName);

return heapString;
}

void freeBluetoothAdapter(uintptr_t env, jobject bluetoothAdapter, char** errorMsg) {
if (bluetoothAdapter != NULL) {
JNIEnv* envPtr = (JNIEnv*)env;
// Get BluetoothAdapter class
jclass bluetoothAdapterClass = (*envPtr)->GetObjectClass(envPtr, bluetoothAdapter);

// Get the close method
jmethodID closeMethod = (*envPtr)->GetMethodID(envPtr, bluetoothAdapterClass, "close", "()V");

// If the method is not available, set error and return
if (closeMethod == NULL) {
copyToError( "Failed to get close method", errorMsg);
return;
}

// Call the close method
(*envPtr)->CallVoidMethod(envPtr, bluetoothAdapter, closeMethod);

// Delete local references
(*envPtr)->DeleteLocalRef(envPtr, bluetoothAdapterClass);
(*envPtr)->DeleteLocalRef(envPtr, bluetoothAdapter);
}
}
*/
import "C"
import (
"errors"
"fmt"
"unsafe"
)

// Adapter has references android.bluetooth.BluetoothAdapter in java
type Adapter struct {
self C.jobject
name string
stop chan struct{}
}

// StopServer send signal to server to end
func (b *Adapter) StopServer() {
go func() {
b.stop <- struct{}{}
}()
}

// NewBluetoothDefaultAdapter get Bluetooth adapter, WARNING: error can means only path of fail put defer before error handling
func NewBluetoothDefaultAdapter() (b *Adapter, e error) {
if runOnJVM == nil {
return nil, errors.New("you must on android call SetVMFunc before")
}
err := runOnJVM(func(vm, env, ctx uintptr) error {
var errMsgC *C.char
adapter := C.getBluetoothAdapter(C.uintptr_t(env), &errMsgC)
if errMsgC != nil {
err := errors.New(C.GoString(errMsgC))
C.free(unsafe.Pointer(errMsgC))
e = err
return nil
}
b = &Adapter{self: adapter}
nameC := C.getBluetoothName(C.uintptr_t(env), adapter, &errMsgC)
if errMsgC != nil {
e = errors.New(C.GoString(errMsgC))
C.free(unsafe.Pointer(errMsgC))
return nil
}
b.name = C.GoString(nameC)
C.free(unsafe.Pointer(nameC))
return nil
})
e = errors.Join(e, err)
return
}

// GetName returns name of device
func (b *Adapter) GetName() string {
return b.name
}

// Close clean bluetooth adapter from memory
func (b *Adapter) Close() {
err := runOnJVM(func(vm, env, ctx uintptr) error {
var errMsgC *C.char
C.freeBluetoothAdapter(C.uintptr_t(env), b.self, &errMsgC)
if errMsgC != nil {
err := errors.New(C.GoString(errMsgC))
C.free(unsafe.Pointer(errMsgC))
return err
}
return nil
})
if err != nil {
return
}
}

// Server creates server and listen to clients
func (b *Adapter) Server(fn Handle) error {
socket, err := b.GetBluetoothServerSocket()
defer fmt.Println(socket.Close())
if err != nil {
return err
}
for {
select {
case <-b.stop:
return nil
default:
}
con, err0 := socket.Accept()
if err0 != nil {
fmt.Println(con.Close())
return err0
}
go func() {
defer fmt.Println(con.Close())
readWriter, er := con.GetReadWriter()
defer fmt.Println(readWriter.Close())
if er != nil {
return
}
fn(readWriter, con)
}()
}
}
7 changes: 7 additions & 0 deletions drivers/bluetooth/example/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package main

import "fyne.io/fyne/v2"

func runClientReturnError(w fyne.Window) {

}
38 changes: 38 additions & 0 deletions drivers/bluetooth/example/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package main

import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"time"
)

func main() {
a := app.New()
w := a.NewWindow("Hello")
buildMainMenu(w)
w.ShowAndRun()
}

func buildMainMenu(w fyne.Window) {
status := widget.NewLabel("status!")
b1 := widget.NewButton("server!", nil)
b2 := widget.NewButton("client!", nil)
con := container.NewVBox(status, b1, b2)
b1.OnTapped = func() {
status.SetText("start server")
status.Refresh()
time.Sleep(time.Second * 5)
con.Hide()
runServerReturnError(w)
}
b2.OnTapped = func() {
status.SetText("start client")
status.Refresh()
time.Sleep(time.Second * 5)
con.Hide()
runClientReturnError(w)
}
w.SetContent(con)
}
75 changes: 75 additions & 0 deletions drivers/bluetooth/example/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package main

import (
"bluetoothFyne/bluetooth" // testing project
"encoding/json"
"fmt"
"fyne.io/fyne/v2"
)

type msg struct {
x, y, z uint64
}

// example of server side
func runServerReturnError(window fyne.Window) {
adapter, err := bluetooth.NewBluetoothDefaultAdapter()
defer adapter.Close() // error can means only path of fail
if err != nil {
return
}
serverAutomatic(adapter) // autonatic many
serverManual(adapter) // manual one
buildMainMenu(window)
}

func comunicate(readWriter *bluetooth.ReadWriterBluetooth, socketInfo *bluetooth.BluetoothSocket) {
m := msg{
x: 10,
y: 10000,
z: 5,
}
for i := uint64(0); i < 100000000000000; i++ {
m.x = (m.x + i + m.y + m.z) % 300
m.y = (m.y + i + m.x + m.z) % 500
m.z = (m.z + i + m.x + m.y) % 700
bytes, er := json.Marshal(m) // defined size of msg
if er == nil {
_, err := readWriter.Write(bytes)
if err != nil {
fmt.Println(err)
}
continue
}
fmt.Println(er)
}

}

func serverAutomatic(adapter *bluetooth.BluetoothAdapter) {
err := adapter.Server(comunicate)
if err != nil {
fmt.Println(err)
}
}

func serverManual(adapter *bluetooth.BluetoothAdapter) {
socket, er := adapter.GetBluetoothServerSocket()
defer fmt.Println(socket.Close())
if er != nil {
fmt.Println(er)
return
}
con, er := socket.Accept()
defer fmt.Println(con.Close())
if er != nil {
fmt.Println(er)
return
}
readWriter, er := con.GetReadWriter()
defer fmt.Println(readWriter.Close())
if er != nil {
return
}
comunicate(readWriter, con)
}
19 changes: 19 additions & 0 deletions drivers/bluetooth/functionJVM.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//go:build android

package bluetooth

import (
"errors"
"fyne.io/fyne/v2/driver"
)

var runOnJVM = originForm

func originForm(fn func(vm, env, ctx uintptr) error) error {
return driver.RunNative(func(context interface{}) error {
if androidContext, ok := context.(driver.AndroidContext); ok {
return fn(androidContext.VM, androidContext.Env, androidContext.Ctx)
}
return errors.New("no context android")
})
}
3 changes: 3 additions & 0 deletions drivers/bluetooth/handle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package bluetooth

type Handle func(readWriter *ReadWriter, socketInfo *Socket)
Loading