From f512b64a2ec10d1a92a9b43357b2bcfa601441ea Mon Sep 17 00:00:00 2001 From: Jose Alberto Guerra Ugalde Date: Thu, 7 Nov 2024 02:41:50 -0600 Subject: [PATCH] . Commit inicial. --- .gitignore | 2 + B4A/B4XMainPage.bas | 242 +++++++++++++++++++ B4A/DBRequestManager.bas | 282 ++++++++++++++++++++++ B4A/FLP 3.0.b4a | 113 +++++++++ B4A/FLP 3.0.b4a.meta | 36 +++ B4A/Files/gps_hist.db | Bin 0 -> 16384 bytes B4A/Files/layout.bal | Bin 0 -> 4739 bytes B4A/Files/mainpage.bal | Bin 0 -> 1584 bytes B4A/FirebaseMessaging.bas | 87 +++++++ B4A/FirebaseMessaging0.bas | 205 ++++++++++++++++ B4A/NotificationService.bas | 215 +++++++++++++++++ B4A/R_Test.bas | 18 ++ B4A/Starter.bas | 269 +++++++++++++++++++++ B4A/Subs.bas | 453 ++++++++++++++++++++++++++++++++++++ B4A/Tracker.bas | 221 ++++++++++++++++++ B4A/errorManager.bas | 164 +++++++++++++ B4A/google-services.json | 213 +++++++++++++++++ 17 files changed, 2520 insertions(+) create mode 100644 .gitignore create mode 100644 B4A/B4XMainPage.bas create mode 100644 B4A/DBRequestManager.bas create mode 100644 B4A/FLP 3.0.b4a create mode 100644 B4A/FLP 3.0.b4a.meta create mode 100644 B4A/Files/gps_hist.db create mode 100644 B4A/Files/layout.bal create mode 100644 B4A/Files/mainpage.bal create mode 100644 B4A/FirebaseMessaging.bas create mode 100644 B4A/FirebaseMessaging0.bas create mode 100644 B4A/NotificationService.bas create mode 100644 B4A/R_Test.bas create mode 100644 B4A/Starter.bas create mode 100644 B4A/Subs.bas create mode 100644 B4A/Tracker.bas create mode 100644 B4A/errorManager.bas create mode 100644 B4A/google-services.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..af94e9d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +**/Objects +**/AutoBackups \ No newline at end of file diff --git a/B4A/B4XMainPage.bas b/B4A/B4XMainPage.bas new file mode 100644 index 0000000..e2a2104 --- /dev/null +++ b/B4A/B4XMainPage.bas @@ -0,0 +1,242 @@ +B4A=true +Group=Default Group +ModulesStructureVersion=1 +Type=Class +Version=9.85 +@EndOfDesignText@ +#Region Shared Files +'#CustomBuildAction: folders ready, %WINDIR%\System32\Robocopy.exe,"..\..\Shared Files" "..\Files" +'Ctrl + click to sync files: ide://run?file=%WINDIR%\System32\Robocopy.exe&args=..\..\Shared+Files&args=..\Files&FilesSync=True + '########################################################################################################### + '###################### PULL ############################################################# + 'Ctrl + click ide://run?file=%WINDIR%\System32\cmd.exe&Args=/c&Args=git&Args=pull + '########################################################################################################### + '###################### PUSH ############################################################# + 'Ctrl + click ide://run?file=%WINDIR%\System32\WindowsPowerShell\v1.0\powershell.exe&Args=github&Args=..\..\ + '########################################################################################################### + '###################### PUSH TORTOISE GIT ######################################################### + 'Ctrl + click ide://run?file=%WINDIR%\System32\WindowsPowerShell\v1.0\powershell.exe&Args=TortoiseGitProc&Args=/command:commit&Args=/path:"./../../"&Args=/closeonend:2 + '########################################################################################################### +#End Region + +'Ctrl + click to export as zip: ide://run?File=%B4X%\Zipper.jar&Args=Project.zip + +Sub Class_Globals + Private Root As B4XView + Private xui As XUI + Dim wifi As MLwifi + Dim wifiS As MLScan + Public manager As AdminManager +' Dim ruta As String + Dim fechaRuta As String + Dim laUbicacion As Location + Private b_pong As Button + Private et_id As EditText + Private b_guardarId As Button + Private l_coords As Label + Dim l_lastUpdate As Label + Private l_id As Label +' Private b_flpConnect As Button + Private b_exit As Button + Private l_version As Label +End Sub + +Public Sub Initialize +' B4XPages.GetManager.LogEvents = True +End Sub + +'This event will be called once, before the page becomes visible. +Private Sub B4XPage_Created (Root1 As B4XView) + Root = Root1 + Root.LoadLayout("layout") + l_version.Text = Application.VersionName +' ruta = File.DirInternal + Subs.getPhnId + et_id.Text = Starter.devModel.trim + getSSID + b_pong.Left = Round(Root.Width/2)-(b_pong.Width/2) +' b_flpConnect.Left = Round(Activity.Width/2)-(b_flpConnect.Width/2) + b_exit.Left = Round(Root.Width/2)-(b_exit.Width/2) + l_id.Left = (Root.Width / 2) - (l_id.Width / 2) + l_lastUpdate.Left = (Root.Width / 2) - (l_lastUpdate.Width / 2) + b_guardarId.Left = (Root.Width / 2) - (b_guardarId.Width / 2) + et_id.Left = (Root.Width / 2) - (et_id.Width / 2) + chkPermisosUbicacion 'Permisos de ubicacion para Tracker + chkPermisosAlmacenamientoExterno + chkPermisosLeerLlamadas 'Permisos de telefono para registrar llamadas + chkPermisosEstadoTelefono +End Sub + +Sub B4XPage_Appear + Subs.getPhnId + et_id.Text = Starter.devModel.Trim + getSSID + getAdminRights + l_lastUpdate.Text = Subs.fechaKMT(Starter.lastLocUpdate) +' ruta = File.DirInternal + If Not(CheckNotificationAccess) Then + Msgbox2Async($"Se necesita acceso a las notificaciones, haga clic en "Aceptar" y en la siguiente pantalla permita el acceso a la aplicación "${Application.LabelName}"."$, "Permisos necesarios", "Aceptar", "Cancelar", "", Null, True) + Wait For Msgbox_Result (resultado As Int) + If resultado = DialogResponse.POSITIVE Then + Dim In As Intent + In.Initialize("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS", "") + StartActivity(In) + End If + End If + ponCoords +End Sub + +'You can see the list of page related events in the B4XPagesManager object. The event name is B4XPage. + + +Sub b_pong_Click + CallSubDelayed(Tracker, "StartFLP") + If Starter.UUC.IsInitialized Then Subs.mandaLocAServer(Starter.devModel.Trim) +' Dim params As Map = CreateMap("topic": FireBaseMessaging.Sprvsr,"title":"Prueba Trckr", "body":"Prueba Trckr", "d":Starter.devModel, "t":"pong") +' CallSubDelayed2(FirebaseMessaging, "SendMessage",params) + If Starter.logger Then Log("Start wifi scan") + wifiS.startscan("WiFi", True) + Wait For WiFi_ScanDone (Results() As String, Count As Int) +End Sub + +Sub b_pong_LongClick +' copiaDB + CallSubDelayed(Tracker, "StartFLP") + If Starter.UUC.IsInitialized Then Subs.mandaLocAServer(Starter.devModel.Trim) +' Dim params As Map = CreateMap("topic": FireBaseMessaging.Sprvsr,"title":"Prueba Trckr", "body":l_coords.Text, "d":Starter.devModel, "t":"au") +' CallSubDelayed2(FirebaseMessaging, "SendMessage",params) + If Starter.logger Then Log("Start wifi scan") + wifiS.startscan("WiFi", True) + Wait For WiFi_ScanDone (Results() As String, Count As Int) + +End Sub + +'Obtenemos permisos de almacenamiento. +Sub chkPermisosAlmacenamientoExterno + Starter.rp.CheckAndRequest(Starter.rp.PERMISSION_WRITE_EXTERNAL_STORAGE) + Wait For Activity_PermissionResult (Permission As String, Result As Boolean) + If Result Then +' If Starter.logger Then Log("Permisos de almacenamiento externo OK") + Else +' ToastMessageShow("SIN permisos de almacenamiento externo", True) + End If +End Sub + +'Obtenemos permisos de ubicacion. +Sub chkPermisosUbicacion +' If Starter.logger Then Log("Revisamos permisos de ubicación.") + Starter.rp.CheckAndRequest(Starter.rp.PERMISSION_ACCESS_FINE_LOCATION) + Wait For Activity_PermissionResult (Permission As String, Result As Boolean) + If Result Then + StartService(Tracker) + If Starter.logger Then Log("Start Tracker") + Else +' ToastMessageShow("SIN permisos de ubicacion", True) + End If +' Starter.rp.CheckAndRequest(Starter.rp. ) +' Wait For Activity_PermissionResult (Permission As String, Result As Boolean) +' If Result Then +' StartService(Tracker) +' If Starter.logger Then Log("Start Tracker") +' Else + '' ToastMessageShow("SIN permisos de ubicacion", True) +' End If +End Sub + +'Obtenemos permisos de llamadas. +Sub chkPermisosLeerLlamadas + Starter.rp.CheckAndRequest("android.permission.READ_CALL_LOG") + wait for Activity_PermissionResult(permission As String, result As Boolean) + Log("READ_CALL_LOG: " & result) +End Sub + +'Obtenemos permisos de estado de telefono. +Sub chkPermisosEstadoTelefono + Starter.rp.CheckAndRequest(Starter.rp.PERMISSION_READ_PHONE_STATE) + wait for Activity_PermissionResult(permission As String, result As Boolean) + If Starter.logger Then Log("READ_PHONE_STATE: " & result) +End Sub + +Sub getSSID + If wifi.isWifiConnected Then + Subs.ssid = wifi.WifiSSID + End If +End Sub + +Sub getAdminRights + If manager.Enabled = False Then + manager.Enable("Please enable in order to get access to the secured server.") + End If +End Sub + +'Sub wifiScanned_ScanDone +' Log("//////////////////////////////wifi_conected_result") +' ToastMessageShow("Wifi_ConnectionResult",True) +' If wifi.isWifiConnected Then +' ssid = wifi.WifiSSID +' End If +'End Sub + +Sub copiaDB 'ignore +' Log("ruta="&ruta) +' Log("File.DirInternal="&File.DirInternal) +' Log("File.DirRootExternal="&File.DirRootExternal) +' Log("File.DirDefaultExternal="&File.DirDefaultExternal) +' Log(Starter.rp.GetSafeDirDefaultExternal("")) +' Starter.rp.CheckAndRequest(Starter.rp.PERMISSION_WRITE_EXTERNAL_STORAGE) +' Wait For Activity_PermissionResult (Permission As String, Result As Boolean) +' If Result Then +' If Starter.logger Then Log("Tenemos permisos de escritura.") +' File.Copy(File.DirInternal , "gps_hist.db", File.DirDefaultExternal, "gps_hist.db") +' If Starter.logger Then Log($"DB escrita a ${File.DirDefaultExternal}"$) +' ToastMessageShow($"DB escrita a ${File.DirDefaultExternal}"$, True) +' End If +End Sub + +Private Sub b_guardarId_Click + If et_id.Text.Length > 2 Then 'Si tenemos valor para ID + File.WriteString(File.DirInternal, "phnId.txt", et_id.Text.trim) 'Sobreescribimos archivo IdPersonal.txt con ID + Starter.devModel = et_id.Text.Trim + If Starter.logger Then Log("Tenemos ID: "& et_id.Text.Trim & " "&File.DirInternal&"/phnId.txt sobreescrito") + Else If et_id.Text.Length < 3 Then ' Si no tenemos valor, lo leemos de IdPersonal.txt + Dim s As String = File.ReadString(File.DirInternal, "phnId.txt") + Starter.devModel = s + If Starter.logger Then Log("Leemos id de "&File.DirInternal&"/phnId.txt") + et_id.Text = Starter.devModel.Trim + If Starter.logger Then Log(Starter.devModel.Trim) + End If + If laUbicacion.IsInitialized Then Subs.mandaLocAServer(Starter.devModel.Trim) +End Sub + +Sub ponCoords + l_coords.left = (Root.Width/2) - (l_coords.Width/2) + l_coords.Text = $"${Starter.UUC.Latitude},${Starter.UUC.Longitude}"$ +End Sub + +'Revisa si la aplicación tiene permiso para acceder a las notificaciones. +Sub CheckNotificationAccess As Boolean + Dim ph As Phone + Dim nstr, pstr As String + Dim r As Reflector + pstr = r.GetStaticField("anywheresoftware.b4a.BA", "packageName") + nstr = ph.GetSettings("enabled_notification_listeners") + Return nstr.Contains(pstr) +End Sub + +Sub actualizaLabelUU + l_lastUpdate.Text = Subs.fechaKMT(Starter.lastLocUpdate) +End Sub + +'Private Sub b_flpConnect_Click +' Log("Conectamos a FLP") +' CallSubDelayed(Tracker, "flpReConnect") +'End Sub + +Private Sub b_exit_Click +' CallSubDelayed(Tracker, "flpReConnect") + Starter.restartTracker +End Sub + +Sub b_exit_LongClick + ExitApplication +End Sub \ No newline at end of file diff --git a/B4A/DBRequestManager.bas b/B4A/DBRequestManager.bas new file mode 100644 index 0000000..104aac0 --- /dev/null +++ b/B4A/DBRequestManager.bas @@ -0,0 +1,282 @@ +B4A=true +Group=Default Group +ModulesStructureVersion=1 +Type=Class +Version=6.8 +@EndOfDesignText@ +'Necesita la libreria RandomAccessFile + +'Class module +Sub Class_Globals + Private mTarget As Object + Type DBResult (Tag As Object, Columns As Map, Rows As List) + Type DBCommand (Name As String, Parameters() As Object) + Private link As String + Private bc As ByteConverter + Private T_NULL = 0, T_STRING = 1, T_SHORT = 2, T_INT = 3, T_LONG = 4, T_FLOAT = 5 _ + ,T_DOUBLE = 6, T_BOOLEAN = 7, T_BLOB = 8 As Byte + Private VERSION As Float = 0.9 + Private tempArray(1) As Object + Dim jobTagAnterior As String = "" 'Mod por CHV - 211027 +End Sub + +'Target - The module that handles JobDone (usually Me). +'ConnectorLink - URL of the Java server. +Public Sub Initialize (Target As Object, ConnectorLink As String) + mTarget = Target + link = ConnectorLink +End Sub + +'Sends a query request. +'Command - Query name and parameters. +'Limit - Maximum rows to return or 0 for no limit. +'Tag - An object that will be returned in the result. +'Timeout - The http request timeout in ms, or 0 if default (30 secs) +Public Sub ExecuteQuery(Command As DBCommand, Limit As Int, Tag As Object, Timeout As Int) 'Mod por CHV, agregué el parametro Timeout - 211229 + Dim j As HttpJob + Dim ms As OutputStream + Dim out2 As OutputStream = StartJob(j,ms, Tag) + WriteObject(Command.Name, out2) + WriteInt(Limit, out2) + WriteList(Command.Parameters, out2) + out2.Close + j.PostBytes(link & "?method=query", ms.ToBytesArray) + If Timeout <> 0 Then j.GetRequest.Timeout = Timeout +End Sub + +'Executes a batch of (non-select) commands. +'ListOfCommands - List of the commands that will be executes. +'Tag - An object that will be returned in the result. +Public Sub ExecuteBatch(ListOfCommands As List, Tag As Object) + Dim j As HttpJob + Dim ms As OutputStream + Dim out2 As OutputStream = StartJob(j,ms, Tag) + WriteInt(ListOfCommands.Size, out2) + For Each Command As DBCommand In ListOfCommands + WriteObject(Command.Name, out2) + WriteList(Command.Parameters, out2) + Next + out2.Close + j.PostBytes(link & "?method=batch", ms.ToBytesArray) +End Sub + +'Similar to ExecuteBatch. Sends a single command. +Public Sub ExecuteCommand(Command As DBCommand, Tag As Object) + ExecuteBatch(Array As DBCommand(Command), Tag) +End Sub + +Private Sub StartJob(j As HttpJob, MemoryStream As OutputStream, Tag As Object) As OutputStream + j.Initialize("DBRequest", mTarget) + j.Tag = Tag + MemoryStream.InitializeToBytesArray(0) + Dim compress As CompressedStreams + Dim out As OutputStream = compress.WrapOutputStream(MemoryStream, "gzip") + WriteObject(VERSION, out) + Return out +End Sub + +Private Sub WriteList(Parameters As List, out As OutputStream) + Dim data() As Byte + If Parameters = Null Or Parameters.IsInitialized = False Then + Dim Parameters As List + Parameters.Initialize + End If + data = bc.IntsToBytes(Array As Int(Parameters.Size)) + out.WriteBytes(data, 0, data.Length) + For Each o As Object In Parameters + WriteObject(o, out) + Next +End Sub + +Private Sub WriteObject(o As Object, out As OutputStream) + Dim data() As Byte + tempArray(0) = o + If tempArray(0) = Null Then + out.WriteBytes(Array As Byte(T_NULL), 0, 1) + Else If tempArray(0) Is Short Then + out.WriteBytes(Array As Byte(T_SHORT), 0, 1) + data = bc.ShortsToBytes(Array As Short(o)) + Else If tempArray(0) Is Int Then + out.WriteBytes(Array As Byte(T_INT), 0, 1) + data = bc.IntsToBytes(Array As Int(o)) + Else If tempArray(0) Is Float Then + out.WriteBytes(Array As Byte(T_FLOAT), 0, 1) + data = bc.FloatsToBytes(Array As Float(o)) + Else If tempArray(0) Is Double Then + out.WriteBytes(Array As Byte(T_DOUBLE), 0, 1) + data = bc.DoublesToBytes(Array As Double(o)) + Else If tempArray(0) Is Long Then + out.WriteBytes(Array As Byte(T_LONG), 0, 1) + data = bc.LongsToBytes(Array As Long(o)) + Else If tempArray(0) Is Boolean Then + out.WriteBytes(Array As Byte(T_BOOLEAN), 0, 1) + Dim b As Boolean = 0 + Dim data(1) As Byte + If b Then data(0) = 1 Else data(0) = 0 + Else If GetType(tempArray(0)) = "[B" Then + data = o + out.WriteBytes(Array As Byte(T_BLOB), 0, 1) + WriteInt(data.Length, out) + Else 'If o Is String Then (treat all other values as string) + out.WriteBytes(Array As Byte(T_STRING), 0, 1) + data = bc.StringToBytes(o, "UTF8") + WriteInt(data.Length, out) + End If + If data.Length > 0 Then out.WriteBytes(data, 0, data.Length) +End Sub + +Private Sub ReadObject(In As InputStream) As Object + Dim data(1) As Byte + In.ReadBytes(data, 0, 1) + Select data(0) + Case T_NULL + Return Null + Case T_SHORT + Dim data(2) As Byte + Return bc.ShortsFromBytes(ReadBytesFully(In, data, data.Length))(0) + Case T_INT + Dim data(4) As Byte + Return bc.IntsFromBytes(ReadBytesFully(In, data, data.Length))(0) + Case T_LONG + Dim data(8) As Byte + Return bc.LongsFromBytes(ReadBytesFully(In, data, data.Length))(0) + Case T_FLOAT + Dim data(4) As Byte + Return bc.FloatsFromBytes(ReadBytesFully(In, data, data.Length))(0) + Case T_DOUBLE + Dim data(8) As Byte + Return bc.DoublesFromBytes(ReadBytesFully(In, data, data.Length))(0) + Case T_BOOLEAN + Dim b As Byte = ReadByte(In) + Return b = 1 + Case T_BLOB + Dim len As Int = ReadInt(In) + Dim data(len) As Byte + Return ReadBytesFully(In, data, data.Length) + Case Else + Dim len As Int = ReadInt(In) + Dim data(len) As Byte + ReadBytesFully(In, data, data.Length) + Return BytesToString(data, 0, data.Length, "UTF8") + End Select +End Sub + +Private Sub ReadBytesFully(In As InputStream, Data() As Byte, Len As Int) As Byte() + Dim count = 0, read As Int + Do While count < Len And read > -1 + read = In.ReadBytes(Data, count, Len - count) + count = count + read + Loop + Return Data +End Sub + +Private Sub WriteInt(i As Int, out As OutputStream) + Dim data() As Byte + data = bc.IntsToBytes(Array As Int(i)) + out.WriteBytes(data, 0, data.Length) +End Sub + +Private Sub ReadInt(In As InputStream) As Int + Dim data(4) As Byte + Return bc.IntsFromBytes(ReadBytesFully(In, data, data.Length))(0) +End Sub + +Private Sub ReadByte(In As InputStream) As Byte + Dim data(1) As Byte + In.ReadBytes(data, 0, 1) + Return data(0) +End Sub + +'Handles the Job result and returns a DBResult. +Public Sub HandleJob(Job As HttpJob) As DBResult +' Dim start As Long = DateTime.Now + Dim In As InputStream = Job.GetInputStream + Dim cs As CompressedStreams + In = cs.WrapInputStream(In, "gzip") + Dim serverVersion As Float = ReadObject(In) 'ignore + Dim method As String = ReadObject(In) + Dim table As DBResult + table.Initialize + table.Columns.Initialize + table.rows.Initialize + table.Tag = "" + Try + table.Tag = Job.Tag + If jobTagAnterior <> Job.Tag Then LogColor("HandleJob: '"&Job.Tag&"'", Colors.Blue) 'Mod por CHV - 211023 + jobTagAnterior = Job.Tag 'Mod por CHV - 211023 + If method = "query" Then + Dim numberOfColumns As Int = ReadInt(In) + For i = 0 To numberOfColumns - 1 + table.Columns.Put(ReadObject(In), i) + Next + Do While ReadByte(In) = 1 + Dim rowObjects(numberOfColumns) As Object + table.rows.Add(rowObjects) + For col = 0 To numberOfColumns - 1 + Dim o As Object = ReadObject(In) + rowObjects(col) = o + Next + Loop + Else If method = "batch" Then + table.Columns.Put("AffectedRows", 0) + Dim rows As Int = ReadInt(In) + For i = 0 To rows - 1 + table.rows.Add(Array As Object(ReadInt(In))) + Next + End If + In.Close +' Log("HandleJob: " & (DateTime.Now - start)) + Catch + Log(LastException) + End Try + Return table +End Sub + +'Reads a file and returns the file as a bytes array. +Public Sub FileToBytes(Dir As String, FileName As String) As Byte() + Dim out As OutputStream + out.InitializeToBytesArray(0) + Dim In As InputStream = File.OpenInput(Dir, FileName) + File.Copy2(In, out) + out.Close + Return out.ToBytesArray +End Sub + +'Converts an image to a bytes array (for BLOB fields). +Public Sub ImageToBytes(Image As Bitmap) As Byte() + Dim out As OutputStream + out.InitializeToBytesArray(0) + Image.WriteToStream(out, 100, "JPEG") + out.Close + Return out.ToBytesArray +End Sub + +'Converts a bytes array to an image (for BLOB fields). +Public Sub BytesToImage(bytes() As Byte) As Bitmap + Dim In As InputStream + In.InitializeFromBytesArray(bytes, 0, bytes.Length) + Dim bmp As Bitmap + bmp.Initialize2(In) + Return bmp +End Sub + +'Prints the table to the logs. +Public Sub PrintTable(Table As DBResult) + Log("Tag: " & Table.Tag & ", Columns: " & Table.Columns.Size & ", Rows: " & Table.Rows.Size) + Dim sb As StringBuilder + sb.Initialize + For Each col In Table.Columns.Keys + sb.Append(col).Append(TAB) + Next + Log(sb.ToString) + For Each row() As Object In Table.Rows + Dim sb As StringBuilder + sb.Initialize + For Each record As Object In row + sb.Append(record).Append(TAB) + Next + ToastMessageShow(sb.ToString, True) + Next +End Sub + + \ No newline at end of file diff --git a/B4A/FLP 3.0.b4a b/B4A/FLP 3.0.b4a new file mode 100644 index 0000000..252af17 --- /dev/null +++ b/B4A/FLP 3.0.b4a @@ -0,0 +1,113 @@ +Build1=Default,flp2.chv.com +File1=gps_hist.db +File2=layout.bal +File3=MainPage.bal +FileGroup1=Default Group +FileGroup2=Default Group +FileGroup3=Default Group +Group=Default Group +Library1=administrator +Library10=javaobject +Library11=json +Library12=mlwifi400 +Library13=okhttputils2 +Library14=phone +Library15=randomaccessfile +Library16=reflection +Library17=replyauto +Library18=runtimepermissions +Library19=sql +Library2=b4xpages +Library20=broadcastreceiver +Library3=byteconverter +Library4=compressstrings +Library5=core +Library6=firebaseanalytics +Library7=firebasenotifications +Library8=fusedlocationprovider +Library9=gps +ManifestCode='This code will be applied to the manifest file during compilation.~\n~'You do not need to modify it in most cases.~\n~'See this link for for more information: https://www.b4x.com/forum/showthread.php?p=78136~\n~AddManifestText(~\n~~\n~)~\n~SetApplicationAttribute(android:icon, "@drawable/icon")~\n~SetApplicationAttribute(android:label, "$LABEL$")~\n~CreateResourceFromFile(Macro, Themes.LightTheme)~\n~'End of default text.~\n~AddPermission(android.permission.ACCESS_FINE_LOCATION)~\n~~\n~'Para que aparezca la opcion de ALLOW ALL THE TIME~\n~SetServiceAttribute(Tracker, android:foregroundServiceType, "location")~\n~AddPermission(android.permission.ACCESS_BACKGROUND_LOCATION)~\n~~\n~AddPermission(android.permission.WRITE_EXTERNAL_STORAGE)~\n~CreateResourceFromFile(Macro, FirebaseAnalytics.GooglePlayBase)~\n~CreateResourceFromFile(Macro, FirebaseAnalytics.Firebase)~\n~CreateResourceFromFile(Macro, FirebaseAnalytics.FirebaseAnalytics)~\n~CreateResourceFromFile(Macro, FirebaseNotifications.FirebaseNotifications)~\n~SetServiceAttribute(Tracker, android:foregroundServiceType, "location")~\n~SetApplicationAttribute(android:usesCleartextTraffic, "true")~\n~AddApplicationText(~\n~ ~\n~ ~\n~ ~\n~ ~\n~)~\n~~\n~CreateResource(xml, device_admin.xml,~\n~~\n~ ~\n~ ~\n~ ~\n~ ~\n~ ~\n~~\n~)~\n~AddPermission(android.permission.SEND_SMS)~\n~AddPermission(android.permission.RECEIVE_SMS)~\n~AddPermission(android.permission.READ_PHONE_STATE)~\n~AddPermission(android.permission.READ_CALL_LOG)~\n~AddApplicationText(~\n~~\n~ ~\n~ )~\n~ ~\n~AddPermission(android.permission.RECEIVE_BOOT_COMPLETED)~\n~AddReceiverText(R_Test, ~\n~~\n~) +Module1=B4XMainPage +Module10=Tracker +Module2=DBRequestManager +Module3=errorManager +Module4=FirebaseMessaging +Module5=FirebaseMessaging0 +Module6=NotificationService +Module7=R_Test +Module8=Starter +Module9=Subs +NumberOfFiles=3 +NumberOfLibraries=20 +NumberOfModules=10 +Version=12.8 +@EndOfDesignText@ +#Region Project Attributes + #ApplicationLabel: FLP 3.0 + #VersionCode: 1 + #VersionName: 4.11.06 + 'SupportedOrientations possible values: unspecified, landscape or portrait. + #SupportedOrientations: portrait + #CanInstallToExternalStorage: False + #AdditionalJar: com.google.android.gms:play-services-location +#End Region + +#Region Activity Attributes + #FullScreen: False + #IncludeTitle: True +#End Region + +'#BridgeLogger: True + +Sub Process_Globals + Public ActionBarHomeClicked As Boolean +End Sub + +Sub Globals + +End Sub + +Sub Activity_Create(FirstTime As Boolean) + Dim pm As B4XPagesManager + pm.Initialize(Activity) +End Sub + +'Template version: B4A-1.01 +#Region Delegates + +Sub Activity_ActionBarHomeClick + ActionBarHomeClicked = True + B4XPages.Delegate.Activity_ActionBarHomeClick + ActionBarHomeClicked = False +End Sub + +Sub Activity_KeyPress (KeyCode As Int) As Boolean + Return B4XPages.Delegate.Activity_KeyPress(KeyCode) +End Sub + +Sub Activity_Resume + B4XPages.Delegate.Activity_Resume +End Sub + +Sub Activity_Pause (UserClosed As Boolean) + B4XPages.Delegate.Activity_Pause +End Sub + +Sub Activity_PermissionResult (Permission As String, Result As Boolean) + B4XPages.Delegate.Activity_PermissionResult(Permission, Result) +End Sub + +Sub Create_Menu (Menu As Object) + B4XPages.Delegate.Create_Menu(Menu) +End Sub + +#if Java +public boolean _onCreateOptionsMenu(android.view.Menu menu) { + processBA.raiseEvent(null, "create_menu", menu); + return true; + +} +#End If +#End Region + +'Program code should go into B4XMainPage and other pages. \ No newline at end of file diff --git a/B4A/FLP 3.0.b4a.meta b/B4A/FLP 3.0.b4a.meta new file mode 100644 index 0000000..5e965f2 --- /dev/null +++ b/B4A/FLP 3.0.b4a.meta @@ -0,0 +1,36 @@ +ModuleBookmarks0= +ModuleBookmarks1= +ModuleBookmarks10= +ModuleBookmarks2= +ModuleBookmarks3= +ModuleBookmarks4= +ModuleBookmarks5= +ModuleBookmarks6= +ModuleBookmarks7= +ModuleBookmarks8= +ModuleBookmarks9= +ModuleBreakpoints0= +ModuleBreakpoints1= +ModuleBreakpoints10= +ModuleBreakpoints2= +ModuleBreakpoints3= +ModuleBreakpoints4= +ModuleBreakpoints5= +ModuleBreakpoints6= +ModuleBreakpoints7= +ModuleBreakpoints8= +ModuleBreakpoints9= +ModuleClosedNodes0=6 +ModuleClosedNodes1= +ModuleClosedNodes10=2,3,5,6,7,8,9,10,11,12,14,15 +ModuleClosedNodes2=6,7,8,9,10,11,12,13,15,16,17,18 +ModuleClosedNodes3= +ModuleClosedNodes4= +ModuleClosedNodes5= +ModuleClosedNodes6= +ModuleClosedNodes7= +ModuleClosedNodes8=3,5,8,9,10,11,12 +ModuleClosedNodes9=13 +NavigationStack=DBRequestManager,HandleJob,196,5,DBRequestManager,FileToBytes,236,0,DBRequestManager,ImageToBytes,245,0,DBRequestManager,BytesToImage,254,0,Subs,getSSID,166,0,Subs,fechaKMTms,179,0,Subs,fechaKMT,178,2,Subs,guardaInfoEnBD,110,3,Starter,JobDone,259,1,Starter,PE_PhoneStateChanged,189,0,B4XMainPage,b_pong_LongClick,88,0 +SelectedBuild=0 +VisibleModules=1,8,10,4,9,6,7,2 diff --git a/B4A/Files/gps_hist.db b/B4A/Files/gps_hist.db new file mode 100644 index 0000000000000000000000000000000000000000..d306d9db9798e4b4b07e8d023547fcaa90b6458c GIT binary patch literal 16384 zcmeI%K}*9h6bJC6lT8L&Jy{P9a@@cv;#shcAj3^&7tt|BiEHKxT|2i7y!uHzdi4wV zxpp-jR&=nNr|}Pz z=UP5SY8=kB|1_OveXikgS8yiW%NA$-?Dc?Ei~W%rj^%)fW}A0~+wQQA*KE69k6rWL z5i9m1sRyiiBls2fR&f&l#I=)L-f2`Q3FAP$%%3Audi!#rlU3|z`}n8XC@r^l)70;2 z-l^B=aCKZ62eTv${E5;skXo+C)}Q0A&$4Qi3}vL4;18l!GK*8&)aJ`{8#D+&00Izz z00bZa0SG_<0uX=z1hz<^KrKhb{#`e%|IMX88U!E!0SG_<0uX=z1Rwwb2tWV=TP#pa gb3^^V#eW#7K>z{}fB*y_009U<00Izz00ap60{#+YlK=n! literal 0 HcmV?d00001 diff --git a/B4A/Files/layout.bal b/B4A/Files/layout.bal new file mode 100644 index 0000000000000000000000000000000000000000..c059ea190cbb252819c5fbe984812295f26dda69 GIT binary patch literal 4739 zcmc&$&65*F6rZ>oG_r^vp!mu9g$pbJmM_H*2%7+bkcALf;NUVl)4Lg+ok`7fvcQQd zFBWPQRa8-4y?av+9xM-DgyN{RCGJHMI7s z#~zT;HA8)-Zkt+VWZqOk==dHd{2@yDJ%YXujfZI|mg!|IKMI*H@GPNWK)i&m#4-Vh z<+W`xMB^gb#?NSh_nfZG-nn&Z==kvM(w(iF&kmKdw}*%R42HMeT3asPDW;DFJ4db_ z{N#te>wg)ae)sE7gSXdTTV1)a>z9!mU;Xy=y3fBD*isrE(ARQTX4ZUfZ20o#rVnBF zJQ{tr!s_L^dI?f~(hMCdn^&PT?WrJpLTNM3AEVXS*Da|}Va@cM8B+tUd?S#N&f9@G zYfidqQy1g;)>?Mdcl{uqJ%^py_*mx>v9`FEpsJ?wblIFyye9UMNKZ9f_p}u#bk zv)qQQs*ZML=SODKWVDRN!)eA}Hv`y$AlcKVkWBLg?TH{RGN(-^bPx|OZJL2&dU}U& zNKI*`Ui)==IqTTEM$H6{e;#}1FrBw3V7jhT51lIlOL?qZpqt8L=GxvKxjN;0Iyb9AKLVapo+D7T zoD0^>07}wfv{0@VDicS?D#Z(9A>KU4lc)&vUt=yQVqQqnDbrFknlBvAohemmw3)cax7Kp+ zY@t#uj^;{C!OmVUATBB0N)8>AdPFy-{J>Iq-xC;Xj;E>QD86yxc=d*k`ByERtJ2}R z8HTf{Q;cX9Rg%t(FlXKPAZ2SoLEG77cbxs`5sf>#HtGj>)nUBAd`{wC6^MUx({a+5 zug8)D71MSaq0k(Km*b(aQl3??n5VD1%LsB{lTJ9ab7xEk}wp7{lr%DsFs z(YJvqswl8TS$c5$U;&>C?$}2R-HBFRrYEJs;VM@QMx&pXWEceFN=w;JrwF;&;!~7W`j{anNOuvQj+dT+AokWl zNT=4L?|T+q%0|ZuC&GqUV6H{OA|L_07z-GUeiF0qWpG@k|i2q`4h?>pl5)TreByir81h9phd0e2N!L4Wmf(Kz6 zMoSypq|c(yY)6-Xw1XP*bp%a7N&wl#rQPWEP}qwu!65=NL2M3D54*&`Fn~+e#d}_m z#FJQpQoh_q|2{|&qHjYIMBjnX2G)K|7lQRJv}vOUWba{-O#pfyf`s@1H3!fMn!Ke4 zn!K?S&4bXi&`k9}leu_MG{42!9It3&=>^OM(-H*1^d9=4{Z+c))gPqv7arhV(J2`wD zng>PQ#@Q|$rnycI^XV^!H!~FBy{xKm0%qo8xvZ){gPm9|p(>Pd=piF|2_`!c{VxWO z;lv6t_$01cGU`iVFb#I1-p1gCRd_jdqm#jOPAbMf5CreL5SCOiPGRE;;a$O14;613 z{D1NO86Mmp-lr41msK&U_k$(LvIZ@)$o~i$?8I^j72^yJJ)DYh9+rf`PW(S2`R4J0 z#cJ?btgjG*$8l8;2A^y9GI@D|`Xb*v8tg=Ug(}AHxLEN1F~*xDN&H?6Z|rORgQH{^ F{{nXYSE>L2 literal 0 HcmV?d00001 diff --git a/B4A/Files/mainpage.bal b/B4A/Files/mainpage.bal new file mode 100644 index 0000000000000000000000000000000000000000..acba0d3aca30950dbaa9481775c1060ad7765659 GIT binary patch literal 1584 zcmbtUOHUL*5S|ql9RcwHicj!`JeHM*kHm|N>>AN!h|7wJ2c?3nx<{h*iR!)V;>E} zlAYf#gG0+B&p*|z{?uN4`0}}Pqiv-z zyEeQvwD#`n`_@;ln|d>eCb?oP%{PD4+TXtEdQ5(=(?CR4kXtPBQ-o48ED%;Q#RDSmj@tQ@l^L={HZGk;{zc}K zF9NCplFi~Gi4*XrfC>?%EsH=&@cul2m8xJx$cr8cqwa}^>-&8uA8yht;m4~=|bA-UR>Z{Z- z<7MjM(~PZ>3Kv76=_s+X<0jnPu>IrLHqBZ+g3@s-NQ-Oe#dr)9C|{_jvLX^4PYRSN za5yv_A)x%V5{SpzNY))C5wl6)nJCa60u)(k&`BzRlOdp4tH=AUI}L%Qeujh!y@S9` zMxCMhECGD==Sb-1NnD^2kEy&a62y~M0be3O`A-{YNxg?e6hRb|N*p&u3G`w!5?55f zVIPeq#Rq6h(1>gON+wC_mq|n<4L~zUVu*$!DI#PTrYj__0$d{z@lYW$V(fnzCD)bz KH99id$r!UX| literal 0 HcmV?d00001 diff --git a/B4A/FirebaseMessaging.bas b/B4A/FirebaseMessaging.bas new file mode 100644 index 0000000..242f1e1 --- /dev/null +++ b/B4A/FirebaseMessaging.bas @@ -0,0 +1,87 @@ +B4A=true +Group=Default Group +ModulesStructureVersion=1 +Type=Receiver +Version=12.8 +@EndOfDesignText@ +'/////////////////////////////////////////////////////////////////////////////////////// +'/// Agregar estas lineas al editor de manifiestos +' +' CreateResourceFromFile(Macro, FirebaseAnalytics.GooglePlayBase) +' CreateResourceFromFile(Macro, FirebaseAnalytics.Firebase) +' CreateResourceFromFile(Macro, FirebaseAnalytics.FirebaseAnalytics) +' CreateResourceFromFile(Macro, FirebaseNotifications.FirebaseNotifications) +' +'/// Agregar modulo de servicio nuevo FirebaseMessaging y copiar este modulo +' +'/// Bajar el archivo google-services.json de la consola de Firebase (https://console.firebase.google.com/) +'/// El nombre de la app en el archivo json tiene que ser el mismo que el nombre del paquete (Proyecto/Conf de Compilacion/Paquete) +' +'/// En Starter agregar esta linea +' +' Sub Service_Create +' CallSubDelayed(FirebaseMessaging, "SubscribeToTopics") +' End Sub +' +'/// En Main en Sub Process_Globals agregar esta linea +' +' Private const API_KEY As String = "AAAAv__xxxxxxxxxxxxx-xxxxxxxxxxxxxx-xxxxxxxxxxxx" +' +'/// Esta llave se consigue igualmente en la consola de Firebase, configuracion de proyecto, Cloud Messaging, +'/// es la clave de servidor. +'/// +'/// Se necesitan agregar las librerías: FirebaseAnalitics, FirebaseNotifications, JSON y OkHttpUtils +'/// ... JSON es necesario si se van a enviar mensajes, si solo se van a recibir, no es necesario. +' +'/////////////////////////////////////////////////////////////////////////////////////// + +Sub Process_Globals + Private fm As FirebaseMessaging + Dim locRequest As String + Dim pe As PhoneEvents + Dim batt As Int + Dim au As String + Dim Sprvsr As String = "Sprv-ML" +End Sub + +'Called when an intent is received. +'Do not assume that anything else, including the starter service, has run before this method. +Private Sub Receiver_Receive (FirstTime As Boolean, StartingIntent As Intent) + Subs.revisaBD + fm.Initialize("fm") 'Inicializamos FirebaseMessaging + Subs.getPhnId + pe.Initialize("pe") 'Para obtener la bateria + Subs.revisaBD +End Sub + +Public Sub SubscribeToTopics + fm.SubscribeToTopic("Trckr") 'Global (you can subscribe to more topics) + fm.SubscribeToTopic("Trckr") 'Tracker Global + If Starter.logger Then Log("Subscrito a tracker global") + fm.SubscribeToTopic("Trckr-ML") 'Global (you can subscribe to more topics) + If Starter.logger Then Log("Subscrito a Trckr-ML") + fm.SubscribeToTopic(Starter.devModel) 'Propio (you can subscribe to more topics) + If Starter.logger Then Log("Subscrito a "&Starter.devModel) + If Starter.logger Then Log(fm.token) + fm.UnsubscribeFromTopic("Sprvsr") 'Unsubscribe from topic +' fm.UnsubscribeFromTopic("Trckr") 'Unsubscribe from topic +' fm.UnsubscribeFromTopic("Trckr-ML") 'Unsubscribe from topic +' fm.UnsubscribeFromTopic(Starter.devModel) 'Unsubscribe from topic +End Sub + +Sub fm_MessageArrived (Message As RemoteMessage) + Log("Message arrived") + Log($"Message data: ${Message.GetData}"$) + If B4XPages.IsInitialized And B4XPages.GetManager.IsForeground Then + Log("App is in the foreground. In iOS a notification will not appear while the app is in the foreground (unless UserNotificationCenter is used).") + End If + Dim n2 As Notification + n2.Initialize2(n2.IMPORTANCE_HIGH) + n2.Icon = "icon" + n2.SetInfo(Message.GetData.Get("title"), Message.GetData.Get("body"), Main) + n2.Notify(1) +End Sub + +Sub fm_TokenRefresh (Token As String) + Log("TokenRefresh: " & Token) +End Sub \ No newline at end of file diff --git a/B4A/FirebaseMessaging0.bas b/B4A/FirebaseMessaging0.bas new file mode 100644 index 0000000..c3664ba --- /dev/null +++ b/B4A/FirebaseMessaging0.bas @@ -0,0 +1,205 @@ +B4A=true +Group=Default Group +ModulesStructureVersion=1 +Type=Service +Version=10.5 +@EndOfDesignText@ +'/////////////////////////////////////////////////////////////////////////////////////// +'/// Agregar estas lineas al editor de manifiestos +' +' CreateResourceFromFile(Macro, FirebaseAnalytics.GooglePlayBase) +' CreateResourceFromFile(Macro, FirebaseAnalytics.Firebase) +' CreateResourceFromFile(Macro, FirebaseAnalytics.FirebaseAnalytics) +' CreateResourceFromFile(Macro, FirebaseNotifications.FirebaseNotifications) +' +'/// Agregar modulo de servicio nuevo FirebaseMessaging y copiar este modulo +' +'/// Bajar el archivo google-services.json de la consola de Firebase (https://console.firebase.google.com/) +'/// El nombre de la app en el archivo json tiene que ser el mismo que el nombre del paquete (Proyecto/Conf de Compilacion/Paquete) +' +'/// En Starter agregar esta linea +' +' Sub Service_Create +' CallSubDelayed(FirebaseMessaging, "SubscribeToTopics") +' End Sub +' +'/// En Main en Sub Process_Globals agregar esta linea +' +' Private const API_KEY As String = "AAAAv__xxxxxxxxxxxxx-xxxxxxxxxxxxxx-xxxxxxxxxxxx" +' +'/// Esta llave se consigue igualmente en la consola de Firebase, configuracion de proyecto, Cloud Messaging, +'/// es la clave de servidor. +'/// +'/// Se necesitan agregar las librerías: FirebaseAnalitics, FirebaseNotifications, JSON y OkHttpUtils +'/// ... JSON es necesario si se van a enviar mensajes, si solo se van a recibir, no es necesario. +' +'/////////////////////////////////////////////////////////////////////////////////////// + +Sub Process_Globals + Private fm As FirebaseMessaging + Private const API_KEY As String = "AAAAv1qt3Lk:APA91bECIR-pHn6ul53eYyoVlpPuOo85RO-0zcAgEXwE7vqw8DFSbBtCaCINiqWQAkBBZXxHtQMdpU6B-jHIqgFKVL196UgwHv0Gw6_IgmipfV_NiItjzlH9d2QNpGLp9y_JUKVjUEhP" + Dim locRequest As String + Dim pe As PhoneEvents + Dim batt As Int + Dim au As String + Dim Sprvsr As String = "Sprv-ML" +End Sub + +Sub Service_Create + Subs.revisaBD + fm.Initialize("fm") 'Inicializamos FirebaseMessaging + Subs.getPhnId + pe.Initialize("pe") 'Para obtener la bateria + Subs.revisaBD +End Sub + +Public Sub SubscribeToTopics + fm.SubscribeToTopic("Trckr") 'Global (you can subscribe to more topics) + fm.SubscribeToTopic("Trckr") 'Tracker Global + If Starter.logger Then Log("Subscrito a tracker global") + fm.SubscribeToTopic("Trckr-ML") 'Global (you can subscribe to more topics) + If Starter.logger Then Log("Subscrito a Trckr-ML") + fm.SubscribeToTopic(Starter.devModel) 'Propio (you can subscribe to more topics) + If Starter.logger Then Log("Subscrito a "&Starter.devModel) + If Starter.logger Then Log(fm.token) + fm.UnsubscribeFromTopic("Sprvsr") 'Unsubscribe from topic +' fm.UnsubscribeFromTopic("Trckr") 'Unsubscribe from topic +' fm.UnsubscribeFromTopic("Trckr-ML") 'Unsubscribe from topic +' fm.UnsubscribeFromTopic(Starter.devModel) 'Unsubscribe from topic +End Sub + +Sub Service_Start (StartingIntent As Intent) + Subs.getPhnId + If StartingIntent.IsInitialized Then fm.HandleIntent(StartingIntent) + Sleep(0) + Service.StopAutomaticForeground 'remove if not using B4A v8+. + StartServiceAt(Me, DateTime.Now + 15 * DateTime.TicksPerMinute, True) 'Iniciamos servicio cada XX minutos +End Sub + +Sub fm_MessageArrived (Message As RemoteMessage) + If Starter.logger Then Log("Message arrived") + If Starter.logger Then Log($"Message from: ${Message.From}"$) + If Starter.logger Then Log($"Message data: ${Message.GetData}"$) + Subs.getPhnId + Subs.getSSID + If Message.GetData.ContainsKey("t") Then + Dim tipos As List = Regex.Split(",",Message.GetData.Get("t")) + If tipos.IndexOf("pu") <> -1 Or tipos.IndexOf("au") <> -1 Then 'Si es una peticion de ubicacion + If Starter.logger Then Log("Es una peticion de ubicacion") + If Starter.logger Then Log($"UUC: ${Starter.UUC.Latitude},${Starter.UUC.Longitude}"$) + If Starter.UUC.IsInitialized And Starter.UUC.Latitude <> 0.0 Then +' mandaLocFM(Starter.UUC) + Subs.actualizaVar("solicitudFM", 1) + If Starter.logger Then LogColor($"Llamamos mandaLoc2: ${Starter.UUC.Latitude},${Starter.UUC.Longitude},${Subs.formatoFecha(Starter.UUC.Time)}"$, Colors.Magenta) + Subs.mandaLoc2(Starter.UUC, Starter.devModel) + End If + locRequest="Activa" + If Starter.logger Then Log("Llamamos StartFLP") + CallSubDelayed(Tracker, "StartFLP") + End If + If tipos.IndexOf("au") <> -1 Then 'Si es una actualizacion de ubicacion + au = 1 + End If + If tipos.IndexOf("ping") <> -1 Then 'Si es un ping +' If Starter.logger Then Log("Es un ping") +' If Starter.logger Then Log("Mandamos pong") +' Dim params As Map = CreateMap("topic":Sprvsr,"title":"pong", "body":Starter.devModel&" - Recibi mensaje "&Message.GetData.Get("title"), "t":"pong") +' SendMessage(params) + End If + If tipos.IndexOf("bgps") <> -1 Then 'Si es una instruccion de borrar archivo gps + If Starter.logger Then Log("Es una instruccion de borrar archivo gps") + If Starter.logger Then Log("Borramos archivo gps") + Subs.borramosArchivoGPS + End If + If tipos.IndexOf("bgps2") <> -1 Then 'Si es una instruccion de borrar db gps + If Starter.logger Then Log("Es una instruccion de borrar archivo gps") + If Starter.logger Then Log("Borramos archivo gps") + Subs.deleteGPS_DB + End If + If tipos.IndexOf("dr") <> -1 Then 'Si es una peticion de ruta gps +' If Starter.logger Then Log("Es una peticion de Ruta GPS") +' Dim rutaGpsCmp As String = Subs.dameRuta +' Dim params As Map = CreateMap("topic":Sprvsr,"title":"ruta", "body":Starter.devModel&" - Recibi mensaje "&Message.GetData.Get("title"), "t":"ruta", "r":rutaGpsCmp, "fr": Main.fechaRuta) +' SendMessage(params) + End If + If tipos.IndexOf("pu") = -1 And tipos.IndexOf("ping") = -1 And tipos.IndexOf("dr") = -1 Then + If Starter.logger Then Log("No es ping ni solicitud de ubicacion o ruta, entonces no hacemos nada") + End If + End If +' Dim n As Notification +' n.Initialize +' n.Icon = "icon" +' n.SetInfo(Message.GetData.Get("title"), Message.GetData.Get("body"), Main) +' n.Notify(1) +End Sub + +Sub Service_Destroy + +End Sub + +Sub SendMessage(params As Map) +' Dim topic As String= params.Get("topic") +' Dim title As String= params.Get("title") +' Dim body As String= params.Get("body") +' Dim tipo As String= params.Get("t") +' If params.ContainsKey("r") Then +' Dim rutaGpsCmp As String= params.Get("r") +' Else +' Dim rutaGpsCmp As String = "" +' End If +' Dim Job As HttpJob +' Job.Initialize("fcm", Me) +' Dim m As Map = CreateMap("to": $"/topics/${topic}"$) +' Dim data As Map = CreateMap("title":title, "body":body, "d":Starter.devModel.Trim, "t":tipo, "w":Subs.ssid, "b":batt, "r":rutaGpsCmp, "v":Application.VersionName) +' m.Put("data", data) +' Dim jg As JSONGenerator +' jg.Initialize(m) +'' Job.PostString("https://fcm.googleapis.com/fcm/send", jg.ToString) +' Job.PostString("https://fcm.googleapis.com/v1/projects/pusher-4c091/messages:send", jg.ToString) +' +'' POST https://fcm.googleapis.com/v1/projects/pusher-4c091/messages:send +'' "message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.", +' +' Job.GetRequest.SetContentType("application/json;charset=UTF-8") +'' Job.GetRequest.SetHeader("Authorization", "key=" & API_KEY) +' Job.GetRequest.SetHeader("Authorization", "Bearer AIzaSyDS-_5lpLX5IiKYrG-0Et-KCKx1bwlY7R0") +' If Starter.logger Then Log("Mandamos mensaje: " & m) +End Sub + +Sub mandaLocFM(loc As Location) + Dim coords As String = loc.Latitude & ","&loc.Longitude & ","&Subs.formatoFecha(DateTime.Now) + If Starter.logger Then LogColor("Iniciamos mandaLocFM ("&coords&")", Colors.Magenta) + Dim t As String + If locRequest="Activa" Then + If au = 1 Then + t = "au" ' es una actualizacion + Else + t = "u" ' es una peticion + End If +' Dim params As Map = CreateMap("topic":Sprvsr,"title":"ubicacionRecibida", "body":coords, "t":t) +' Log(params) +' SendMessage(params) + locRequest="Enviada" + CallSubDelayed(Tracker,"CreateLocationRequest") + End If +End Sub + +Sub pe_BatteryChanged (Level As Int, Scale As Int, Plugged As Boolean, Intent As Intent) + batt=Level +End Sub + +Sub PE_PhoneStateChanged (State As String, IncomingNumber As String, Intent As Intent) + If Starter.logger Then Log("Phone state: " & State) + If State = "RINGING" Then + If Starter.logger Then Log("The incoming number is: " & IncomingNumber) + Else + End If +End Sub + +'Sub wifiScanned_ScanDone +' Log("//////////////////////////////wifi_conected_result") +' ToastMessageShow("Wifi_ConnectionResult",True) +' If Main.wifi.isWifiConnected Then +' ssid = Main.wifi.WifiSSID +' End If +'End Sub diff --git a/B4A/NotificationService.bas b/B4A/NotificationService.bas new file mode 100644 index 0000000..0570463 --- /dev/null +++ b/B4A/NotificationService.bas @@ -0,0 +1,215 @@ +B4A=true +Group=Default Group +ModulesStructureVersion=1 +Type=Service +Version=11 +@EndOfDesignText@ +#Region Service Attributes + #StartAtBoot: False +#End Region + +'################################################################################################## +'Requiere las liberias: Reflection y NotificationListener} +'################################################################################################## +'Agregar estas lienas al manifiesto: +' +'AddApplicationText( +' +' +' ) +' +'Lo siguiente es para revisar que tenga autorización para interceptar notificaciones, hay que ponerlo en Main o Starter: + +' If Not(CheckNotificationAccess) Then +' Msgbox2Async($"Se necesita acceso a las notificaciones, haga clic en "Aceptar" y en la siguiente pantalla permita el acceso a la aplicación "${Application.LabelName}"."$, "Permisos necesarios", "Aceptar", "Cancelar", "", Null, True) +' Wait For Msgbox_Result (resultado As Int) +' If resultado = DialogResponse.POSITIVE Then +' Dim In As Intent +' In.Initialize("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS", "") +' StartActivity(In) +' End If +' End If +' +''Revisa si la aplicación tiene permiso para acceder a las notificaciones. +'Sub CheckNotificationAccess As Boolean +' Dim ph As Phone +' Dim nstr, pstr As String +' Dim r As Reflector +' pstr = r.GetStaticField("anywheresoftware.b4a.BA", "packageName") +' nstr = ph.GetSettings("enabled_notification_listeners") +' Return nstr.Contains(pstr) +'End Sub +'################################################################################################## + +Sub Process_Globals + 'These global variables will be declared once when the application starts. + 'These variables can be accessed from all modules. + Dim rp As ReplyAuto +' Dim activo As Boolean = True +' Dim ultimaNoti As String + Dim logger As Boolean = False 'ignore +End Sub + +Sub Service_Create + rp.Initialize("NotiMon") + #if not(DEBUG) + logger = False + #end if + Subs.revisaBD +' If logger Then Log("**************** Iniciamos Monitor Keymon ***********************") +End Sub + +Sub Service_Start (StartingIntent As Intent) +' Log("NotificationService Start") + If rp.HandleIntent(StartingIntent) Then Return +' DateTime.DateFormat = "mm" +' ultimaNoti = DateTime.Date(DateTime.now) +' If logger Then LogColor($"Ultima notificación en el minuto ${ultimaNoti}"$, Colors.green) +End Sub + +Sub Service_Destroy + +End Sub + +Sub NotiMon_NotificationPosted (SBN As StatusBarNotification) + Subs.revisaYguardaUUC + If Starter.logger Then LogColor(SBN.PackageName & " - " & SBN.Message, Colors.blue) +' Log("-= Notificacion recibida =-") +' If SBN.PackageName = "com.whatsapp" Then +' If logger Then LogColor(SBN.PackageName, Colors.Red) +' If logger Then LogColor("isGroupWA2: "&isGroupWA(SBN),Colors.Magenta) +' If logger Then LogColor("isPersonWA: "&isPersonWA(SBN),Colors.Magenta) +' If logger Then Log($"getGroupName: |${getGroupName(SBN.Title)}|"$) + +' Si recibimos un mensaje con la palabra "donde", "Donde" o "bien", entonces mandamos la última ubicación conocida. +' If SBN.Message.Contains("donde") Or SBN.Message.Contains("Donde") Or SBN.Message.Contains("bien") Then 'Si el mensaje contiene "#NS" y tiene un segundo parametro ... +' If esMensajeWAValido(SBN) Then +' Subs.bitacora($"Recibimos notificación, pedimos actualización y mandamos UUC."$) + If Starter.logger Then LogColor($"Recibimos notificación, pedimos actualización y mandamos UUC."$, Colors.Green) + If Tracker.flp.IsInitialized And Tracker.flp.IsConnected Then Tracker.flp.RequestLocationUpdates(Tracker.locReqSmall) + Sleep(2000) + Private ultimaLoc As Location = Subs.traeUltimaUbicacionGuardada + Subs.mandaLoc2(ultimaLoc, Starter.devModel) +' End If +' End If +' End If +End Sub + +'Regresa verdadero si el mensaje de whatsapp es un mensaje valido. +Sub esMensajeWAValido(SBN As StatusBarNotification) As Boolean 'ignore + Private valido As Boolean = False + Private ww() As String = Regex.Split("\|", SBN.Key) + If ww(3) <> Null And ww(3) <> "null" Then valido = True + Return valido +End Sub + +'Returns TRUE if the sender is a GROUP. +'Searches the provided sbn.title for the string ": " to know if the sender is a group. +Sub isGroupWA2(sbnTitle As String) As Boolean 'ignore + Private x As Boolean = Regex.ismatch(".*(: ).*", sbnTitle) + Return x +End Sub + +'Returns TRUE if the sender is a GROUP. +'Searches the provided notification object for the string "@g.us" and if found returns TRUE. +Sub isGroupWA(sbn As StatusBarNotification) As Boolean 'ignore + Private a As Boolean = False + If sbn.As(String).IndexOf("@g.us") > -1 Then a = True 'ignore + Return a +End Sub + +'Returns TRUE if the sender is a PERSON. +'Searches the provided notification object for the string "@s.whatsapp.net" and if found returns TRUE. +Sub isPersonWA(sbn As StatusBarNotification) As Boolean 'ignore + Private a As Boolean = False + If sbn.As(String).IndexOf("@s.whatsapp.net") > -1 Then a = True 'ignore + Return a +End Sub + +'Returns TRUE if the sender is a PERSON. +'Searches the provided notification object for the string "channel=individual" and if found returns TRUE. +Sub isPersonWA2(sbn As StatusBarNotification) As Boolean 'ignore + Private a As Boolean = False + If sbn.As(String).IndexOf("channel=individual") > -1 Then a = True 'ignore + Return a +End Sub + +'Returns the sender's number. +'Searches the provided notification object and gets the numbers between "shortcut=" and "@s.whatsapp.net". +Sub getNumberWA(sbn As StatusBarNotification) As String + Private a As Int = sbn.As(String).IndexOf("@s.whatsapp.net") 'ignore + If a > -1 Then + Private x As String = sbn.As(String) 'ignore + Private y As Int = x.IndexOf("shortcut=") + If (y+9) > 0 And a > (y+9) Then x = x.SubString2(y+9, a) Else x = "Not a person" + Else 'It is probably is a group. + x = "Not a person" + End If + Return x +End Sub + +'Returns the name of the group from the given text. +'If it is not a group, then returns the notification's title. +Sub getGroupName(sbnTitle As String) As String 'ignore + Private a As Int = sbnTitle.IndexOf(": ") + Private x As String = sbnTitle + If a > -1 Then + Private b As String = sbnTitle.SubString2(0, a) + x = Regex.Replace(" \(.+\)", b, "") + End If + Return x +End Sub + +'Returns the name of the group from the given notification object. +'Searches the provided notification for the string "hiddenConversationTitle" and if found, gets the name. +'If it is not a group,then it returns the notification's title. +Sub getGroupName2(sbn As StatusBarNotification) As String 'ignore + Private inicio As Int = sbn.Extras.As(String).IndexOf("hiddenConversationTitle=") 'ignore + If inicio > -1 And sbn.Extras.As(String).IndexOf("hiddenConversationTitle=null") = -1 Then 'ignore + Private x As String = sbn.Extras.As(String) 'ignore + Private fin As Int = x.IndexOf(", android.reduced.images=") + x = x.SubString2(inicio+24, fin) + x = Regex.Replace(" \(.+\)", x, "") 'Replace anything between () with "", this en the case that we have something like "MyGroupName (5 messages)" + Else 'Is not from a group. + Private x As String = sbn.Title + End If + Return x +End Sub + +'Returns the person's name (or the number) when the message comes from a group. +'Searches the provided sbn.title for the string ": " in the title and returns the string after that, +'if it does not find ": " then returns the complete string. +Sub getPersonFromGroup(sbnTitle As String) As String 'ignore + Private a As Int = sbnTitle.IndexOf(": ") + If a = -1 Then a = -2 'Is not from a group. + Private b As String = sbnTitle.SubString(a+2) + Return b +End Sub + +'Returns the NUMBER of the sender and if NOT a person, then returns the name of the group. +Sub getNumberOrGroupWA(sbn As StatusBarNotification) As String 'ignore + Private numRemitente As String = getNumberWA(sbn) + If numRemitente = "Not a person" Then numRemitente = getGroupName(sbn.Title) + Return numRemitente +End Sub + +'Regresa el "shortcut" del remitente. +'Si es de un grupo, es algo como "120363023512345678@g.us" +'Si es de una persona, entonces "5215512345678@s.whatsapp.net" +Sub getShortcut(sbn As StatusBarNotification) As String 'ignore + Private ap As Int = sbn.As(String).IndexOf("@s.whatsapp.net") 'ignore + Private ag As Int = sbn.As(String).IndexOf("@g.us") 'ignore + Private x As String = sbn.As(String) 'ignore + Private y As Int = x.IndexOf("shortcut=") + If ap > -1 Then + Private x As String = sbn.As(String) 'ignore + Private y As Int = x.IndexOf("shortcut=") + x = x.SubString2(y+9, ap+15) + Else if ag > -1 Then 'It is probably is a group. + Private x As String = sbn.As(String) 'ignore + Private y As Int = x.IndexOf("shortcut=") + x = x.SubString2(y+9, ag+5) + End If + Return x +End Sub \ No newline at end of file diff --git a/B4A/R_Test.bas b/B4A/R_Test.bas new file mode 100644 index 0000000..4b9bbdb --- /dev/null +++ b/B4A/R_Test.bas @@ -0,0 +1,18 @@ +B4A=true +Group=Default Group +ModulesStructureVersion=1 +Type=Receiver +Version=12.5 +@EndOfDesignText@ +Sub Process_Globals + +End Sub + +'Called when an intent is received. +'Do not assume that anything else, including the starter service, has run before this method. +Private Sub Receiver_Receive (FirstTime As Boolean, StartingIntent As Intent) + Log("RECEIVER RECEIVED - BOOT COMPLETED") + Subs.revisaBD + Subs.bitacora("RECEIVER BOOT COMPLETED") +End Sub + diff --git a/B4A/Starter.bas b/B4A/Starter.bas new file mode 100644 index 0000000..f4e4195 --- /dev/null +++ b/B4A/Starter.bas @@ -0,0 +1,269 @@ +B4A=true +Group=Default Group +ModulesStructureVersion=1 +Type=Service +Version=9.9 +@EndOfDesignText@ +#Region Service Attributes + #StartAtBoot: False + #ExcludeFromLibrary: True +#End Region + +Sub Process_Globals + 'These global variables will be declared once when the application starts. + 'These variables can be accessed from all modules. + Public rp As RuntimePermissions 'Para obtener permisos android 6+ + Public FLP As FusedLocationProvider 'Para Tracker + Dim reqManager As DBRequestManager + Dim cmd As DBCommand + Dim Timer1, Timer2 As Timer + Dim interval As Int = 300 '300 segs (5 mins) + Dim UUC As Location + Dim run As Int = 0 'ignore + Dim devModel As String + Dim lastLocUpdate As String = 0 + Dim errorLog As SQL + Dim PE As PhoneEvents + Dim PhId As PhoneId + + Dim callStartTime, callEndTime As Long + Dim isIncoming As Boolean + Dim lastState As String = "IDLE" + Dim savedNumber As String + Dim locAntTime As String = "0" + + Dim logger As Boolean = True + Dim BroadCast As BroadCastReceiver + Dim T2Interval As Int = 600000 + Dim pausarEnvio As Boolean = False + Dim IsNetAvailable As Boolean +End Sub + +Sub Service_Create + 'This is the program entry point. + 'This is a good place to load resources that are not specific to a single activity. + Subs.revisaBD + CallSubDelayed(FirebaseMessaging, "SubscribeToTopics") 'Para FirebaseMessaging + lastState = "IDLE" + PE.InitializeWithPhoneState("PE",PhId) +End Sub + +Sub Service_Start (StartingIntent As Intent) + Service.StopAutomaticForeground 'Starter service can start in the foreground state in some edge cases. +' reqManager.Initialize(Me, "http://keymon.lat:1781") + reqManager.Initialize(Me, "http://keymon.lat:9000") + Timer1.Initialize("Timer1", interval * 1000) + Timer2.Initialize("Timer2", T2Interval) ' Timer para que si hay un error al conectarse con el servidor de DBRequest, no mande nada en 120 segs. + Timer1.Enabled = True + UUC.Initialize + UUC.Accuracy = 1000 + Subs.getPhnId + Subs.bitacora($"Starter - Service_Start"$) + StartService(Tracker) + StartServiceAt(Tracker, DateTime.Now + 30 * DateTime.TicksPerMinute, True) + #if RELEASE + logger = False + #end if + Subs.revisaYguardaUUC + BroadCast.Initialize("BroadcastReceiver") ' Inicializa el receptor de difusión con el nombre "BroadcastReceiver" + BroadCast.addAction("android.net.conn.CONNECTIVITY_CHANGE") ' Agrega una acción para escuchar los cambios en la conectividad de red + BroadCast.SetPriority(100) ' Establece la prioridad del receptor de difusión (BroadCast) a 100 + BroadCast.registerReceiver("") ' Registra el receptor de difusión sin un filtro específico +End Sub + +Sub BroadcastReceiver_OnReceive (Action As String, i As Object) + ' Declaración de una variable Intent para manejar el objeto recibido + Dim retIn As Intent + ' Declaración de una variable booleana para verificar si hay red disponible + ' Asignación del objeto recibido al Intent + retIn = i + ' Declaración de variables para almacenar el estado de la conexión y el tipo de red + Dim IsConnected As String + Dim TheType As String + ' Verifica si la acción recibida es un cambio de conectividad + If Action = "android.net.conn.CONNECTIVITY_CHANGE" Then + ' Crea un objeto JavaObject basado en el intent recibido para acceder a métodos específicos + Dim jo As JavaObject = retIn + ' Obtener información de red desde el Intent + Dim NetworkInfo As JavaObject = jo.RunMethod("getParcelableExtra", Array("networkInfo")) + ' Obtener el tipo de red (WiFi, Móvil, etc.) + TheType = NetworkInfo.RunMethod("getTypeName", Null) + ' Obtener el estado de la conexión (Conectado, Desconectado, etc.) + IsConnected = NetworkInfo.RunMethod("getState", Null) + ' Loguear y mostrar un mensaje Toast con el tipo y estado de la conexión +' Log($"TheType: ${TheType}, IsConnected: ${IsConnected}"$) +' ToastMessageShow($"TheType: ${TheType}, IsConnected: ${IsConnected}"$, False) + ' Verifica si el dispositivo está conectado + If IsConnected.EqualsIgnoreCase("CONNECTED") Then + ' Si está conectado, establece que la red está disponible + IsNetAvailable = True + Else + ' Si no está conectado, establece que la red no está disponible + IsNetAvailable = False + End If +' Log(IsNetAvailable) + If IsNetAvailable Then + Log(">>>>> MANDAMOS INFO A SERVER") + Subs.mandaLocAServer(devModel.Trim) + End If + End If + ' Finaliza la transmisión actual + BroadCast.AbortBroadcast +End Sub + +Private Sub Timer1_Tick +' Log("TIMER1: " & Timer1) + Subs.borraArribaDeXXXBitacora(3000) + Subs.borraArribaDeXXXUbicaciones(5000) +End Sub + +Private Sub Timer2_Tick +' Log("TIMER2: " & Timer1) + LogColor(">>>>>>>> PAUSAR ENVIO FALSE", Colors.Red) + pausarEnvio = False + Timer2.Enabled = False +End Sub + +Sub Service_TaskRemoved + 'This event will be raised when the user removes the app from the recent apps list. +End Sub + +'Return true to allow the OS default exceptions handler to handle the uncaught exception. +Sub Application_Error (Error As Exception, StackTrace As String) As Boolean + Subs.bitacora($"ERROR -> ${Error}"$) + Return True +End Sub + +Sub Service_Destroy + Subs.bitacora($"Starter - Service destroyed"$) +' Subs.gps_hist.ExecNonQuery($"insert into BITACORA (RUTA, TEXTO, FECHA) values ('${devModel}', 'Starter - Service destroyed')"$) +End Sub + +Sub restartTracker + If logger Then Log("Llamamos RESTART-TRACKER") + Subs.bitacora("Llamamos RESTART-TRACKER") + StopService(Tracker) + Sleep(1000) + StartService(Tracker) +End Sub + +Sub PE_PhoneStateChanged (State As String, IncomingNumber As String, Intent As Intent) + 'Incoming call- goes from IDLE To RINGING when it rings, To OFFHOOK when it's answered, to IDLE when its hung up + 'Outgoing call- goes from IDLE To OFFHOOK when it dials out, To IDLE when hung up + Log($">>>>> Phone State Changed -> ${State} -> "$ & IncomingNumber) + If IncomingNumber = "" Then + Return + End If + Log($">>>>> Phone State Changed 2 -> ${State} -> "$ & IncomingNumber) + Select State + Case "RINGING" + 'calling in progress + isIncoming = True + callStartTime = DateTime.Now + savedNumber = IncomingNumber + Subs.bitacora($"RINGING - ${IncomingNumber}"$) + Exit + Case "OFFHOOK" + 'Transition of ringing->offhook are pickups of incoming calls. Nothing donw on them + If lastState <> "RINGING" Then + 'outgoing call start + isIncoming = False + callStartTime = DateTime.Now + Subs.bitacora($"OFFHOOK - ${savedNumber}"$) + End If + Exit + Case "IDLE" + 'Went to idle- this is the end of a call. What type depends on previous state(s) + If lastState = "RINGING" Then + 'missed call + callEndTime = 0 + Subs.bitacora($"IDLE - Missed call (${IncomingNumber}): ${callEndTime}"$) + else if isIncoming Then + 'incoming call is finished + callEndTime = DateTime.Now + Subs.bitacora($"IDLE - Incoming call is finished (${IncomingNumber}): ${callEndTime}"$) + Else + 'outgoing call is finished + callEndTime = DateTime.Now + Subs.bitacora($"IDLE - Outgoing call is finished (${IncomingNumber}): ${callEndTime}"$) + End If + Exit + End Select + lastState = State + + 'Log("PhoneStateChanged, State = " & State & ", IncomingNumber = " & IncomingNumber & "; PhoneFlag = " & PhoneFlag) +End Sub + +Sub JobDone(Job As HttpJob) + Log("JOBDONE STARTER") + Try + If Job.JobName = "DBRequest" Then + Dim RESULT As DBResult = reqManager.HandleJob(Job) + Log($"Tag: ${RESULT.tag}, success=${Job.Success}"$) + End If + 'Log(Job.Tag) + If Job.Success = False Then +' Log("JOBDONE ERROR") + LogColor("Error: " & Job.ErrorMessage, Colors.red) + If Job.ErrorMessage.Contains("java.net.SocketTimeoutException") Then ' Si hubo error, pausamos los siguientes envios por 120 segs (pausarEnvio = True). + Timer2_Tick + Timer2.Enabled = False + Timer2.Interval = T2Interval + Timer2.Enabled = True + LogColor(">>>>>> PAUSAR ENVIO TRUE REINICIO", Colors.Red) + End If +' If Job.ErrorMessage.Contains("ORA-00001") Then ' Si hubo error, pausamos los siguientes envios por 120 segs (pausarEnvio = True). +' pausarEnvio = False +' If Job.JobName = "DBRequest" Then +' Dim RESULT As DBResult = reqManager.HandleJob(Job) +' If RESULT.Tag.As(String).StartsWith("guardaDatos_") Then 'query tag +' LogColor(">>>>>> YA EXISTE EN WEB", Colors.Red) +' Private id() As String = Regex.Split("_", RESULT.tag) +' Private id1 As String = id(1) +' For Each records() As Object In RESULT.Rows +' Subs.gps_hist.ExecNonQuery($"update RUTA_GPS set ENVIADO = 2 where FECHA = '${id1}'"$) +' Next +' LogColor($"Actualizamos ${id1}"$, Colors.red) +' End If +' End If +' End If + Else 'If Job Success then ... +' Log("JOBDONE SUCCESS") + Timer2_Tick + If Job.JobName = "DBRequest" Then + Dim RESULT As DBResult = reqManager.HandleJob(Job) + + ' Si ya no existe la ubicacion en web, la insertamos nuevamente. + If RESULT.Tag.As(String).StartsWith("borraDatos_") Then 'query tag + Private id() As String = Regex.Split("_", RESULT.tag) + Private id1 As String = id(1) + For Each records() As Object In RESULT.Rows + ' Traemos la ubicacion de RUTA_GPS y la mandamos a web. + Private g As ResultSet = Subs.gps_hist.ExecQuery($"select * from RUTA_GPS where FECHA = '${id1}'"$) + cmd.Initialize + Do While g.NextRow + cmd.Name = "guardaDatos" + cmd.Parameters = Array As Object(g.GetString("FECHA"), devModel, g.GetString("FECHA").SubString2(0,12), $"${g.GetString("LAT")},${g.GetString("LON")},${g.GetString("ACC")},True,${g.GetString("SPEED")},True"$, "Coords") + reqManager.ExecuteCommand(cmd,$"guardaDatos_${id1}"$) + If logger Then LogColor($">>>> Mandamos loc a server: ${g.GetString("FECHA")}, ${g.GetString("LAT")},${g.GetString("LON")},${g.GetString("ACC")},True,${g.GetString("SPEED")},True"$, Colors.Blue) + Loop + g.Close + Next + LogColor($"Actualizamos ${id1}"$, Colors.red) + End If + + ' Si se guardo bien la ubicacion en web, actualizamos la columna ENVIADO en RUTA_GPS. + If RESULT.Tag.As(String).StartsWith("guardaDatos_") Then 'query tag + Private id() As String = Regex.Split("_", RESULT.tag) + Private id1 As String = id(1) + For Each records() As Object In RESULT.Rows + Subs.gps_hist.ExecNonQuery($"update RUTA_GPS set ENVIADO = 1 where FECHA = '${id1}'"$) + Next + LogColor($"Actualizamos ${id1}"$, Colors.red) + End If + End If + End If + Catch + Log(LastException) + End Try +End Sub \ No newline at end of file diff --git a/B4A/Subs.bas b/B4A/Subs.bas new file mode 100644 index 0000000..3acf54b --- /dev/null +++ b/B4A/Subs.bas @@ -0,0 +1,453 @@ +B4A=true +Group=Default Group +ModulesStructureVersion=1 +Type=StaticCode +Version=11 +@EndOfDesignText@ +'Code module +'Subs in this code module will be accessible from all modules. +Sub Process_Globals + 'These global variables will be declared once when the application starts. + 'These variables can be accessed from all modules. + Public GZip As GZipStrings + Private su As StringUtils + Dim phn As Phone + Dim devModel As String + Dim gps_hist As SQL + Dim wifi As MLwifi + Dim ssid As String + Dim locAntTime As String = "0" + Dim solicitudFM As Int = 0 +End Sub + +Sub getPhnId 'Pone el valor de phn.Model en la variable global "devModel" + Private elId As String + If File.Exists(File.DirInternal, "phnId.txt") Then + elId = File.ReadString(File.DirInternal, "phnId.txt") + Else + File.WriteString(File.DirInternal, "phnId.txt", "") 'Creamos el archivo + End If + If elId.Length < 3 Then 'Si el modelo del archivo es menos de 2, lo sustituimos con devmodel + devModel = phn.Model + If devModel.Length > 1 Then elId = devModel + End If + If elId.Length < 3 Then 'Si el modelo del archivo es menos de 2, lo sustituimos con android_id + elId = phn.GetSettings("android_id") 'Intentamos con "android_id" + End If + If elId.Length < 3 Then elId = $"dev${DateTime.GetHour(DateTime.Now)}"$ + File.WriteString(File.DirInternal, "phnId.txt", elId) 'Sobreescribimos archivo phnId.txt with elId +' If Starter.logger Then Log("Escribimos phnId: "&elId&" a "&File.DirInternal&"/phnId.txt") + Starter.devModel = elId +' If Starter.logger Then Log(Starter.devModel) +End Sub + +Sub compress(str As String) As String ' Compresion + Dim compressed() As Byte = GZip.compress(str) + If Starter.logger Then Log($"CompressedBytesLength: ${compressed.Length}"$) + Dim base64 As String = su.EncodeBase64(compressed) + If Starter.logger Then Log($"CompressedBytes converted to base64 Length: ${base64.Length}"$) + If Starter.logger Then Log($"CompressedBytes converted to base64: ${base64}"$) + Return base64 +End Sub + +Sub decompress(base64 As String) As String ' Descompresion 'ignore + Dim decompressedbytes() As Byte = su.DecodeBase64(base64) + If Starter.logger Then Log($"decompressedbytesLength: ${decompressedbytes.Length}"$) + Dim bc As ByteConverter + Dim uncompressed As String = bc.StringFromBytes(decompressedbytes,"UTF8") + If Starter.logger Then Log($"uncompressedLength: ${uncompressed.Length}"$) + If Starter.logger Then Log($"Decompressed String = ${uncompressed}"$) + Return uncompressed +End Sub + +Sub formatoFecha(fecha As String) As String 'Convierte una fecha al formato yyMMddHHmmss +' Log(fecha) + Dim OrigFormat As String = DateTime.DateFormat 'save orig date format + DateTime.DateFormat="yyMMddHHmmss" + Dim lastUpdate As String=DateTime.Date(fecha) + DateTime.DateFormat=OrigFormat 'return to orig date format +' Log(lastUpdate) + Return lastUpdate +End Sub + +Sub CreateNotification (Body As String) As Notification + Dim notification As Notification + notification.Initialize2(notification.IMPORTANCE_LOW) + notification.Icon = "icon" + notification.SetInfo("FLP", Body, Main) + Return notification +End Sub + +Sub guardaInfoEnArchivo(coords As String) 'Escribimos coordenadas y fecha a un archivo de texto 'ignore + ' Cambiamos el formato de la hora + Dim OrigFormat As String=DateTime.DateFormat 'save orig date format + DateTime.DateFormat="MMM-dd HH:mm:ss" + Dim lastUpdate As String=DateTime.Date(DateTime.Now) + DateTime.DateFormat=OrigFormat 'return to orig date format + + Dim ubic As String = coords&","&lastUpdate + Dim out As OutputStream = File.OpenOutput(File.DirRootExternal, "gps.txt", True) + Dim s As String = ubic & CRLF + Dim t() As Byte = s.GetBytes("UTF-8") + out.WriteBytes(t, 0, t.Length) + out.Close +End Sub + +'Guarda la ubicacion dada en la tabla "RUTA_GPS". +Sub guardaInfoEnBD(loc As Location) 'Escribimos coordenadas y fecha a una BD + If loc.Latitude <> "0.0" Then + revisaBD + Private bearingDiff As String = "31" + Private distanceDiff As String = "21" + Private tmpLoc As Location + If Starter.logger Then Log($"Guardamos ubicacion en db (${loc.Latitude},${loc.Longitude})"$) + 'Traemos la ultima ubicacion guardada. + Private locAnt As ResultSet = gps_hist.ExecQuery($"select FECHA, LAT, LON, ACC, TIME, ifnull(BEARING, 0) as BEARING, ifnull(BEARING_DIFF, 0) as BEARING_DIFF, ifnull(DISTANCE_DIFF, 0) as DISTANCE_DIFF, ifnull(SPEED, 0) as SPEED, ifnull(ENVIADO, 0) as ENVIADO from RUTA_GPS order by time desc limit 1"$) + Do While locAnt.NextRow + 'Calculamos el cambio de direccion. + bearingDiff = loc.Bearing - locAnt.GetString("BEARING") + If locAnt.GetString("BEARING") > loc.Bearing Then bearingDiff = locAnt.GetString("BEARING") - loc.Bearing + 'Calculamos la distancia recorrida. + tmpLoc.Initialize2(locAnt.GetString("LAT"), locAnt.GetString("LON")) + distanceDiff = loc.DistanceTo(tmpLoc) +' Log($"${loc.Accuracy}, ${distanceDiff}, ${bearingDiff}"$) + Loop + ' Si la precision es menor a 50 y la dirección cambió o la distancia es mayor a 20 mts, la guardamos. + If loc.Accuracy < 50 And (bearingDiff > 30 Or distanceDiff > 20) Then + gps_hist.ExecNonQuery2("INSERT INTO RUTA_GPS(fecha, lat, lon, acc, time, bearing, bearing_diff, distance_diff, speed) VALUES (?,?,?,?,?,?,?,?,?)", Array As Object (fechaKMTms(loc.time),loc.Latitude,loc.Longitude, loc.Accuracy, loc.time, loc.Bearing, bearingDiff, distanceDiff, loc.Speed)) + End If + End If +End Sub + +Sub dameRuta As String + Dim c As Cursor + If gps_hist.IsInitialized = False Then gps_hist.Initialize(File.DirInternal, "gps_hist.db", True) + c = gps_hist.ExecQuery("select FECHA, LAT, LON from RUTA_GPS order by FECHA desc limit 380") + c.Position = 0 + Dim ruta2 As String = "" + If c.RowCount>0 Then + For i=0 To c.RowCount -1 + c.Position=i + ruta2=ruta2&CRLF&c.GetString("LAT")&","&c.GetString("LON") + B4XPages.MainPage.fechaRuta = c.GetString("FECHA") + Next + End If + c.Close + Return compress(ruta2) +End Sub + +Sub deleteGPS_DB +' gps_hist.ExecQuery + gps_hist.ExecNonQuery("delete from RUTA_GPS") + gps_hist.ExecNonQuery("vacuum;") + If Starter.logger Then Log("RUTA_GPS borrada") +End Sub + +Sub borramosArchivoGPS + Dim out As OutputStream = File.OpenOutput(File.DirRootExternal, "gps.txt", False) + Dim s As String = "" + Dim t() As Byte = s.GetBytes("UTF-8") + out.WriteBytes(t, 0, t.Length) + out.Close +End Sub + +Sub revisaBD + If Not(File.Exists(File.DirInternal, "gps_hist.db")) Then File.Copy(File.DirAssets, "gps_hist.db", File.DirInternal, "gps_hist.db") + If Not(gps_hist.IsInitialized) Then gps_hist.Initialize(File.DirInternal, "gps_hist.db", True) + If Not(Starter.errorLog.IsInitialized) Then Starter.errorLog.Initialize(File.DirInternal, "errorLog.db", True) + gps_hist.ExecNonQuery("CREATE TABLE IF NOT EXISTS BITACORA(RUTA TEXT, TEXTO TEXT, FECHA TEXT)") + gps_hist.ExecNonQuery("CREATE TABLE IF NOT EXISTS RUTA_GPS(FECHA INTEGER, LAT TEXT, LON TEXT, ACC INT, TIME INT)") + gps_hist.ExecNonQuery("CREATE TABLE IF NOT EXISTS CAT_VARIABLES(NOMBRE TEXT, VALOR TEXT)") + agregaColumna("RUTA_GPS", "BEARING", "TEXT") + agregaColumna("RUTA_GPS", "BEARING_DIFF", "TEXT") + agregaColumna("RUTA_GPS", "DISTANCE_DIFF", "TEXT") + agregaColumna("RUTA_GPS", "SPEED", "TEXT") + agregaColumna("RUTA_GPS", "ENVIADO", "TEXT") +End Sub + +Sub getSSID + If wifi.isWifiConnected Then + ssid = wifi.WifiSSID + End If +End Sub + +'Convierte una fecha al formato yyMMddHHmmss +Sub fechaKMT(fecha As String) As String 'ignore +' Log(fecha) + Dim OrigFormat As String = DateTime.DateFormat 'save orig date format + DateTime.DateFormat="yyMMddHHmmss" + Dim nuevaFecha As String=DateTime.Date(fecha) + DateTime.DateFormat=OrigFormat 'return to orig date format +' Log(nuevaFecha) + Return nuevaFecha +End Sub + +'Convierte una fecha al formato yyMMddHHmmssSSS -- con milisegundos +Sub fechaKMTms(fecha As String) As String 'ignore +' Log(fecha) + Dim OrigFormat As String = DateTime.DateFormat 'save orig date format + DateTime.DateFormat="yyMMddHHmmssSSS" + Dim nuevaFecha As String=DateTime.Date(fecha) + DateTime.DateFormat=OrigFormat 'return to orig date format +' Log(nuevaFecha) + Return nuevaFecha +End Sub + +'Convierte una fecha en formato YYMMDDHHMMSS a Ticks +Sub fechaKMT2Ticks(fKMT As String) As Long 'ignore + Try + If fKMT.Length = 12 Then + Private parteFecha As String = fKMT.SubString2(0,6) + Private parteHora As String = fKMT.SubString(6) + Private OrigFormat As String = DateTime.DateFormat 'save original date format + DateTime.DateFormat="yymmdd" + DateTime.TimeFormat="HHmmss" + Private ticks As Long = DateTime.DateTimeParse(parteFecha,parteHora) + DateTime.DateFormat=OrigFormat 'return to original date format + Return ticks + Else + If Starter.logger Then Log("Formato de fecha incorrecto, debe de ser 'YYMMDDHHMMSS', no '"&fKMT&"' largo="&fKMT.Length) + Return 0 + End If + Catch + Log("FLP-fechaKMT2Ticks Error -> " & LastException) + If Starter.logger Then LogColor($"Fecha dada: ${fKMT}, Parte Fecha: ${parteFecha}, Parte Hora: ${parteHora}"$, Colors.Red) + Return 0 + End Try +End Sub + +'Convierte una fecha al formato yyMMddHHmmss +Sub fechaNormal(fecha As String) As String 'ignore +' Log(fecha) + Dim OrigFormat As String = DateTime.DateFormat 'save orig date format + DateTime.DateFormat="MM/dd HH:mm:ss" + Dim nuevaFecha As String = DateTime.Date(fecha) + DateTime.DateFormat=OrigFormat 'return to orig date format + Return nuevaFecha +End Sub + +Sub mandaLocAServer( id As String) + Starter.lastLocUpdate = DateTime.Now + If Not(IsPaused(Main)) Then CallSubDelayed(Main, "actualizaLabelUU") + Try + If Starter.IsNetAvailable Then + ' Traemos las ubicaciones de RUTA_GPS que no estan ENVIADAS. + Private g0 As ResultSet = gps_hist.ExecQuery($"select * from RUTA_GPS where ifNull(ENVIADO,'0') = '0'"$) + Do While g0.NextRow + Starter.cmd.Initialize + Starter.cmd.Name = "borraDatos" + Starter.cmd.Parameters = Array As Object(g0.GetString("FECHA"), id) + If Starter.logger Then LogColor($">>>> Borramos loc de server: ${g0.GetString("FECHA")}, ${id}"$, Colors.Blue) + ' Borramos de web la ubicacion, para despues en JobDone volverla a insertar, esto es porque si ya existe en web, causa un error por indices unicos y no se actualiza nada! + Starter.reqManager.ExecuteCommand(Starter.cmd,$"borraDatos_${g0.GetString("FECHA")}"$) + Loop + g0.close + End If +' Sleep(10000) +' Log($"PAUSAR_ENVIO: ${Starter.pausarEnvio}, IS_NET_AVAILABLE: ${Starter.IsNetAvailable}"$) +' If Not(Starter.pausarEnvio) And Starter.IsNetAvailable Then +' Private g As ResultSet = gps_hist.ExecQuery($"select * from RUTA_GPS where ifNull(ENVIADO,'0') = '0'"$) +' Do While g.NextRow +' Starter.pausarEnvio = True +' Starter.Timer2.Enabled = False +' Starter.Timer2.Interval = Starter.T2Interval +' Starter.Timer2.Enabled = True +' LogColor("PAUSAR ENVIO TRUE", Colors.Red) +' Starter.cmd.Initialize +' Starter.cmd.Name = "guardaDatos" +'' Log($"Time: ${g.GetString("TIME")}, Acc: ${g.GetString("ACC")}, Enviado: ${g.GetString("ENVIADO")}"$) +' Starter.cmd.Parameters = Array As Object(g.GetString("FECHA"), id, g.GetString("FECHA"), $"${g.GetString("LAT")},${g.GetString("LON")},${g.GetString("ACC")},True,${g.GetString("SPEED")},True"$, "Coords") +' If Starter.logger Then +' If Starter.logger Then LogColor($">>>> Mandamos loc a server: ${g.GetString("LAT")},${g.GetString("LON")},${g.GetString("ACC")},True,${g.GetString("SPEED")},True"$, Colors.Blue) +' End If +' Starter.reqManager.ExecuteCommand(Starter.cmd,$"guardaDatos_${g.GetString("FECHA")}"$) +' Loop +' g.close +' End If + Catch + Log(LastException) + End Try +End Sub + +'Manda la ubicacion al servidor de BD y a FirebaseMessage. +Sub mandaLoc2(loc As Location, id As String) 'ignore + Try + Private minsTranscurridosLoc As String = ticksAMins(loc.time - traeVar("locAntTime", 0)) 'Minutos transcurridos desde la ultima ubicacion ACTUALIZADA. + Private minsTranscurridos As String = ticksAMins(DateTime.Now - traeUltimaUbicacionGuardada.Time) 'Minutos transcurridos desde la ultima ubicacion guardada. + Dim el_texto As String = "" + If Starter.logger Then LogColor("Guardada: " & minsTranscurridos & " mins., Actualizada: " & minsTranscurridosLoc & " mins.", Colors.green) +' If traeVar("solicitudFM", 0) = 1 Or minsTranscurridos >= 3 Or minsTranscurridosLoc >= 3 Then ' Para que no mande mensajes constantes, minimo 1 minuto entre mensajes. +' CallSubDelayed(Tracker,"CreateLocationRequest") + Tracker.flp.RequestLocationUpdates(Tracker.locReqNormal) + 'Solo mandamos la ubicacion si la precision es dentro de XX mts + el_texto = $"LocChange - Coords NO enviadas (Acc:${loc.Accuracy})."$ + If loc.Accuracy < 50 Then + If Starter.logger Then LogColor("Guardamos y enviamos ubicacion.", Colors.green) + guardaInfoEnBD(loc) 'Escribimos coordenadas y fecha a una bd + mandaLocAServer(id) +' FirebaseMessaging.locRequest = "Activa" +' CallSubDelayed2(FirebaseMessaging, "mandaLocFM", loc) + el_texto = $"LocChange - Coords enviadas (Acc:${loc.Accuracy})."$ + End If + bitacora($"${el_texto}"$) + actualizaVar("solicitudFM", 0) +' ToastMessageShow("LocChanged MORE than a min.", False) +' Else + '' ToastMessageShow("Locatin changed but less than a min!.", False) +' End If + actualizaVar("locAntTime", loc.time) + Catch + CreateNotification(LastException) + Log(LastException) + End Try +End Sub + +Sub ConvertMillisecondsToString(t As Long) As String 'ignore + Dim hours, minutes, seconds As Int + hours = t / DateTime.TicksPerHour + minutes = (t Mod DateTime.TicksPerHour) / DateTime.TicksPerMinute + seconds = (t Mod DateTime.TicksPerMinute) / DateTime.TicksPerSecond + Return $"$1.0{hours}:$2.0{minutes}:$2.0{seconds}"$ +End Sub + +'Convierte ticks a minutos. +Sub ticksAMins(ts As Long) As Long 'ignore + Private m As Long = ((ts/1000)/60) + Return m +End Sub + +Sub bitacora(texto As String) 'ignore + revisaBD +' Log(fechaNormal(DateTime.now)) + Try + gps_hist.ExecNonQuery($"insert into BITACORA (RUTA, TEXTO, FECHA) values ('${Starter.devModel}', '${texto}', '${fechaNormal(DateTime.now)}')"$) + Catch + Log(LastException) + End Try +End Sub + +'Borramos renglones extra de la tabla de errores +Sub borraArribaDeXXXBitacora(limite As Int) 'ignore + If Starter.logger Then LogColor("Recortamos la tabla de bitacora, limite de 10,000", Colors.Magenta) + gps_hist.ExecNonQuery($"DELETE FROM bitacora WHERE fecha NOT in (SELECT fecha FROM bitacora ORDER BY fecha desc LIMIT ${limite})"$) + gps_hist.ExecNonQuery("vacuum;") +' if starter.logger then Log("Borramos mas de 100 de errorLog") +End Sub + +'Borramos renglones extra de la tabla RUTA_GPS +Sub borraArribaDeXXXUbicaciones(limite As Int) 'ignore + If Starter.logger Then LogColor("Recortamos la tabla RUTA_GPS, limite de 10,000", Colors.Magenta) + gps_hist.ExecNonQuery($"DELETE FROM RUTA_GPS WHERE fecha NOT in (SELECT fecha FROM RUTA_GPS ORDER BY fecha desc LIMIT ${limite})"$) + gps_hist.ExecNonQuery("vacuum;") +' if starter.logger then Log("Borramos mas de 100 de errorLog") +End Sub + +'Regresa la ultima ubicacion guardada o una ubicacion sin inicializar si no encuentra nada. +Sub traeUltimaUbicacionGuardada As Location 'ignore + Private loc As Location + loc.Initialize + Private c As Cursor = gps_hist.ExecQuery($"select FECHA, LAT, LON, ACC, TIME, ifnull(BEARING, 0) as BEARING, ifnull(BEARING_DIFF, 0) as BEARING_DIFF, ifnull(DISTANCE_DIFF, 0) as DISTANCE_DIFF, ifnull(SPEED, 0) as SPEED, ifnull(ENVIADO, 0) as ENVIADO from RUTA_GPS order by fecha desc limit 1"$) + If c.RowCount > 0 Then + c.Position = 0 + loc.Latitude = c.GetString("LAT") + loc.Longitude = c.GetString("LON") + loc.Bearing = c.GetString("BEARING") + loc.Speed = c.GetString("SPEED") + loc.Accuracy = 0 + If c.GetString("ACC") <> Null Then loc.Accuracy = c.GetString("ACC") + loc.Time = 0 + If c.GetString("TIME") <> Null Then loc.Time = c.GetString("TIME") + End If + c.Close + Return loc +End Sub + +'Busca la ultima ubicacion guardada en la table GPS_HIST, y dependiendo del tiempo transcurrido hace lo siguiente: +' - Mas de 10 minutos -> Pide actualización de ubicacion. +' - Mas de 20 minutos -> Apaga y prende el servicio de FLP. +' - Mas de 30 minutos -> Reinicia la aplicación. +Sub revisaYguardaUUC + Private ultimaLoc As Location = traeUltimaUbicacionGuardada + If ultimaLoc.IsInitialized Then + Starter.UUC = ultimaLoc + Private minsTranscurridos As String = ticksAMins(DateTime.Now - ultimaLoc.Time) 'Minutos transcurridos desde la ultima ubicacion guardada. + If Starter.logger Then Log($"Ultima ubicacion guardada hace ${minsTranscurridos} mins."$) + If minsTranscurridos > 10 And minsTranscurridos < 20 Then + bitacora("Mas de 10 mins - REQ-UPDATE") + LogColor($"Ubicacion vieja (mas de 10 mins.)"$, Colors.Red) + LogColor("Pedimos actualizacion!", Colors.blue) + If Tracker.flp.IsInitialized And Tracker.flp.IsConnected And Tracker.locReqSmall.IsInitialized Then + bitacora("REQ-UPDATE") : LogColor("REQ-UPDATE", Colors.magenta) + Try + Tracker.flp.RequestLocationUpdates(Tracker.locReqSmall) + Catch + Log(LastException) + End Try + Else + bitacora("TRACKER APAGADO - RESTART-TRACKER") : LogColor("RESTART-TRACKER", Colors.magenta) + StopService(Tracker) + Sleep(5000) + StartService(Tracker) + End If + else if minsTranscurridos >= 20 And minsTranscurridos < 30 Then + LogColor("RESTART-TRACKER", Colors.red) + bitacora("Mas de 20 mins - RESTART-TRACKER") +' bitacora(Tracker.flp.SuspendedCause) + StopService(Tracker) + Sleep(5000) + StartService(Tracker) + else If minsTranscurridos >= 30 Then + If Tracker.flp.IsInitialized And Tracker.flp.IsConnected Then LogColor($"FLP.Connected: ${Tracker.flp.IsConnected}"$, Colors.Red) + If Tracker.flp.IsInitialized And Not(Tracker.flp.IsConnecting) Then 'Si NO esta en proceso de conectarse ... + LogColor("RESTART-APP", Colors.red) + bitacora("Mas de 30 mins - RESTART-APP") +' bitacora(Tracker.flp.SuspendedCause) 'ignore + Starter.UUC.Time = DateTime.Now + guardaInfoEnBD(Starter.UUC) 'Guardamos la ultima ubicacion con la hora actual, para que no se reinicie la app si no consigue una ubicacion nueva. +' Sleep(1000) + StopService(Tracker) + Sleep(5000) + StartService(Tracker) + CallSubDelayed(Tracker, "StartFLP") +' ExitApplication + End If + End If +' LogColor($"Ultima loc: ${Subs.fechaNormal(ultimaLoc.Time)}"$, Colors.blue) + End If +End Sub + +Sub actualizaVar(nombre As String, valor As String) + gps_hist.ExecNonQuery($"delete from CAT_VARIABLES where NOMBRE = '${nombre}'"$) + gps_hist.ExecNonQuery($"insert into CAT_VARIABLES (NOMBRE, VALOR) values ('${nombre}', '${valor}')"$) +End Sub + +'Regresa el valor desde CAT_VARIABLES o del default espacificado en DEFAULT +Sub traeVar(nombre As String, default As String) As String + Private v As String = default + Private c As Cursor = gps_hist.ExecQuery2("select VALOR from CAT_VARIABLES WHERE NOMBRE = ?", Array As String (nombre)) + If c.RowCount > 0 Then + c.Position = 0 + v = c.GetString("VALOR") + End If + Return v +End Sub + +Sub agregaColumna(tabla As String, columna As String, tipo As String) 'ignore + Try 'Intentamos usar "pragma_table_info" para revisar si existe la columna en la tabla + Private c As Cursor = gps_hist.ExecQuery($"SELECT COUNT(*) AS fCol FROM pragma_table_info('${tabla}') WHERE name='${columna}'"$) + c.Position = 0 + If c.GetString("fCol") = 0 Then 'Si no esta la columna la agregamos + gps_hist.ExecNonQuery($"ALTER TABLE ${tabla} ADD COLUMN ${columna} ${tipo}"$) + Log($"Columna "${columna} ${tipo}", agregada a "${tabla}"."$) + End If +' Log(1) + Catch 'Si no funciona "pragma_table_info" lo hacemos con try/catch + Try + gps_hist.ExecNonQuery($"ALTER TABLE ${tabla} ADD COLUMN ${columna} ${tipo}"$) + Log($"Columna "${columna} ${tipo}", agregada a "${tabla}".."$) + Catch + Log(LastException) + End Try + Log(2) + End Try +End Sub diff --git a/B4A/Tracker.bas b/B4A/Tracker.bas new file mode 100644 index 0000000..e263926 --- /dev/null +++ b/B4A/Tracker.bas @@ -0,0 +1,221 @@ +B4A=true +Group=Default Group +ModulesStructureVersion=1 +Type=Service +Version=10.5 +@EndOfDesignText@ +#Region Service Attributes + #StartAtBoot: True +#End Region +'****************************************************************************** +'No olvidar agregar esta linea al editor de manifiesto: +' SetServiceAttribute(Tracker, android:foregroundServiceType, "location") +' +'En Starter agregar estas lineas en Process_Globals +' Public rp As RuntimePermissions +' Public FLP As FusedLocationProvider +' Private flpStarted As Boolean +' +'En Main agregar estas lineas a Activity_Resume +' Starter.rp.CheckAndRequest(Starter.rp.PERMISSION_ACCESS_FINE_LOCATION) +' Wait For Activity_PermissionResult (Permission As String, Result As Boolean) +' If Result Then +' StartService(Tracker) +' Log("Start Tracker") +' Else +' ToastMessageShow("No permission", True) +' End If +' +'Se necesitan las librerias FusedLocationProvider, GPS, Phone y RunTimePermissions +' +'Y en Main agregar estas dos lineas: +'#AdditionalJar: com.android.support:support-v4 +'#AdditionalJar: com.google.android.gms:play-services-location + +Sub Process_Globals + Private nid As Int = 1 + Private Tracking As Boolean + Private lock As PhoneWakeState + Public flp As FusedLocationProvider + Private flpStarted As Boolean + Dim minAccuracy As Int = 50 + Dim killerCalled As Int = 0 + Dim locReqSmall, locReqNormal As LocationRequest +End Sub + +Sub Service_Create + Service.AutomaticForegroundMode = Service.AUTOMATIC_FOREGROUND_NEVER 'we are handling it ourselves + Subs.revisaBD + flp.Initialize("flp") + flp.Connect + lock.PartialLock + StartFLP +' If Starter.logger Then Log("FLP initialized") + Subs.bitacora($"Iniciamos Tracker"$) +End Sub + +Sub Service_Start (StartingIntent As Intent) + locReqSmall = CreateLocationRequest2(1000, 0) 'ignore + locReqNormal = CreateLocationRequest2(30000, 30) 'ignore + Service.StopAutomaticForeground + Service.StartForeground(nid, Subs.CreateNotification("...")) + Track + StartServiceAt(Me, DateTime.Now + 60 * DateTime.TicksPerMinute, True) + Subs.bitacora($"Tracker - Service_Start"$) +End Sub + +'Apagamos y prendemos sel servicio "Tracker" o reinicamos la aplicacion. +'killerCalled = 0 -> Apagamos y prendemos. +'killerCalled = 1 -> ExitApplication (se reinican todos los servicios). +Sub flpReConnect + Try + If killerCalled = 0 Then + killerCalled = 1 + CallSubDelayed(Starter, "restartTracker") + Else + Subs.bitacora("Llamamos EXIT-APP") + ExitApplication + End If + Catch + Log("FLP-Tracker Error -> " & LastException) + End Try +End Sub + +Sub flp_ConnectionSuccess +' If Starter.logger Then Log("FLP - Connected to location provider") + Subs.bitacora("FLP - Connection Success") +End Sub + +Sub flp_ConnectionFailed(ConnectionResult1 As Int) + Log("Failed to connect to location provider") + Subs.bitacora("FLP - Connection Failed") +End Sub + +Public Sub Track + If Starter.logger Then Log("Track") + If Tracking Then Return 'Si ya estamos "rastreando" no hacemos nada (return) + If Starter.rp.Check(Starter.rp.PERMISSION_ACCESS_FINE_LOCATION) = False Then + Log("No permission") + Return + End If + StartFLP 'Iniciamos FusedLocationProvider + Tracking = True +End Sub + +Public Sub StartFLP +' Log("StartFLP - flpStarted="&flpStarted) + Private cont As Int = 0 + Do While flp.IsConnected = False + Sleep(500) +' If Starter.logger Then Log($"FLP Sleeping - ${cont}"$) + Subs.bitacora($"Sleeping - ${cont}"$) + cont = cont + 1 + If cont > 70 Then + cont = 0 + LogColor("Reiniciamos FLP", Colors.red) + killerCalled = 1 : flpReConnect + End If + Loop + flp.RequestLocationUpdates(CreateLocationRequest) 'Buscamos ubicacion + flpStarted = True +' End If +End Sub + +Sub CreateLocationRequest As LocationRequest +' Log("CreateLocationRequest") + Dim lr As LocationRequest + lr.Initialize + lr.SetInterval(30000) 'Intervalo deseado para actualizaciones de ubicacion en milisegundos + lr.SetFastestInterval(lr.GetInterval / 2) 'Intervalo minimo para actualizaciones de ubicacion + lr.SetSmallestDisplacement(30) 'Solo registra cambio de ubicacion si es mayor a XX mts + lr.SetPriority(lr.Priority.PRIORITY_HIGH_ACCURACY) + Return lr +End Sub + +Sub CreateLocationRequest2(interval0 As Int, displacement0 As Int) As LocationRequest 'ignore +' Log("CreateLocationRequestSmall") + Dim lr As LocationRequest + lr.Initialize + lr.SetInterval(interval0) 'Intervalo deseado para actualizaciones de ubicacion en milisegundos + lr.SetFastestInterval(lr.GetInterval / 2) 'Intervalo minimo para actualizaciones de ubicacion + lr.SetSmallestDisplacement(displacement0) 'Solo registra cambio de ubicacion si es mayor a XX mts + lr.SetPriority(lr.Priority.PRIORITY_HIGH_ACCURACY) + Return lr +End Sub + +Public Sub StopFLP + If Starter.logger Then Log("StopFLP") + If flpStarted Then + If flp.IsConnected Then flp.RemoveLocationUpdates 'Eliminamos todas las solicitudes de ubicacion + flpStarted = False + End If +End Sub + +Sub flp_LocationChanged (Location1 As Location) + Starter.UUC = Location1 +' If Starter.logger Then LogColor("FLP_LocationChanged", Colors.Red) + If Starter.logger Then LogColor($"FLP_LocationChanged - Acc: ${Location1.Accuracy}"$, Colors.Red) + Private vel As String = (Location1.Speed * 60 * 60) / 1000 ' Kms x hora de ultima ubicacion. +' If Starter.logger Then Log((Location1.time & " - " & Starter.locAntTime)) + If Starter.logger Then Log(((Location1.time - Subs.traeVar("locAntTime", 0)) / 1000) & " segs") + + If Location1.Latitude <> 0 Then Subs.guardaInfoEnBD(Location1) + +' Private minsTranscurridosLoc As String = Subs.ticksAMins(Location1.time - Starter.locAntTime) 'Minutos transcurridos desde la ultima ubicacion ACTUALIZADA. +' Private minsTranscurridos As String = Subs.ticksAMins(DateTime.Now - Subs.traeUltimaUbicacionGuardada.Time) 'Minutos transcurridos desde la ultima ubicacion guardada. +' If Starter.logger Then LogColor(">>>>>>>> Guardada: " & minsTranscurridos & " mins., Actualizada: " & minsTranscurridosLoc & " mins.", Colors.red) +' If Not(IsPaused(Main)) Then +' Main.laUbicacion = Location1 ' Actualizamos la etiqueta de ubicacion en la pantalla principal. +' If Starter.logger Then Log("Main.laubicacion actializada") +' End If +' Dim el_texto As String = "" +' If Starter.logger Then Log(Location1.Accuracy&" - "&Starter.UUC.AccuracyValid) +' If minsTranscurridosLoc > 1 Or minsTranscurridos >= 15 Then ' Para que no mande mensajes constantes, minimo 1 minuto entre mensajes. +' flp.RequestLocationUpdates(CreateLocationRequest) +' 'Solo mandamos la ubicacion si la precision es dentro de XX mts +' el_texto = $"LocChange - Coords NO enviadas (Acc:${Location1.Accuracy})."$ +' If Location1.Accuracy < minAccuracy Then +' If Starter.logger Then LogColor("Guardamos y enviamos ubicacion.", Colors.green) + Subs.mandaLoc2(Location1, Starter.devModel) +' el_texto = $"LocChange - Coords enviadas (Acc:${Location1.Accuracy})."$ +' End If +' ' If Starter.logger Then Log("Loc changed : "&Location1.Latitude&","&Location1.Longitude&"|"&Starter.devModel&"|") +' Subs.bitacora($"${el_texto}"$) +'' ToastMessageShow("LocChanged MORE than a min.", False) +' Else +'' ToastMessageShow("Locatin changed but less than a min!.", False) +' End If +' Subs.actualizaVar("locAntTime", Location1.time) +End Sub + +Sub flp_ConnectionSuspended(SuspendedCause1 As Int) + Dim Cause As String + Log("ConnectionSuspended") + Subs.bitacora("ConnectionSuspended") + If(SuspendedCause1=flp.SuspendedCause.CAUSE_NETWORK_LOST) Then + Cause="Suspended by NetworkLost" + Else If(SuspendedCause1=flp.SuspendedCause.CAUSE_SERVICE_DISCONNECTED) Then + Cause="Suspended by Disconnected" + Else + Cause="Suspended: Unknow cause" + End If + Cause = Cause&": "& Subs.fechaNormal(DateTime.Now) + LogColor(Cause, Colors.magenta) + Subs.bitacora(Cause) + flp.Disconnect + Sleep(5000) + flp.Connect +End Sub + +Sub Service_Destroy + If Tracking Then + Try + StopFLP + Catch + Log(LastException) + End Try + End If + Tracking = False + lock.ReleasePartialLock + Subs.bitacora($"Tracker - Service_Destroy"$) +End Sub diff --git a/B4A/errorManager.bas b/B4A/errorManager.bas new file mode 100644 index 0000000..e803ad9 --- /dev/null +++ b/B4A/errorManager.bas @@ -0,0 +1,164 @@ +B4A=true +Group=Default Group +ModulesStructureVersion=1 +Type=Activity +Version=10.2 +@EndOfDesignText@ +#Region Activity Attributes + #FullScreen: False + #IncludeTitle: True +#End Region +'****************************************************************************** +'Este modulo intercepta los errores de la aplicación mediante "Starter.Application_Error" y muestra una pantalla +'con el log del error y lo manda al servidor con un query de DBRequestManager, se necesita que exista el query +'en el "config.properties" llamado "guardaErrores" y que tenga el siguiente texto: +' +'sql.guardaErrores=INSERT INTO KELLOGGS.PUSH_INFO (ID, RUTA, FECHA, DATOS) VALUES((?),(?),(?),(?)) +' +'Agregar estas lineas a "Starter.Process_Globals" +' 'Para los Logs +' Dim logs As StringBuilder +' Private logcat As LogCat +' +'Agregar estas lineas a "Starter.Service_Create" +' 'Para los Logs +' #if RELEASE +' logcat.LogCatStart(Array As String("-v","raw","*:F","B4A:v"), "logcat") +' #end if +' logs.Initialize +' +'Agregar este Sub a "Starter" +' +'Return true to allow the OS default exceptions handler to handle the uncaught exception. 'Para los Logs +'Sub Application_Error (Error As Exception, StackTrace As String) As Boolean +' 'wait for 500ms to allow the logs to be updated. +' Dim jo As JavaObject +' Dim l As Long = 500: jo.InitializeStatic("java.lang.Thread").RunMethod("sleep", Array(l)) 'Sleep 500ms +' logcat.LogCatStop +' logs.Append(StackTrace) +' Subs.revisaBD +' Subs.errorLog.ExecNonQuery2("INSERT INTO errores(fecha, error) VALUES (?,?)", Array As Object (Subs.fechaKMT(DateTime.now), logs)) +' StartActivity(errorManager) +' Return True +'End Sub +'****************************************************************************** + +Sub Process_Globals + 'These global variables will be declared once when the application starts. + 'These variables can be accessed from all modules. + +End Sub + +Sub Globals + 'These global variables will be redeclared each time the activity is created. + 'These variables can only be accessed from this module. +' Dim errorLog As SQL + Dim c As Cursor +' Private p_principal As Panel +' Private l_titulo As Label +' Private svScroll As ScrollView +' Private etText As EditText +' Private c_continuar As Button +' Private p_botones As Panel +' Private b_salir As Button +End Sub + +Sub Activity_Create(FirstTime As Boolean) + 'Do not forget to load the layout file created with the visual designer. For example: +' Activity.LoadLayout("errorManager") +End Sub + +Sub Activity_Resume + Dim elError As String = "" + Dim laFecha As String = "" +' svScroll.Initialize(500dip) +' Activity.AddView(svScroll, 0, 300, 100%x, 80%y) +' p_principal.Height = Activity.Height +' p_principal.Width = Activity.Width +' svScroll.Width = Round(p_principal.Width * 0.9) +' svScroll.Left = Round(p_principal.Width/2)-Round(svScroll.Width/2) +' p_botones.Left = Round(p_principal.Width/2)-Round(p_botones.Width/2) +' p_botones.Top = Activity.Height - (p_botones.Height + 80) +' etText.Initialize("") +' etText.TextSize = 13 +' etText.Wrap = True +' Activity.RemoveViewAt(1) +' svScroll.Panel.AddView(etText, 0, 0, 90%x, 80%y) +'' etText.Width = svScroll.Width - 100 +' etText.InputType = etText.INPUT_TYPE_NONE +' etText.Gravity = Gravity.TOP +' etText.SingleLine = False +' etText.Wrap = True +' Dim lblText, edtText As StringBuilder +' Dim lbl As Label +' lbl.Initialize("") +' Activity.AddView(lbl, 0, 300, 100%x, 100%y) 'ignore +' etText.Text = "" +' Subs.revisaBD + c = Starter.errorLog.ExecQuery("select * from errores order by fecha desc limit 1") + If c.RowCount > 0 Then + c.Position = 0 + elError = c.GetString("error") + laFecha = c.GetString("fecha") +' etText.Text = elError + End If + c.Close + +' Log("++++++" & Starter.logsStr) +' etText.Text = etText.Text & Starter.logsStr +' lbl.TextSize = etText.TextSize +' lbl.Text = etText.Text +' Dim su As StringUtils +' Dim edheight As Int = su.MeasureMultilineTextHeight(lbl, lbl.Text) +' lbl.RemoveView +' etText.Height = edheight +' svScroll.Panel.Height = edheight +' svScroll.Height = Round(Activity.Height * 0.9) +' Log(edheight) + + Dim cmd As DBCommand + cmd.Initialize + cmd.Name = "guardaErrores" + cmd.Parameters = Array As Object(laFecha, Starter.devModel, laFecha, elError) + Log($"Mandamos: ${Subs.fechaKMT(DateTime.Now)}, |${Starter.devModel}|, ${Subs.fechaKMT(DateTime.Now)}"$) + Starter.reqManager.ExecuteCommand(cmd,"guardaErrores") +End Sub + +Sub Activity_Pause (UserClosed As Boolean) + +End Sub + +Sub JobDone(Job As HttpJob) + If Job.Success = False Then + ToastMessageShow("Error: " & Job.ErrorMessage, True) + Else + LogColor("JobDone: '" & Starter.reqManager.HandleJob(Job).tag & "' - Registros: " & Starter.reqManager.HandleJob(Job).Rows.Size, Colors.Green) 'Mod por CHV - 211110 + If Job.JobName = "DBRequest" Then + Dim result As DBResult = Starter.reqManager.HandleJob(Job) + If result.Tag = "guardaErrores" Then 'query tag + For Each records() As Object In result.Rows + For Each k As String In result.Columns.Keys + Log("GuardaErrores: " & k & ": " & records(result.Columns.Get(k))) + Next + Next + End If + End If + End If + Job.Release +End Sub + +Private Sub c_continuar_Click +' Subs.iniciaActividad("Principal") +' B4XPages.ShowPage("Principal") +End Sub + +Private Sub b_salir_Click + cierraActividades +End Sub + +Sub cierraActividades + Log("closing activities") + Dim jo As JavaObject + jo.InitializeContext + jo.RunMethod("finishAffinity", Null) +End Sub \ No newline at end of file diff --git a/B4A/google-services.json b/B4A/google-services.json new file mode 100644 index 0000000..9268fd1 --- /dev/null +++ b/B4A/google-services.json @@ -0,0 +1,213 @@ +{ + "project_info": { + "project_number": "821860097209", + "project_id": "pusher-4c091", + "storage_bucket": "pusher-4c091.firebasestorage.app" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:821860097209:android:328e84bfb7adb112f24f68", + "android_client_info": { + "package_name": "dreparto.keymon.com.mx" + } + }, + "oauth_client": [ + { + "client_id": "821860097209-ef17t5620111ghub7l0tple62otbb56v.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyDS-_5lpLX5IiKYrG-0Et-KCKx1bwlY7R0" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "821860097209-ef17t5620111ghub7l0tple62otbb56v.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:821860097209:android:7bdc730c1cad5927f24f68", + "android_client_info": { + "package_name": "durakelo.keymon.com.mx" + } + }, + "oauth_client": [ + { + "client_id": "821860097209-ef17t5620111ghub7l0tple62otbb56v.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyDS-_5lpLX5IiKYrG-0Et-KCKx1bwlY7R0" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "821860097209-ef17t5620111ghub7l0tple62otbb56v.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:821860097209:android:4a9c1af4c93ba100f24f68", + "android_client_info": { + "package_name": "flp2.chv.com" + } + }, + "oauth_client": [ + { + "client_id": "821860097209-ef17t5620111ghub7l0tple62otbb56v.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyDS-_5lpLX5IiKYrG-0Et-KCKx1bwlY7R0" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "821860097209-ef17t5620111ghub7l0tple62otbb56v.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:821860097209:android:7b6620b2a870f23cf24f68", + "android_client_info": { + "package_name": "gunav2.keymon.com.mx" + } + }, + "oauth_client": [ + { + "client_id": "821860097209-ef17t5620111ghub7l0tple62otbb56v.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyDS-_5lpLX5IiKYrG-0Et-KCKx1bwlY7R0" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "821860097209-ef17t5620111ghub7l0tple62otbb56v.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:821860097209:android:f0e04307b3842e25f24f68", + "android_client_info": { + "package_name": "prof_rep.keymon.com.mx" + } + }, + "oauth_client": [ + { + "client_id": "821860097209-ef17t5620111ghub7l0tple62otbb56v.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyDS-_5lpLX5IiKYrG-0Et-KCKx1bwlY7R0" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "821860097209-ef17t5620111ghub7l0tple62otbb56v.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:821860097209:android:7780f81ae43bf0f3f24f68", + "android_client_info": { + "package_name": "pusher.chv.com" + } + }, + "oauth_client": [ + { + "client_id": "821860097209-ef17t5620111ghub7l0tple62otbb56v.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyDS-_5lpLX5IiKYrG-0Et-KCKx1bwlY7R0" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "821860097209-ef17t5620111ghub7l0tple62otbb56v.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:821860097209:android:7c55bc95da6d952df24f68", + "android_client_info": { + "package_name": "ths.keymon.com.mx" + } + }, + "oauth_client": [ + { + "client_id": "821860097209-ef17t5620111ghub7l0tple62otbb56v.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyDS-_5lpLX5IiKYrG-0Et-KCKx1bwlY7R0" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "821860097209-ef17t5620111ghub7l0tple62otbb56v.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file