Este arquivo PRW contém o ferramental necessário para customizações ADVPL à partir do CloudBridge, nele você entenderá todo o mecanismo de comunicação entre o Navegador Embedado (JavaScript) e o ADVPL.
As funções mais importantes são:
Function u_CBCustom()
Função principal, deve ser inserida na TAG mainFunction no arquivo .cloud, base do aplicativo, para ser instanciada no momento da abertura.
Function loadFinished()
Função atribuida como bloco de código do oWebEngine:bLoadFinished, ela será disparada ao termino da carga das páginas e como parâmetro recebe a URL carregada.
Function notificationTapped()
Função atribuida como bloco de código do oMobile:bNotificationTapped, ela será disparada quando a notificação for selecionada no dispositivo móvel. Ela recebe o ID inserido na notificação.
Function jsToAdvpl()
Função atribuida como bloco de código do oWebChannel:bJsToAdvpl, ela será disparada durante o recebimento da Função dialog.jsToAdvpl() que será disparada via JavaScript.
Arquivo
#include "TOTVS.CH" #include "FILEIO.CH" #INCLUDE "TBIConn.ch" Function u_CBCustom() // --------------------------------------------------------------- local i local oWebChannel local cMultGet := "" local cHost,nPort,lConnected local tempPath := GetTempPath() PRIVATE oWebEngine PRIVATE oMultGet PRIVATE oGetLink PRIVATE oDlg oDlg := TWindow():New(10, 10, 800, 600, "TOTVS - CloudBridge", NIL, NIL, NIL, NIL, NIL, NIL, NIL,; CLR_BLACK, CLR_WHITE, NIL, NIL, NIL, NIL, NIL, NIL, .T. ) oDlg:setCSS("QPushButton{margin: 1px; background-color: "+_colorTOTVS+"; border: none; color: #fff; }") // Android ou iOS if _rmtType == 8 .Or. _rmtType == 9 // Libera rotacao do dispositivo PRIVATE oMobile := TMobile():New(oDlg) oMobile:SetScreenOrientation(-1) // Temporario do dispositivo tempPath := oMobile:GetTempPath() + "/" // Prepara bloco de codigo pra callBack de notificacao oMobile:bNotificationTapped := {|id| notificationTapped(id) } endif // Prepara o conector WebSocket, sempre pra LOCALHOST e retorna a PORTA LIVRE oWebChannel := TWebChannel():New() nPort := oWebChannel::connect() cHost := oWebChannel:cHost nPort := oWebChannel:nPort lConnected := oWebChannel:lConnected // Verifica conexão if !lConnected msgStop("Erro na conexão com o WebSocket") return endif // Define o CallBack JavaScript oWebChannel:bJsToAdvpl := {|self,codeType,codeContent| jsToAdvpl(self,codeType,codeContent) } // Monta link GLOBAL if subs(tempPath,1,2) == "C:" tempPath := "file:///" + strTran(tempPath, "\", "/") endif mainHtml := tempPath + mainHtml oWebEngine := TWebEngine():New(oDlg, 0, 0, 100, 100,, nPort) oWebEngine:bLoadFinished := {|self,url| loadFinished(self,url) } oWebEngine:setAsMain() // Define como WebEngine que recebera o KEY_BACK (Android) // Painel superior @ 000, 000 MSPANEL pnTop SIZE 250, 024 OF oDlg COLORS 0, 16777215 RAISED // Mansano: 250, 012 pnTop:setCss("QFrame{background-color: #6BB4F1;}") // Alinha componentes pnTop:Align := CONTROL_ALIGN_TOP oWebEngine:Align := CONTROL_ALIGN_ALLCLIENT // CONOUT fake para exibir a mensageria oFont1 := TFont():New("Courier",,018,,.F.,,,,,.F.,.F.) @ 038, 000 MSPANEL ConOut SIZE 250, 16 OF oDlg COLORS 0, 16777215 RAISED oMultGet := TSimpleEditor():New( 0,0,ConOut, 50,50,"",, {| u | if( pCount() > 0, cMultGet := u, cMultGet )}, oFont1, .T.) ConOut:Align := CONTROL_ALIGN_BOTTOM oMultGet:Align := CONTROL_ALIGN_ALLCLIENT CreateFormTop(pnTop) oDlg:Activate("MAXIMIZED") Return // Parseia o Json em um Hash Static Function getJsonHash(jsonData, oHash) // --------------------------------------------------------------- Local oJson := tJsonParser():New() Local jsonfields := {} Local nRetParser := 0 if empty(jsonData) return .F. endif // Converte JSON pra Hash return oJson:Json_Hash(jsonData, len(jsonData), @jsonfields, @nRetParser, @oHash) return // Retorna valor do campo no Hash Static Function getHField(oHash, jsonField) // --------------------------------------------------------------- Local xGet := Nil // Recupera valor do campo if HMGet(oHash, jsonField, xGet) return xGet else return "" endif return // Bloco de codigo do termino da carga da pagina static function loadFinished(self,url) // --------------------------------------------------------------- oGetLink:cText := url+space(1000) return // CALLBACK das notificacoes static function notificationTapped(id) conout("createNotification: id: " + cValToChar(id)) return // CALLBACK JavaScript static function jsToAdvpl(self,codeType,codeContent) // --------------------------------------------------------------- Local i Local oTmpHash := .F. Local xRet, lRet Local xVal, fnCallBack, cFile, aBarResult,; aDevicesResult, sMsg, cPosition, id, title, message,; cQuery, nFields, nRecords, aAccelerometerSensor if valType(codeType) == "C" _conout("jsToAdvpl->codeType: " + codeType + " = " + codeContent) if codeType == "runAdvpl" if getJsonHash(codeContent, @oTmpHash) xVal := &("{|| " + getHField(oTmpHash, "codeBlock") + "}") if valType(xVal) == "B" xRet := eval(xVal) xRet := cValToChar(xRet) // Converte pra String // Executa o CallBack fnCallBack = getHField(oTmpHash, "callBack")+"('" +xRet+ "')" oWebEngine:runJavaScript(fnCallBack) endif else _msgAlert("RunAdvpl command not executed", "CloudBridge") endif return endif // Android Ou iOS if _rmtType == 8 .Or. _rmtType == 9 // Tira foto if codeType == "getPicture" cFile:= oMobile:TakePicture() _conout("[getPicture] - " + cFile) // Executa o CallBack fnCallBack = codeContent+"('" +cFile+ "')" oWebEngine:runJavaScript(fnCallBack) return endif // Codigo de barras if codeType == "barCodeScanner" aBarResult := oMobile:BarCode() If aBarResult[1] = "" _conout("[barCode] - " + "Nenhum código de barras disponÃvel.") Else _conout("[barCode] - " + "Código: " + aBarResult[1] + chr(13) + "Formato: " + aBarResult[2]) // Executa o CallBack fnCallBack = codeContent+"('" +aBarResult[1]+ "')" oWebEngine:runJavaScript(fnCallBack) EndIf return endif // Verifica dispositivos pareados if codeType == "pairedDevices" aDevicesResult:= oMobile:GetPairedBluetoothDevices() sMsg := "" For i := 1 to len(aDevicesResult) sMsg := sMsg + "Nome: " + aDevicesResult[i][1] + chr(13) sMsg := sMsg + "Endereço: " + aDevicesResult[i][2] + chr(13) + chr(13) Next i If sMsg = "" sMsg := "Nenhum dispositivo pareado ou interface Bluetooth desligada." Else sMsg := "Dispositivos Bluetooth Pareados:" + chr(13) + chr(13) + sMsg EndIf _conout("[bluetooth] - " + sMsg) // Executa o CallBack fnCallBack = codeContent+"('" +SubStr(sMsg,1,30)+ "')" oWebEngine:runJavaScript(fnCallBack) return endif // Libera orientacao if codeType == "unlockOrientation" oMobile:SetScreenOrientation(-1) _conout("[unlockOrientation]") return endif // Trava orientacao if codeType == "lockOrientation" oMobile:SetScreenOrientation(2) _conout("[lockOrientation]") return endif // GPS if codeType == "getCurrentPosition" cPosition := oMobile:getGeoCoordinate(1) // PARA LIMPAR O SINAL DE GRAUS CHR(176) //cPosition := StrTran(cPosition, chr(176), "") _conout("[getCurrentPosition] - " + cPosition) // Executa o CallBack fnCallBack = codeContent+"('" +cPosition+ "')" oWebEngine:runJavaScript(fnCallBack) return endif // Testa perifericos if codeType == "testDevice" if getJsonHash(codeContent, @oTmpHash) lRet := oMobile:TestDevice(getHField(oTmpHash, "testFeature")) // Executa o CallBack fnCallBack = getHField(oTmpHash, "callBack")+"('" +cValToChar(lRet)+ "')" oWebEngine:runJavaScript(fnCallBack) else _msgAlert("testDevice command not executed", "CloudBridge") endif return endif // Cria notificacao // O callBack sera disparado pelo bloco de codigo bNotificationTapped if codeType == "createNotification" if getJsonHash(codeContent, @oTmpHash) id := getHField(oTmpHash, "id") title := getHField(oTmpHash, "title") message := getHField(oTmpHash, "message") oMobile:CreateNotification(id, title, message) else _msgAlert("createNotification command not executed", "CloudBridge") endif return endif // Abre configuracoes if codeType == "openSettings" oWebEngine:runJavaScript("openSettings") oMobile:OpenSettings(val(codeContent)) return endif // Recupera diretorio temporario if codeType == "getTempPath" fnCallBack = codeContent + "('" + oMobile:GetTempPath() + "');" oWebEngine:runJavaScript(fnCallBack) endif // Aciona o vibracall do dispositivo if codeType == "vibrate" oMobile:vibrate(val(codeContent)) endif // Efetua uma leitura no sensor de acelerômetro do dispositivo if codeType == "readAccelerometer" aAccelerometerSensor:= oMobile:ReadAccelerometer() sMsg := "Valores lidos do sensor acelerometro (m/s2):" + "\n" sMsg += "x:" + str(aAccelerometerSensor[1]) + "\n" sMsg += "y:" + str(aAccelerometerSensor[2]) + "\n" sMsg += "z:" + str(aAccelerometerSensor[3]) _conout("accelerometer - " + sMsg) // Executa o CallBack fnCallBack = codeContent+"('" +sMsg+ "')" oWebEngine:runJavaScript(fnCallBack) return endif // Pre-insere um contato da agenda do dispositivo if codeType == "addContact" if getJsonHash(codeContent, @oTmpHash) aEmails := {} aPhones := {} aPostals := {} oTMobCont := TMobileContact():New() oTMobCont:cName := getHField(oTmpHash, "name") oTMobCont:cCompany := getHField(oTmpHash, "company") oTMobCont:cJobTitle := getHField(oTmpHash, "jobTitle") oTMobCont:cNote := getHField(oTmpHash, "note") cEmail1 := getHField(oTmpHash, "email1") nEmailType1 := getHField(oTmpHash, "emailType1") aAdd( aEmails, TMobileContactEmail():New2( nEmailType1, cEmail1 ) ) cPhone1 := getHField(oTmpHash, "phone1") nPhoneType1 := getHField(oTmpHash, "phoneType1") aAdd( aPhones, TMobileContactPhone():New2( nPhoneType1, cPhone1 ) ) aAdd( aPostals, TMobileContactPostal():New2( 2, getHField(oTmpHash, "postal") ) ) oTMobCont:aEmails := aEmails oTMobCont:aPhones := aPhones oTMobCont:aPostals := aPostals oMobile:AddContact( oTMobCont ) else _msgAlert("addContact command not executed", "CloudBridge") endif return endif // Procura contatos no dispositivo if codeType == "findContact" if getJsonHash(codeContent, @oTmpHash) aContacts := {} oTMobCont := TMobileContact():New() aContacts := oTMobCont:FindContact( getHField( oTmpHash, "filter" ) ) VarInfo( "Contatos encontrados", aContacts ) else _msgAlert("findContact command not executed", "CloudBridge") endif return endif endif if codeType == "change_link" oGetLink:cText := codeContent+space(1000) return endif // SQLITE - BEGIN ----------------------------------------------------------------------------- // SQLITE - Begin transction if codeType == "dbBegin" .and. getJsonHash(codeContent, @oTmpHash) if TCSQLExec("BEGIN") < 0 // Executa o CallBack de ERRO fnCallBack = getHField(oTmpHash, "callBackError") + "('" +TcSqlError()+ "');" oWebEngine:runJavaScript(fnCallBack) else // Executa o CallBack de SUCESSO fnCallBack = getHField(oTmpHash, "callBackSuccess") + "();" oWebEngine:runJavaScript(fnCallBack) endif endif // SQLITE - Commit if codeType == "dbCommit" .and. getJsonHash(codeContent, @oTmpHash) if TCSQLExec("COMMIT") < 0 // Executa o CallBack de ERRO fnCallBack = getHField(oTmpHash, "callBackError") + "('" +TcSqlError()+ "');" oWebEngine:runJavaScript(fnCallBack) else // Executa o CallBack de SUCESSO fnCallBack = getHField(oTmpHash, "callBackSuccess") + "();" oWebEngine:runJavaScript(fnCallBack) endif endif // SQLITE - RollBack if codeType == "dbRollback" .and. getJsonHash(codeContent, @oTmpHash) if TCSQLExec("ROLLBACK") < 0 // Executa o CallBack de ERRO fnCallBack = getHField(oTmpHash, "callBackError") + "('" +TcSqlError()+ "');" oWebEngine:runJavaScript(fnCallBack) else // Executa o CallBack de SUCESSO fnCallBack = getHField(oTmpHash, "callBackSuccess") + "();" oWebEngine:runJavaScript(fnCallBack) endif endif // SQLITE - Executa query if codeType == "dbExec" if getJsonHash(codeContent, @oTmpHash) cQuery := getHField(oTmpHash, "query") // Testa query if TCSQLExec(cQuery) < 0 // Executa o CallBack de ERRO fnCallBack = getHField(oTmpHash, "callBackError") + "('" +TcSqlError()+ "');" oWebEngine:runJavaScript(fnCallBack) else // Executa o CallBack de SUCESSO fnCallBack = getHField(oTmpHash, "callBackSuccess") + "();" oWebEngine:runJavaScript(fnCallBack) endif else _msgAlert("dbExec command not executed", "CloudBridge") endif endif // SQLITE - Recupera dados da query if codeType == "dbGet" if getJsonHash(codeContent, @oTmpHash) cQuery := getHField(oTmpHash, "query") // Testa query if TCSQLExec(cQuery) < 0 // Executa o CallBack de ERRO fnCallBack = getHField(oTmpHash, "callBackError") + "('" +TcSqlError()+ "');" oWebEngine:runJavaScript(fnCallBack) return endif // Recupera os dados dbUseArea(.T., 'TOPCONN', TCGenQry(,,cQuery),'TRB', .F., .T.) nFields := TRB->(fCount()) nRecords := TRB->(recCount()) xRet := "[" while !TRB->(eof()) xRet += '{' for i := 1 to nFields xRet += quotedStr( fieldName(i) )+":" xRet += quotedStr( fieldGet(i) ) + iif(i < nFields, ",", "") next i xRet += '}' + iif(TRB->(recno()) < nRecords, ",", "") TRB->(DbSkip()) end xRet += "]" TRB->(dbCloseArea()) // Executa o CallBack fnCallBack = getHField(oTmpHash, "callBackSuccess") + "(" +xRet+ ");" oWebEngine:runJavaScript(fnCallBack) else _msgAlert("dbGet command not executed", "CloudBridge") endif endif // SQLITE - END ----------------------------------------------------------------------------- if codeType == "page_started" //form:loadForm() endif endif return // Cria componentes superiores static function CreateFormTop(pnTop) // --------------------------------------------------------------- Local oButton1 Local cLink := mainHtml + space(1000) Local oFontBtn := TFont():New("Arial Narrow",,022,,.F.,,,,,.F.,.F.) Local oFontGet := TFont():New("Arial",,022,,.F.,,,,,.F.,.F.) oWebEngine:navigate(mainHtml) @ 000, 221 BUTTON oButtonL1 PROMPT "<" SIZE 028, 011 OF pnTop ACTION {|| oWebEngine:goBack() } FONT oFontBtn PIXEL @ 000, 221 BUTTON oButtonL2 PROMPT ">" SIZE 028, 011 OF pnTop ACTION {|| oWebEngine:goForward() } FONT oFontBtn PIXEL oGetLink := TGet():New( 000, 000, { | u | If( PCount() == 0, cLink, cLink := u ) },pnTop, 220, 011,,, 0, 16777215,,.F.,,.T.,,.F.,,.F.,.F.,,.F.,.F. ,,"cLink",,,, ) oGetLink:setFont(oFontGet) @ 000, 221 BUTTON oButton1 PROMPT "Go" SIZE 028, 011 OF pnTop ACTION {|| oWebEngine:navigate(cLink) } FONT oFontBtn PIXEL @ 000, 221 BUTTON oButton2 PROMPT "Menu" SIZE 028, 011 OF pnTop ACTION {|| oGetLink:cText:=mainHtml, oMultGet:Load(""), oWebEngine:Navigate(mainHtml) } FONT oFontBtn PIXEL @ 000, 221 BUTTON oButtonClose PROMPT "X" SIZE 028, 011 OF pnTop ACTION {|| oDlg:end() } FONT oFontBtn PIXEL oButtonClose:setCSS("QPushButton{margin: 1px; background-color: #C75050; border: none; color: #fff; }") oGetLink:setCSS("QLineEdit{margin: 1px; border: 1px solid "+_colorTOTVS) oButtonL1:Align := CONTROL_ALIGN_LEFT oButtonL2:Align := CONTROL_ALIGN_LEFT oButtonClose:Align := CONTROL_ALIGN_RIGHT oButton2:Align := CONTROL_ALIGN_RIGHT oButton1:Align := CONTROL_ALIGN_RIGHT oGetLink:Align := CONTROL_ALIGN_ALLCLIENT return // --------------------------------------------------------------- // Funcoes de apoio // --------------------------------------------------------------- static function _conout(cText) conOut(cText) oMultGet:Load(cText) Return Static function _msgAlert(cText) // Android Ou iOS if _rmtType == 8 .Or. _rmtType == 9 oMobile:CreateNotification(999, "CloudBridge", cText) else msgAlert(cText) conout(cText) endif Return static function quotedStr(xText) return ('"'+ cValToChar(xText) +'"')