881 lines
17 KiB
Go

package Bluetooth
import (
"fmt"
"log"
"os"
"strings"
//"errors"
"github.com/fatih/structs"
gotime "time"
"github.com/veandco/go-sdl2/ttf"
"github.com/cuu/gogame/color"
"github.com/cuu/gogame/draw"
"github.com/cuu/gogame/event"
"github.com/cuu/gogame/font"
"github.com/cuu/gogame/rect"
"github.com/cuu/gogame/surface"
"github.com/cuu/gogame/time"
//"github.com/godbus/dbus"
bleapi "github.com/muka/go-bluetooth/api"
//"github.com/muka/go-bluetooth/bluez"
// "github.com/muka/go-bluetooth/bluez/profile"
"github.com/muka/go-bluetooth/bluez/profile/adapter"
"github.com/muka/go-bluetooth/bluez/profile/device"
"github.com/clockworkpi/LauncherGoDev/sysgo/UI"
logrus "github.com/sirupsen/logrus"
)
func showDeviceInfo(dev *device.Device1) {
if dev == nil {
return
}
props, err := dev.GetProperties()
if err != nil {
fmt.Printf("%s: Failed to get properties: %s\n", dev.Path, err.Error())
return
}
fmt.Printf("name=%s addr=%s rssi=%d\n", props.Name, props.Address, props.RSSI)
}
type BleForgetConfirmPage struct {
UI.ConfirmPage
}
func NewBleForgetConfirmPage() *BleForgetConfirmPage {
p := &BleForgetConfirmPage{}
p.ListFont = UI.Fonts["veramono20"]
p.FootMsg = [5]string{"Nav", "", "", "Cancel", "Yes"}
p.ConfirmText = "Confirm Forget?"
p.ConfirmPage.ConfirmText = p.ConfirmText
return p
}
func (self *BleForgetConfirmPage) KeyDown(ev *event.Event) {
if ev.Data["Key"] == UI.CurKeys["A"] || ev.Data["Key"] == UI.CurKeys["Menu"] {
self.ReturnToUpLevelPage()
self.Screen.Refresh()
}
if ev.Data["Key"] == UI.CurKeys["B"] {
self.SnapMsg("Deleting")
self.Screen.Refresh()
time.BlockDelay(400)
self.ReturnToUpLevelPage()
self.Screen.Refresh()
}
}
func (self *BleForgetConfirmPage) Draw() {
self.DrawBG()
for _, v := range self.MyList {
v.Draw()
}
}
type BleInfoPageSelector struct {
UI.InfoPageSelector
}
func NewBleInfoPageSelector() *BleInfoPageSelector {
p := &BleInfoPageSelector{}
p.Width = UI.Width
p.BackgroundColor = &color.Color{131, 199, 219, 255} //SkinManager().GiveColor('Front')
return p
}
func (self *BleInfoPageSelector) Draw() {
idx := self.Parent.GetPsIndex()
mylist := self.Parent.GetMyList()
if idx < len(mylist) {
_, y := mylist[idx].Coord()
_, h := mylist[idx].Size()
x := self.PosX + 2
self.PosY = y + 1
self.Height = h - 3
canvas_ := self.Parent.GetCanvasHWND()
rect_ := rect.Rect(x, self.PosY, self.Width-4, self.Height)
draw.AARoundRect(canvas_, &rect_, self.BackgroundColor, 4, 0, self.BackgroundColor)
}
}
type BleInfoPage struct {
UI.Page
ListFontObj *ttf.Font
ListSmFontObj *ttf.Font
ListSm2FontObj *ttf.Font
AList map[string]interface{}
Scroller *UI.ListScroller
ConfirmPage1 *BleForgetConfirmPage
MyDevice *device.Device1 // from NetItem-> from BluetoothPage
Props *device.Device1Properties
Path string
}
func NewBleInfoPage() *BleInfoPage {
p := &BleInfoPage{}
p.FootMsg = [5]string{"Nav", "Disconnect", "Forget", "Back", ""}
p.ListFontObj = UI.Fonts["varela15"]
p.ListSmFontObj = UI.Fonts["varela12"]
p.ListSm2FontObj = UI.Fonts["varela11"]
return p
}
func (self *BleInfoPage) Init() {
if self.Screen != nil {
if self.Screen.CanvasHWND != nil && self.CanvasHWND == nil {
self.CanvasHWND = self.Screen.CanvasHWND
}
}
self.PosX = self.Index * self.Screen.Width
self.Width = self.Screen.Width // equals to screen width
self.Height = self.Screen.Height
ps := NewBleInfoPageSelector()
ps.Parent = self
self.Ps = ps
self.PsIndex = 0
self.Scroller = UI.NewListScroller()
self.Scroller.Parent = self
self.Scroller.PosX = 2
self.Scroller.PosY = 2
self.Scroller.Init()
self.ConfirmPage1 = NewBleForgetConfirmPage()
self.ConfirmPage1.Screen = self.Screen
self.ConfirmPage1.Name = "Confirm Forget"
self.ConfirmPage1.Init()
}
func (self *BleInfoPage) GenList() {
self.AList = structs.Map(self.Props) //map[string]interface{}
self.MyList = nil
self.PsIndex = 0
start_x := 0
start_y := 0
i := 0
skip_arrays := []string{"ManufacturerData", "AdvertisingFlags", "ServiceData"}
for k, v := range self.AList {
skip2 := false
for _, u := range skip_arrays {
if strings.HasPrefix(k, u) {
skip2 = true
break
}
}
if skip2 {
continue
}
li := UI.NewInfoPageListItem()
li.Parent = self
li.PosX = start_x
li.PosY = start_y + i*NetItemDefaultHeight
li.Width = UI.Width
li.Fonts["normal"] = self.ListFontObj
if k == "UUIDs" {
li.Fonts["small"] = self.ListSm2FontObj
} else {
li.Fonts["small"] = self.ListSmFontObj
}
li.Init(k)
li.Flag = k
sm_text := ""
if k == "UUIDs" {
if len(v.([]string)) > 1 {
sm_text = v.([]string)[0]
} else {
sm_text = "<empty>"
}
} else {
sm_text = fmt.Sprintf("%v", v)
}
if len(sm_text) > 20 {
sm_text = sm_text[:20]
}
li.SetSmallText(sm_text)
li.PosX = 2
self.MyList = append(self.MyList, li)
i += 1
}
}
func (self *BleInfoPage) ScrollUp() {
if len(self.MyList) == 0 {
return
}
self.PsIndex -= 1
if self.PsIndex < 0 {
self.PsIndex = 0
}
cur_li := self.MyList[self.PsIndex]
x, y := cur_li.Coord()
if y < 0 {
for i, v := range self.MyList {
x, y = v.Coord()
_, h := v.Size()
self.MyList[i].NewCoord(x, y+h)
}
}
}
func (self *BleInfoPage) ScrollDown() {
if len(self.MyList) == 0 {
return
}
self.PsIndex += 1
if self.PsIndex >= len(self.MyList) {
self.PsIndex = len(self.MyList) - 1
}
cur_li := self.MyList[self.PsIndex]
x, y := cur_li.Coord()
_, h := cur_li.Size()
if y+h > self.Height {
for i, v := range self.MyList {
x, y = v.Coord()
_, h = v.Size()
self.MyList[i].NewCoord(x, y-h)
}
}
}
func (self *BleInfoPage) TryToForget() {
//muka Adapter1 RemoveDevice Path
adapter, err := bleapi.GetAdapter(adapterID)
if err == nil {
self.Screen.MsgBox.SetText("Forgeting")
self.Screen.MsgBox.Draw()
self.Screen.SwapAndShow()
err = adapter.RemoveDevice(self.MyDevice.Path())
if err != nil {
fmt.Println("BleInfoPage TryToForget: ", err)
}
time.BlockDelay(400)
self.ReturnToUpLevelPage()
self.Screen.Refresh()
} else {
fmt.Println("BleInfoPage TryToForget GetAdapter: ", err)
}
}
func (self *BleInfoPage) TryToDisconnect() {
is_connected, _ := self.MyDevice.GetConnected()
if is_connected {
self.Screen.FootBar.UpdateNavText("Disconnecting")
self.Screen.ShowMsg("Disconnecting",0)
self.MyDevice.Disconnect()
time.BlockDelay(350)
self.ReturnToUpLevelPage()
self.Screen.Refresh()
self.Screen.FootBar.ResetNavText()
}
}
func (self *BleInfoPage) Click() {
if self.PsIndex >= len(self.MyList) {
return
}
cur_li := self.MyList[self.PsIndex]
fmt.Println(cur_li.(*UI.InfoPageListItem).Flag)
}
func (self *BleInfoPage) OnLoadCb() {
if self.Props.Connected == true {
self.FootMsg[1] = "Disconnect"
} else {
self.FootMsg[1] = ""
}
self.GenList()
}
func (self *BleInfoPage) KeyDown(ev *event.Event) {
if ev.Data["Key"] == UI.CurKeys["A"] || ev.Data["Key"] == UI.CurKeys["Menu"] {
self.ReturnToUpLevelPage()
self.Screen.Refresh()
}
if ev.Data["Key"] == UI.CurKeys["Up"] {
self.ScrollUp()
self.Screen.Refresh()
}
if ev.Data["Key"] == UI.CurKeys["Down"] {
self.ScrollDown()
self.Screen.Refresh()
}
if ev.Data["Key"] == UI.CurKeys["Enter"] {
self.Click()
}
if ev.Data["Key"] == UI.CurKeys["X"] {
self.TryToDisconnect()
}
if ev.Data["Key"] == UI.CurKeys["Y"] {
self.TryToForget()
}
}
func (self *BleInfoPage) Draw() {
if len(self.MyList) == 0 {
return
}
self.ClearCanvas()
if len(self.MyList)*UI.DefaultInfoPageListItemHeight > self.Height {
self.Ps.(*BleInfoPageSelector).Width = self.Width - 10
self.Ps.(*BleInfoPageSelector).PosX = 9
self.Ps.Draw()
for _, v := range self.MyList {
v.Draw()
}
self.Scroller.UpdateSize(len(self.MyList)*UI.DefaultInfoPageListItemHeight,
self.PsIndex*UI.DefaultInfoPageListItemHeight)
self.Scroller.Draw()
} else {
self.Ps.(*BleInfoPageSelector).Width = self.Width
self.Ps.Draw()
for _, v := range self.MyList {
v.Draw()
}
}
}
type BleListMessageBox struct {
UI.Label
Parent UI.PageInterface
}
func NewBleListMessageBox() *BleListMessageBox {
p := &BleListMessageBox{}
p.Color = &color.Color{83, 83, 83, 255}
return p
}
func (self *BleListMessageBox) Draw() {
my_text := font.Render(self.FontObj, self.Text, true, self.Color, nil)
w := surface.GetWidth(my_text)
h := surface.GetHeight(my_text)
pw, ph := self.Parent.Size()
x := (pw - w) / 2
y := (ph - h) / 2
padding := 10
rect_ := rect.Rect(x-padding, y-padding, w+padding*2, h+padding*2)
draw.Rect(self.CanvasHWND, &color.Color{255, 255, 255, 255}, &rect_, 0)
draw.Rect(self.CanvasHWND, &color.Color{0, 0, 0, 255}, &rect_, 1)
rect2_ := rect.Rect(x, y, w, h)
surface.Blit(self.CanvasHWND, my_text, &rect2_, nil)
my_text.Free()
}
type BluetoothPage struct {
UI.Page
Devices []*device.Device1
BlePassword string
Connecting bool
Scanning bool
ListFontObj *ttf.Font
Scroller *UI.ListScroller
InfoPage *BleInfoPage
PrevState int
ShowingMessageBox bool
MsgBox *BleListMessageBox
ConnectTry int
//BlockCb ??
LastStatusMsg string
ADAPTER_DEV string // == adapterID
Offline bool
Leader *BluetoothPlugin
}
func NewBluetoothPage() *BluetoothPage {
p := &BluetoothPage{}
p.PageIconMargin = 20
p.SelectedIconTopOffset = 20
p.EasingDur = 10
p.Align = UI.ALIGN["SLeft"]
p.ADAPTER_DEV = adapterID
p.FootMsg = [5]string{"Nav", "Scan", "Info", "Back", "TryConnect"}
p.ListFontObj = UI.Fonts["notosanscjk15"]
return p
}
func (self *BluetoothPage) ShowBox(msg string) {
self.MsgBox.Text = msg
self.ShowingMessageBox = true
self.Screen.ShowMsg(msg,0)
}
func (self *BluetoothPage) HideBox() {
self.ShowingMessageBox = false
self.Screen.Refresh()
}
func (self *BluetoothPage) Init() {
self.PosX = self.Index * self.Screen.Width
self.Width = self.Screen.Width
self.Height = self.Screen.Height
self.CanvasHWND = self.Screen.CanvasHWND
ps := NewBleInfoPageSelector()
ps.Parent = self
ps.Width = UI.Width - 12
self.Ps = ps
self.PsIndex = 0
msgbox := NewBleListMessageBox()
msgbox.CanvasHWND = self.CanvasHWND
msgbox.Init(" ", UI.Fonts["veramono12"], nil)
msgbox.Parent = self
self.MsgBox = msgbox
self.Scroller = UI.NewListScroller()
self.Scroller.Parent = self
self.Scroller.PosX = 2
self.Scroller.PosY = 2
self.Scroller.Init()
self.InfoPage = NewBleInfoPage()
self.InfoPage.Screen = self.Screen
self.InfoPage.Name = "BluetoothInfo"
self.InfoPage.Init()
}
func (self *BluetoothPage) AbortedAndReturnToUpLevel() {
self.HideBox()
self.Screen.FootBar.ResetNavText()
self.ReturnToUpLevelPage()
self.Screen.Refresh()
}
func (self *BluetoothPage) TryConnect() {
if self.PsIndex >= len(self.MyList) {
return
}
cur_li := self.MyList[self.PsIndex]
if cur_li.(*NetItem).Props.Connected {
return
}
self.Screen.FootBar.UpdateNavText("Connecting")
self.ShowBox("Connecting")
self.Leader.PairPage.DevObj = cur_li.(*NetItem).Device
err := cur_li.(*NetItem).Device.Pair()
if err != nil {
fmt.Println(err)
err_msg := ""
s := err.Error()
err_msg = "Pair error"
if strings.Contains(s, "ConnectionAttemptFailed") {
err_msg = "Page Timeout"
}
if strings.Contains(s, "NoReply") {
err_msg = "NoReply,Cancelling"
dev1 := cur_li.(*NetItem).Device
dev1.CancelPairing()
}
if strings.Contains(s, "Exists") {
err_msg = "Already Exists"
adapter, err := bleapi.GetAdapter(adapterID)
if err == nil {
err = adapter.RemoveDevice(cur_li.(*NetItem).Device.Path())
if err != nil {
fmt.Println(err)
}
} else {
fmt.Println(err)
}
}
self.Leader.PairPage.PairErrorCb(err_msg)
self.Leader.PairPage.DevObj = nil
} else {
self.Leader.PairPage.PairOKCb()
dev1 := cur_li.(*NetItem).Device
err = dev1.SetTrusted(true)
if err != nil {
fmt.Println(err)
}
cur_li.(*NetItem).Device.Connect()
}
self.HideBox()
self.Screen.FootBar.ResetNavText()
}
//GetDevices returns a list of bluetooth discovered Devices
func (self *BluetoothPage) GetDevices() ([]*device.Device1, error) {
adapter, err := bleapi.GetAdapter(adapterID)
if err != nil {
return nil, err
}
list, err := adapter.GetDevices()
return list, err
}
func (self *BluetoothPage) RefreshDevices() {
// sync the cached devices
self.Devices = self.Devices[:0]
devices, err := self.GetDevices()
if err != nil {
panic(err)
os.Exit(1)
}
self.Devices = devices
}
func (self *BluetoothPage) GenNetworkList() {
self.MyList = nil
start_x := 0
start_y := 0
for i, v := range self.Devices { // v == bleapi.Device
props, err := v.GetProperties()
if err != nil {
log.Printf("%s: Failed to get properties: %s\n", v.Path, err.Error())
return
}
ni := NewNetItem()
ni.Parent = self
ni.PosX = start_x
ni.PosY = start_y + i*NetItemDefaultHeight
ni.Width = UI.Width
ni.FontObj = self.ListFontObj
ni.Props = props
ni.Parent = self
ni.Device = v
if props.Name != "" {
ni.Init(props.Name)
} else {
ni.Init(props.Address)
}
self.MyList = append(self.MyList, ni)
}
self.PsIndex = 0
}
func (self *BluetoothPage) Rescan() {
if self.Scanning == true {
self.ShowBox("Bluetooth scanning")
self.Screen.FootBar.UpdateNavText("Scanning")
}
a, err := adapter.GetAdapter(adapterID)
if err != nil {
fmt.Println(err)
return
}
discovery, cancel, err := bleapi.Discover(a, nil)
if err != nil {
fmt.Println(err)
}
defer cancel()
wait := make(chan error)
self.Scanning = true
self.ShowBox("Bluetooth scanning")
self.Screen.FootBar.UpdateNavText("Scanning")
go func() {
for dev := range discovery {
if dev == nil {
return
}
wait <- nil
}
}()
go func() {
sleep := 5
gotime.Sleep(gotime.Duration(sleep) * gotime.Second)
logrus.Debugf("Discovery timeout exceeded (%ds)", sleep)
wait <- nil
}()
err = <-wait
if err != nil {
fmt.Println(err)
}
self.Scanning = false
self.HideBox()
self.Screen.FootBar.ResetNavText()
}
func (self *BluetoothPage) OnLoadCb() {
self.Offline = false
if self.Screen.TitleBar.InAirPlaneMode == false {
out := UI.System("hcitool dev | grep hci0 |cut -f3")
if len(out) < 17 {
self.Offline = true
fmt.Println("Bluetooth OnLoadCb ,can not find hci0 alive,try to reboot")
} else {
self.Rescan()
self.RefreshDevices()
self.GenNetworkList()
}
} else {
self.Offline = true
}
}
func (self *BluetoothPage) ScrollUp() {
if len(self.MyList) == 0 {
return
}
self.PsIndex -= 1
if self.PsIndex < 0 {
self.PsIndex = 0
}
cur_ni := self.MyList[self.PsIndex] //*NetItem
if cur_ni.(*NetItem).PosY < 0 {
for i := 0; i < len(self.MyList); i++ {
self.MyList[i].(*NetItem).PosY += self.MyList[i].(*NetItem).Height
}
}
}
func (self *BluetoothPage) ScrollDown() {
if len(self.MyList) == 0 {
return
}
self.PsIndex += 1
if self.PsIndex >= len(self.MyList) {
self.PsIndex = len(self.MyList) - 1
}
cur_ni := self.MyList[self.PsIndex]
if cur_ni.(*NetItem).PosY+cur_ni.(*NetItem).Height > self.Height {
for i := 0; i < len(self.MyList); i++ {
self.MyList[i].(*NetItem).PosY -= self.MyList[i].(*NetItem).Height
}
}
}
func (self *BluetoothPage) KeyDown(ev *event.Event) {
if ev.Data["Key"] == UI.CurKeys["A"] || ev.Data["Key"] == UI.CurKeys["Menu"] {
if self.Offline == true {
self.AbortedAndReturnToUpLevel()
return
}
a, nil := bleapi.GetAdapter(adapterID)
err := a.StopDiscovery()
if err != nil {
fmt.Println(err)
}
self.HideBox()
self.ReturnToUpLevelPage()
self.Screen.Refresh()
self.Screen.FootBar.ResetNavText()
}
if ev.Data["Key"] == UI.CurKeys["Up"] {
self.ScrollUp()
self.Screen.Refresh()
}
if ev.Data["Key"] == UI.CurKeys["Down"] {
self.ScrollDown()
self.Screen.Refresh()
}
if ev.Data["Key"] == UI.CurKeys["X"] {
if self.Offline == false {
self.Rescan()
}
}
if ev.Data["Key"] == UI.CurKeys["Y"] {
if len(self.MyList) == 0 {
return
}
if self.Offline == true {
return
}
self.InfoPage.Props = self.MyList[self.PsIndex].(*NetItem).Props
self.InfoPage.Path = self.MyList[self.PsIndex].(*NetItem).Path
self.InfoPage.MyDevice = self.MyList[self.PsIndex].(*NetItem).Device
self.Screen.PushPage(self.InfoPage)
self.Screen.Refresh()
}
if ev.Data["Key"] == UI.CurKeys["B"] {
if self.Offline == false {
self.TryConnect()
}
}
}
func (self *BluetoothPage) Draw() {
self.ClearCanvas()
if len(self.MyList) == 0 {
return
}
if len(self.MyList)*NetItemDefaultHeight > self.Height {
self.Ps.(*BleInfoPageSelector).Width = self.Width - 11
self.Ps.Draw()
for _, v := range self.MyList {
v.Draw()
}
self.Scroller.UpdateSize(len(self.MyList)*NetItemDefaultHeight, self.PsIndex*NetItemDefaultHeight)
self.Scroller.Draw()
} else {
self.Ps.(*BleInfoPageSelector).Width = self.Width
self.Ps.Draw()
for _, v := range self.MyList {
v.Draw()
}
}
}