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 0000000..d306d9d
Binary files /dev/null and b/B4A/Files/gps_hist.db differ
diff --git a/B4A/Files/layout.bal b/B4A/Files/layout.bal
new file mode 100644
index 0000000..c059ea1
Binary files /dev/null and b/B4A/Files/layout.bal differ
diff --git a/B4A/Files/mainpage.bal b/B4A/Files/mainpage.bal
new file mode 100644
index 0000000..acba0d3
Binary files /dev/null and b/B4A/Files/mainpage.bal differ
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