aW1wb3J0IGFuZHJvaWQuTWFuaWZlc3QKaW1wb3J0IGFuZHJvaWQuY29udGVudC5Db250ZXh0CmltcG9ydCBhbmRyb2lkLmNvbnRlbnQucG0uUGFja2FnZU1hbmFnZXIKaW1wb3J0IGFuZHJvaWQubmV0LndpZmkuV2lmaU1hbmFnZXIKaW1wb3J0IGFuZHJvaWQub3MuQnVuZGxlCmltcG9ydCBhbmRyb2lkLm9zLkhhbmRsZXIKaW1wb3J0IGFuZHJvaWQub3MuTG9vcGVyCmltcG9ydCBhbmRyb2lkLnZpZXcuTWVudQppbXBvcnQgYW5kcm9pZC52aWV3Lk1lbnVJdGVtCmltcG9ydCBhbmRyb2lkLndpZGdldC5CdXR0b24KaW1wb3J0IGFuZHJvaWQud2lkZ2V0LkVkaXRUZXh0CmltcG9ydCBhbmRyb2lkLndpZGdldC5UZXh0VmlldwppbXBvcnQgYW5kcm9pZHguYXBwY29tcGF0LmFwcC5BcHBDb21wYXRBY3Rpdml0eQppbXBvcnQgYW5kcm9pZHguY29yZS5hcHAuQWN0aXZpdHlDb21wYXQKaW1wb3J0IGFuZHJvaWR4LmNvcmUuY29udGVudC5Db250ZXh0Q29tcGF0CmltcG9ydCBhbmRyb2lkeC5yb29tLioKaW1wb3J0IGNvbS5naXRodWIubWlrZXBoaWwuY2hhcnRpbmcuY2hhcnRzLkxpbmVDaGFydAppbXBvcnQgY29tLmdpdGh1Yi5taWtlcGhpbC5jaGFydGluZy5kYXRhLkVudHJ5CmltcG9ydCBjb20uZ2l0aHViLm1pa2VwaGlsLmNoYXJ0aW5nLmRhdGEuTGluZURhdGEKaW1wb3J0IGNvbS5naXRodWIubWlrZXBoaWwuY2hhcnRpbmcuZGF0YS5MaW5lRGF0YVNldAppbXBvcnQgamF2YS51dGlsLioKaW1wb3J0IGtvdGxpbnguY29yb3V0aW5lcy4qCmltcG9ydCBqYXZhLnRleHQuU2ltcGxlRGF0ZUZvcm1hdAoKLy8gRW50aWRhZCBwYXJhIGxhIGJhc2UgZGUgZGF0b3MKQEVudGl0eSh0YWJsZU5hbWUgPSAmcXVvdDt3aWZpX21lYXN1cmVtZW50cyZxdW90OykKZGF0YSBjbGFzcyBXaWZpTWVhc3VyZW1lbnQoCiAgICBAUHJpbWFyeUtleShhdXRvR2VuZXJhdGUgPSB0cnVlKSB2YWwgaWQ6IEludCA9IDAsCiAgICB2YWwgc3NpZDogU3RyaW5nLAogICAgdmFsIGJzc2lkOiBTdHJpbmcsCiAgICB2YWwgc2lnbmFsU3RyZW5ndGg6IEludCwKICAgIHZhbCB0aW1lc3RhbXA6IExvbmcgPSBTeXN0ZW0uY3VycmVudFRpbWVNaWxsaXMoKQopCgovLyBEQU8gcGFyYSBhY2NlZGVyIGEgbGEgYmFzZSBkZSBkYXRvcwpARGFvCmludGVyZmFjZSBXaWZpTWVhc3VyZW1lbnREYW8gewogICAgQFF1ZXJ5KCZxdW90O1NFTEVDVCAqIEZST00gd2lmaV9tZWFzdXJlbWVudHMgV0hFUkUgc3NpZCA9IDpzc2lkIE9SREVSIEJZIHRpbWVzdGFtcCBERVNDJnF1b3Q7KQogICAgc3VzcGVuZCBmdW4gZ2V0TWVhc3VyZW1lbnRzRm9yTmV0d29yayhzc2lkOiBTdHJpbmcpOiBMaXN0Jmx0O1dpZmlNZWFzdXJlbWVudCZndDsKCiAgICBASW5zZXJ0CiAgICBzdXNwZW5kIGZ1biBpbnNlcnQobWVhc3VyZW1lbnQ6IFdpZmlNZWFzdXJlbWVudCkKCiAgICBAUXVlcnkoJnF1b3Q7REVMRVRFIEZST00gd2lmaV9tZWFzdXJlbWVudHMgV0hFUkUgdGltZXN0YW1wICZsdDsgOnRpbWVzdGFtcCZxdW90OykKICAgIHN1c3BlbmQgZnVuIGRlbGV0ZU9sZE1lYXN1cmVtZW50cyh0aW1lc3RhbXA6IExvbmcpCn0KCi8vIEJhc2UgZGUgZGF0b3MKQERhdGFiYXNlKGVudGl0aWVzID0gW1dpZmlNZWFzdXJlbWVudDo6Y2xhc3NdLCB2ZXJzaW9uID0gMSkKYWJzdHJhY3QgY2xhc3MgQXBwRGF0YWJhc2UgOiBSb29tRGF0YWJhc2UoKSB7CiAgICBhYnN0cmFjdCBmdW4gd2lmaU1lYXN1cmVtZW50RGFvKCk6IFdpZmlNZWFzdXJlbWVudERhbwp9CgpjbGFzcyBNYWluQWN0aXZpdHkgOiBBcHBDb21wYXRBY3Rpdml0eSgpIHsKICAgIHByaXZhdGUgbGF0ZWluaXQgdmFyIHdpZmlNYW5hZ2VyOiBXaWZpTWFuYWdlcgogICAgcHJpdmF0ZSBsYXRlaW5pdCB2YXIgc2NhblJlc3VsdHNUZXh0OiBUZXh0VmlldwogICAgcHJpdmF0ZSBsYXRlaW5pdCB2YXIgc2NhbkJ1dHRvbjogQnV0dG9uCiAgICBwcml2YXRlIGxhdGVpbml0IHZhciBmaWx0ZXJFZGl0OiBFZGl0VGV4dAogICAgcHJpdmF0ZSBsYXRlaW5pdCB2YXIgc2lnbmFsQ2hhcnQ6IExpbmVDaGFydAogICAgcHJpdmF0ZSBsYXRlaW5pdCB2YXIgZGI6IEFwcERhdGFiYXNlCiAgICAKICAgIHByaXZhdGUgdmFsIFBFUk1JU1NJT05TX1JFUVVFU1RfQ09ERSA9IDEyMwogICAgcHJpdmF0ZSB2YWwgcmVxdWlyZWRQZXJtaXNzaW9ucyA9IGFycmF5T2YoCiAgICAgICAgTWFuaWZlc3QucGVybWlzc2lvbi5BQ0NFU1NfRklORV9MT0NBVElPTiwKICAgICAgICBNYW5pZmVzdC5wZXJtaXNzaW9uLkFDQ0VTU19XSUZJX1NUQVRFLAogICAgICAgIE1hbmlmZXN0LnBlcm1pc3Npb24uQ0hBTkdFX1dJRklfU1RBVEUKICAgICkKICAgIAogICAgcHJpdmF0ZSB2YWwgaGFuZGxlciA9IEhhbmRsZXIoTG9vcGVyLmdldE1haW5Mb29wZXIoKSkKICAgIHByaXZhdGUgdmFyIGlzQXV0b1VwZGF0ZUVuYWJsZWQgPSBmYWxzZQogICAgcHJpdmF0ZSB2YWwgdXBkYXRlSW50ZXJ2YWwgPSA1MDAwTCAvLyA1IHNlZ3VuZG9zCiAgICBwcml2YXRlIHZhbCBzY29wZSA9IENvcm91dGluZVNjb3BlKERpc3BhdGNoZXJzLk1haW4gKyBKb2IoKSkKCiAgICBvdmVycmlkZSBmdW4gb25DcmVhdGUoc2F2ZWRJbnN0YW5jZVN0YXRlOiBCdW5kbGU/KSB7CiAgICAgICAgc3VwZXIub25DcmVhdGUoc2F2ZWRJbnN0YW5jZVN0YXRlKQogICAgICAgIHNldENvbnRlbnRWaWV3KFIubGF5b3V0LmFjdGl2aXR5X21haW4pCgogICAgICAgIC8vIEluaWNpYWxpemFyIGNvbXBvbmVudGVzCiAgICAgICAgd2lmaU1hbmFnZXIgPSBhcHBsaWNhdGlvbkNvbnRleHQuZ2V0U3lzdGVtU2VydmljZShDb250ZXh0LldJRklfU0VSVklDRSkgYXMgV2lmaU1hbmFnZXIKICAgICAgICBzY2FuUmVzdWx0c1RleHQgPSBmaW5kVmlld0J5SWQoUi5pZC5zY2FuUmVzdWx0c1RleHQpCiAgICAgICAgc2NhbkJ1dHRvbiA9IGZpbmRWaWV3QnlJZChSLmlkLnNjYW5CdXR0b24pCiAgICAgICAgZmlsdGVyRWRpdCA9IGZpbmRWaWV3QnlJZChSLmlkLmZpbHRlckVkaXQpCiAgICAgICAgc2lnbmFsQ2hhcnQgPSBmaW5kVmlld0J5SWQoUi5pZC5zaWduYWxDaGFydCkKCiAgICAgICAgLy8gSW5pY2lhbGl6YXIgYmFzZSBkZSBkYXRvcwogICAgICAgIGRiID0gUm9vbS5kYXRhYmFzZUJ1aWxkZXIoCiAgICAgICAgICAgIGFwcGxpY2F0aW9uQ29udGV4dCwKICAgICAgICAgICAgQXBwRGF0YWJhc2U6OmNsYXNzLmphdmEsCiAgICAgICAgICAgICZxdW90O3dpZmktZGF0YWJhc2UmcXVvdDsKICAgICAgICApLmJ1aWxkKCkKCiAgICAgICAgLy8gQ29uZmlndXJhciBlbCBnciZhYWN1dGU7ZmljbwogICAgICAgIHNldHVwQ2hhcnQoKQoKICAgICAgICBzY2FuQnV0dG9uLnNldE9uQ2xpY2tMaXN0ZW5lciB7CiAgICAgICAgICAgIGlmIChjaGVja0FuZFJlcXVlc3RQZXJtaXNzaW9ucygpKSB7CiAgICAgICAgICAgICAgICBzY2FuV2lmaU5ldHdvcmtzKCkKICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgLy8gTGltcGlhciBkYXRvcyBhbnRpZ3VvcyAobSZhYWN1dGU7cyBkZSAyNCBob3JhcykKICAgICAgICBzY29wZS5sYXVuY2ggewogICAgICAgICAgICB2YWwgb25lRGF5QWdvID0gU3lzdGVtLmN1cnJlbnRUaW1lTWlsbGlzKCkgLSAoMjQgKiA2MCAqIDYwICogMTAwMCkKICAgICAgICAgICAgZGIud2lmaU1lYXN1cmVtZW50RGFvKCkuZGVsZXRlT2xkTWVhc3VyZW1lbnRzKG9uZURheUFnbykKICAgICAgICB9CiAgICB9CgogICAgcHJpdmF0ZSBmdW4gc2V0dXBDaGFydCgpIHsKICAgICAgICBzaWduYWxDaGFydC5hcHBseSB7CiAgICAgICAgICAgIGRlc2NyaXB0aW9uLmlzRW5hYmxlZCA9IGZhbHNlCiAgICAgICAgICAgIHNldERyYXdHcmlkQmFja2dyb3VuZChmYWxzZSkKICAgICAgICAgICAgeEF4aXMuc2V0RHJhd0dyaWRMaW5lcyhmYWxzZSkKICAgICAgICAgICAgYXhpc1JpZ2h0LmlzRW5hYmxlZCA9IGZhbHNlCiAgICAgICAgICAgIGxlZ2VuZC5pc0VuYWJsZWQgPSB0cnVlCiAgICAgICAgfQogICAgfQoKICAgIHByaXZhdGUgZnVuIHN0YXJ0QXV0b1VwZGF0ZSgpIHsKICAgICAgICBpc0F1dG9VcGRhdGVFbmFibGVkID0gdHJ1ZQogICAgICAgIGhhbmRsZXIucG9zdERlbGF5ZWQob2JqZWN0IDogUnVubmFibGUgewogICAgICAgICAgICBvdmVycmlkZSBmdW4gcnVuKCkgewogICAgICAgICAgICAgICAgaWYgKGlzQXV0b1VwZGF0ZUVuYWJsZWQgJmFtcDsmYW1wOyBjaGVja0FuZFJlcXVlc3RQZXJtaXNzaW9ucygpKSB7CiAgICAgICAgICAgICAgICAgICAgc2NhbldpZmlOZXR3b3JrcygpCiAgICAgICAgICAgICAgICAgICAgaGFuZGxlci5wb3N0RGVsYXllZCh0aGlzLCB1cGRhdGVJbnRlcnZhbCkKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0sIHVwZGF0ZUludGVydmFsKQogICAgfQoKICAgIHByaXZhdGUgZnVuIHN0b3BBdXRvVXBkYXRlKCkgewogICAgICAgIGlzQXV0b1VwZGF0ZUVuYWJsZWQgPSBmYWxzZQogICAgICAgIGhhbmRsZXIucmVtb3ZlQ2FsbGJhY2tzQW5kTWVzc2FnZXMobnVsbCkKICAgIH0KCiAgICBwcml2YXRlIGZ1biBzY2FuV2lmaU5ldHdvcmtzKCkgewogICAgICAgIHZhbCByZXN1bHRzID0gd2lmaU1hbmFnZXIuc2NhblJlc3VsdHMKICAgICAgICB2YWwgZmlsdGVyVGV4dCA9IGZpbHRlckVkaXQudGV4dC50b1N0cmluZygpLmxvd2VyY2FzZSgpCiAgICAgICAgCiAgICAgICAgdmFsIGZpbHRlcmVkUmVzdWx0cyA9IHJlc3VsdHMuZmlsdGVyIHsgcmVzdWx0IC0mZ3Q7CiAgICAgICAgICAgIGZpbHRlclRleHQuaXNFbXB0eSgpIHx8IHJlc3VsdC5TU0lELmxvd2VyY2FzZSgpLmNvbnRhaW5zKGZpbHRlclRleHQpCiAgICAgICAgfQoKICAgICAgICB2YWwgbmV0d29ya3NJbmZvID0gU3RyaW5nQnVpbGRlcigpCiAgICAgICAgCiAgICAgICAgc2NvcGUubGF1bmNoIHsKICAgICAgICAgICAgZmlsdGVyZWRSZXN1bHRzLmZvckVhY2ggeyByZXN1bHQgLSZndDsKICAgICAgICAgICAgICAgIC8vIEd1YXJkYXIgbWVkaWNpJm9hY3V0ZTtuIGVuIGxhIGJhc2UgZGUgZGF0b3MKICAgICAgICAgICAgICAgIGRiLndpZmlNZWFzdXJlbWVudERhbygpLmluc2VydCgKICAgICAgICAgICAgICAgICAgICBXaWZpTWVhc3VyZW1lbnQoCiAgICAgICAgICAgICAgICAgICAgICAgIHNzaWQgPSByZXN1bHQuU1NJRCwKICAgICAgICAgICAgICAgICAgICAgICAgYnNzaWQgPSByZXN1bHQuQlNTSUQsCiAgICAgICAgICAgICAgICAgICAgICAgIHNpZ25hbFN0cmVuZ3RoID0gcmVzdWx0LmxldmVsCiAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgKQoKICAgICAgICAgICAgICAgIG5ldHdvcmtzSW5mby5hcHBlbmQoJnF1b3Q7JnF1b3Q7JnF1b3Q7CiAgICAgICAgICAgICAgICAgICAgU1NJRDogJHtyZXN1bHQuU1NJRH0KICAgICAgICAgICAgICAgICAgICBCU1NJRDogJHtyZXN1bHQuQlNTSUR9CiAgICAgICAgICAgICAgICAgICAgSW50ZW5zaWRhZCBkZSBzZSZudGlsZGU7YWw6ICR7cmVzdWx0LmxldmVsfSBkQm0KICAgICAgICAgICAgICAgICAgICBGcmVjdWVuY2lhOiAke3Jlc3VsdC5mcmVxdWVuY3l9IE1IegogICAgICAgICAgICAgICAgICAgIENhcGFjaWRhZGVzOiAke3Jlc3VsdC5jYXBhYmlsaXRpZXN9CiAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAmcXVvdDsmcXVvdDsmcXVvdDsudHJpbUluZGVudCgpKQogICAgICAgICAgICB9CiAgICAgICAgICAgIAogICAgICAgICAgICBzY2FuUmVzdWx0c1RleHQudGV4dCA9IG5ldHdvcmtzSW5mby50b1N0cmluZygpCiAgICAgICAgICAgIHVwZGF0ZUNoYXJ0KGZpbHRlcmVkUmVzdWx0c1swXS5TU0lEKSAvLyBBY3R1YWxpemFyIGdyJmFhY3V0ZTtmaWNvIHBhcmEgbGEgcHJpbWVyYSByZWQKICAgICAgICB9CiAgICB9CgogICAgcHJpdmF0ZSBmdW4gdXBkYXRlQ2hhcnQoc3NpZDogU3RyaW5nKSB7CiAgICAgICAgc2NvcGUubGF1bmNoIHsKICAgICAgICAgICAgdmFsIG1lYXN1cmVtZW50cyA9IGRiLndpZmlNZWFzdXJlbWVudERhbygpLmdldE1lYXN1cmVtZW50c0Zvck5ldHdvcmsoc3NpZCkKICAgICAgICAgICAgdmFsIGVudHJpZXMgPSBtZWFzdXJlbWVudHMubWFwSW5kZXhlZCB7IGluZGV4LCBtZWFzdXJlbWVudCAtJmd0OwogICAgICAgICAgICAgICAgRW50cnkoaW5kZXgudG9GbG9hdCgpLCBtZWFzdXJlbWVudC5zaWduYWxTdHJlbmd0aC50b0Zsb2F0KCkpCiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIHZhbCBkYXRhU2V0ID0gTGluZURhdGFTZXQoZW50cmllcywgJnF1b3Q7U2UmbnRpbGRlO2FsIGRlICRzc2lkJnF1b3Q7KS5hcHBseSB7CiAgICAgICAgICAgICAgICBjb2xvciA9IENvbnRleHRDb21wYXQuZ2V0Q29sb3IodGhpc0BNYWluQWN0aXZpdHksIFIuY29sb3IucHVycGxlXzUwMCkKICAgICAgICAgICAgICAgIHNldERyYXdDaXJjbGVzKGZhbHNlKQogICAgICAgICAgICAgICAgbGluZVdpZHRoID0gMmYKICAgICAgICAgICAgfQoKICAgICAgICAgICAgc2lnbmFsQ2hhcnQuZGF0YSA9IExpbmVEYXRhKGRhdGFTZXQpCiAgICAgICAgICAgIHNpZ25hbENoYXJ0LmludmFsaWRhdGUoKQogICAgICAgIH0KICAgIH0KCiAgICBvdmVycmlkZSBmdW4gb25DcmVhdGVPcHRpb25zTWVudShtZW51OiBNZW51KTogQm9vbGVhbiB7CiAgICAgICAgbWVudUluZmxhdGVyLmluZmxhdGUoUi5tZW51Lm1haW5fbWVudSwgbWVudSkKICAgICAgICByZXR1cm4gdHJ1ZQogICAgfQoKICAgIG92ZXJyaWRlIGZ1biBvbk9wdGlvbnNJdGVtU2VsZWN0ZWQoaXRlbTogTWVudUl0ZW0pOiBCb29sZWFuIHsKICAgICAgICByZXR1cm4gd2hlbiAoaXRlbS5pdGVtSWQpIHsKICAgICAgICAgICAgUi5pZC5hY3Rpb25fYXV0b191cGRhdGUgLSZndDsgewogICAgICAgICAgICAgICAgaWYgKGlzQXV0b1VwZGF0ZUVuYWJsZWQpIHsKICAgICAgICAgICAgICAgICAgICBzdG9wQXV0b1VwZGF0ZSgpCiAgICAgICAgICAgICAgICAgICAgaXRlbS5zZXRUaXRsZSgmcXVvdDtJbmljaWFyIGFjdHVhbGl6YWNpJm9hY3V0ZTtuIGF1dG9tJmFhY3V0ZTt0aWNhJnF1b3Q7KQogICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICBzdGFydEF1dG9VcGRhdGUoKQogICAgICAgICAgICAgICAgICAgIGl0ZW0uc2V0VGl0bGUoJnF1b3Q7RGV0ZW5lciBhY3R1YWxpemFjaSZvYWN1dGU7biBhdXRvbSZhYWN1dGU7dGljYSZxdW90OykKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIHRydWUKICAgICAgICAgICAgfQogICAgICAgICAgICBlbHNlIC0mZ3Q7IHN1cGVyLm9uT3B0aW9uc0l0ZW1TZWxlY3RlZChpdGVtKQogICAgICAgIH0KICAgIH0KCiAgICBvdmVycmlkZSBmdW4gb25EZXN0cm95KCkgewogICAgICAgIHN1cGVyLm9uRGVzdHJveSgpCiAgICAgICAgc3RvcEF1dG9VcGRhdGUoKQogICAgICAgIHNjb3BlLmNhbmNlbCgpCiAgICB9Cn0=
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.net.wifi.WifiManager
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.Menu
import android.view.MenuItem
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.room.*
import com.github.mikephil.charting.charts.LineChart
import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.data.LineData
import com.github.mikephil.charting.data.LineDataSet
import java.util.*
import kotlinx.coroutines.*
import java.text.SimpleDateFormat
// Entidad para la base de datos
@Entity(tableName = "wifi_measurements")
data class WifiMeasurement(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val ssid: String,
val bssid: String,
val signalStrength: Int,
val timestamp: Long = System.currentTimeMillis()
)
// DAO para acceder a la base de datos
@Dao
interface WifiMeasurementDao {
@Query("SELECT * FROM wifi_measurements WHERE ssid = :ssid ORDER BY timestamp DESC")
suspend fun getMeasurementsForNetwork(ssid: String): List<WifiMeasurement>
@Insert
suspend fun insert(measurement: WifiMeasurement)
@Query("DELETE FROM wifi_measurements WHERE timestamp < :timestamp")
suspend fun deleteOldMeasurements(timestamp: Long)
}
// Base de datos
@Database(entities = [WifiMeasurement::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun wifiMeasurementDao(): WifiMeasurementDao
}
class MainActivity : AppCompatActivity() {
private lateinit var wifiManager: WifiManager
private lateinit var scanResultsText: TextView
private lateinit var scanButton: Button
private lateinit var filterEdit: EditText
private lateinit var signalChart: LineChart
private lateinit var db: AppDatabase
private val PERMISSIONS_REQUEST_CODE = 123
private val requiredPermissions = arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_WIFI_STATE,
Manifest.permission.CHANGE_WIFI_STATE
)
private val handler = Handler(Looper.getMainLooper())
private var isAutoUpdateEnabled = false
private val updateInterval = 5000L // 5 segundos
private val scope = CoroutineScope(Dispatchers.Main + Job())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Inicializar componentes
wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
scanResultsText = findViewById(R.id.scanResultsText)
scanButton = findViewById(R.id.scanButton)
filterEdit = findViewById(R.id.filterEdit)
signalChart = findViewById(R.id.signalChart)
// Inicializar base de datos
db = Room.databaseBuilder(
applicationContext,
AppDatabase::class.java,
"wifi-database"
).build()
// Configurar el gráfico
setupChart()
scanButton.setOnClickListener {
if (checkAndRequestPermissions()) {
scanWifiNetworks()
}
}
// Limpiar datos antiguos (más de 24 horas)
scope.launch {
val oneDayAgo = System.currentTimeMillis() - (24 * 60 * 60 * 1000)
db.wifiMeasurementDao().deleteOldMeasurements(oneDayAgo)
}
}
private fun setupChart() {
signalChart.apply {
description.isEnabled = false
setDrawGridBackground(false)
xAxis.setDrawGridLines(false)
axisRight.isEnabled = false
legend.isEnabled = true
}
}
private fun startAutoUpdate() {
isAutoUpdateEnabled = true
handler.postDelayed(object : Runnable {
override fun run() {
if (isAutoUpdateEnabled && checkAndRequestPermissions()) {
scanWifiNetworks()
handler.postDelayed(this, updateInterval)
}
}
}, updateInterval)
}
private fun stopAutoUpdate() {
isAutoUpdateEnabled = false
handler.removeCallbacksAndMessages(null)
}
private fun scanWifiNetworks() {
val results = wifiManager.scanResults
val filterText = filterEdit.text.toString().lowercase()
val filteredResults = results.filter { result ->
filterText.isEmpty() || result.SSID.lowercase().contains(filterText)
}
val networksInfo = StringBuilder()
scope.launch {
filteredResults.forEach { result ->
// Guardar medición en la base de datos
db.wifiMeasurementDao().insert(
WifiMeasurement(
ssid = result.SSID,
bssid = result.BSSID,
signalStrength = result.level
)
)
networksInfo.append("""
SSID: ${result.SSID}
BSSID: ${result.BSSID}
Intensidad de señal: ${result.level} dBm
Frecuencia: ${result.frequency} MHz
Capacidades: ${result.capabilities}
""".trimIndent())
}
scanResultsText.text = networksInfo.toString()
updateChart(filteredResults[0].SSID) // Actualizar gráfico para la primera red
}
}
private fun updateChart(ssid: String) {
scope.launch {
val measurements = db.wifiMeasurementDao().getMeasurementsForNetwork(ssid)
val entries = measurements.mapIndexed { index, measurement ->
Entry(index.toFloat(), measurement.signalStrength.toFloat())
}
val dataSet = LineDataSet(entries, "Señal de $ssid").apply {
color = ContextCompat.getColor(this@MainActivity, R.color.purple_500)
setDrawCircles(false)
lineWidth = 2f
}
signalChart.data = LineData(dataSet)
signalChart.invalidate()
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.main_menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_auto_update -> {
if (isAutoUpdateEnabled) {
stopAutoUpdate()
item.setTitle("Iniciar actualización automática")
} else {
startAutoUpdate()
item.setTitle("Detener actualización automática")
}
true
}
else -> super.onOptionsItemSelected(item)
}
}
override fun onDestroy() {
super.onDestroy()
stopAutoUpdate()
scope.cancel()
}
}