Android műszerfal mérési feladatok

A mérés során egy Android alkalmazást fog fejleszteni, ami egy felületen a jármű aktuális adatait jeleníti meg (fordulatszám, sebesség), a második felületen térképen jeleníti meg az út fontosabb adatait, és egy harmadik felületen a különböző mért értékeket teszi elérhetővé listás nézetben.

Bevezető

Az Android platform a legkönnyebben hozzáférhető mobil platform. A fejlesztés bármilyen operációs rendszeren végezhető, ingyenes fejlesztői eszközökkel. A fejlesztés nyelve Java, a legtöbb standard Java csomag implementálásra került Android-on is, így megfelelő Java tudással csak a platform-specifikus futási modellt és osztályokat kell megtanulnia a leendő fejlesztőnek. Az Android platformról egy kissé elavult nagyar nyelvű bevezető elérhető itt, vagy angol nyelven az Android Developers oldalon.

Az Android platformon több különböző térképkezelő könyvtár is elérhető, ezek közül a legelterjedtebb a Google által biztosított Goolge Maps. Ezt a Google publikusan elérhető alkalmazások fejlesztéséhez ingyenesen biztosítja, a méréshez ezt fogjuk használni. Az Android platformon charting keretrendszerek is szép számmal érhetőek el, sok közülük ingyenes. De egy olyan alapvető komponens, mint a mutatós műszer - a gépjárművek műszerfalának legfontosabb eleme sajnos még a legjobbakból is hiányzik. Ezt az eszközt az ingyenesen elérhető implementációk közül a sule.io valósítja meg a legjobban. A hivatalosan elérhető maven repo, és a github-ra feltöltött forrás is tartalmaz egy apró hibát, ez a projektben mellékelt .aar könyvtárban már javításra került.

Mérési feladatok

Virtuális gép beállításai

A virtuális gép nem kezeli megfelelően a Guest Additions-t, ezért a felbontást át kell állítani 1920x1080 ra:

xrandr -s 1920x1080

Projekt létrehozása

A fejlesztéshez az Android Studio-t fogja használni. Ennek 2016. áprilisában jelent meg a 2.1-es verziója, ez került a virtuális gépekre is. Az Android 23-as SDK-ja is telepítésre került a megfelelő kiegészítő csomagokkal.

  • Töltse le és állítsa be a mellékelt debug.keystore-t, és helyettesítse vele a beépítettet!
  • Töltse le a mellékelt .zip file-t, tömörítse ki a projektet.
  • Indítsa el az Android Studio-t (/usr/local/android-studio/bin/studio.sh)
  • Importálja a projektet! (File > New > Import Project)
  • Fordítsa le a projektet!
  • Vizsgálja a meg a projekt struktúráját!

Virtuális Android beállítása mint fordítási cél

A fejlesztett szoftver teszteléséhez egy VirtualBox-ban futó Android-ot fog használni.

Mielőtt elindítaná a virtuális gépet, győződjön meg róla, hogy a hálózat Bridge módban van. Így garantálható, hogy a fejlesztői gépről el fogja érni a virtuálisat.

  • Indítsa el a virtuális gépet.
  • Miután elindult az Android, a beállításokban (Settings > About Tablet > Status) vizsgálja meg a virtuális gép IP címét.
  • Az adb alkalmazást a /home/obd/Android/Sdk/platform-tools könyvtárban találja.
  • A fejlesztői gépen indítsa újra az adb szervert (./adb kill-server), és csatlakozzon a virtuális géphez (./adb connect x.x.x.x).
  • Listázza ki az elérhető eszközöket (./adb devices)!

Ha nincs közte a virtuális gép, akkor érdemes újraindítani az adb démont a virtuális Androidon:

su

setprop service.adb.tcp.port 5555

stop adbd

start adbd

És próbáljon meg csatlakozni ez után.

Végül indítsa el az alkalmazást, célként adja meg a csatlakoztatott Andorid eszközt! Próbálja ki a navigációt!

Amennyiben nem sikerül az alkalmazást az Android Studio-ból futtatnia, az adb segítségével parancssorból is feltelepítheti az alkalmazást. Ehhez a path-hoz hozzá kell adni a /home/obd/Android/Sdk/platform-tools könyvtárat.

Adja hozzáadta az adb-t a path-hoz.

Feltöltés előtt az APK file-t a Build-menüből lehet újragenerálni a Build APK menüelem segítségével.

Navigáljon a projektjén belül az app/build/outputs/apk mappába. Ott adja ki az adb install -r app-debug.apk

Műszerfal

Az első nézet, amit el fog készíteni, a műszerfal lesz. Ehhez a műszer komponens fogja használni.

  • Adja hozzá az .aar formátumban mellékelt műszer könyvtárat a projekthez!
    • File > New > New Module... > Import .AAR package
    • Adja hozzá a projekt build.gradle file-jának függőségeihez (dependencies) az új modult: compile project(':Name-Of-Your-Module')
  • Hozzon létre egy új nézetet (File > New > Android Resource File) adja meg az erőforrás típusát (Layout) névnek válassza azt, hogy framgent_dashboard.
  • Adja hozzá a gauge namespace-t a gyökérelemhez (LinearLayout): 
    xmlns:gauge="http://schemas.android.com/apk/res-auto"
  • A nézeten helyezzen el egy műszert (io.sule.gaugelibrary.GaugeView). Azonosítónak állítsa be hogy a "speedMeter".
<io.sule.gaugelibrary.GaugeView
    android:id="@+id/speedMeter"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:background="#ffffff"
    gauge:showOuterShadow="false"
    gauge:showOuterRim="false"
    gauge:showInnerRim="false"
    gauge:needleWidth="0.010"
    gauge:needleHeight="0.40"
    gauge:scaleStartValue="0"
    gauge:scaleEndValue="100"
    gauge:showNeedle="true" />
  • Hozzon létre egy új osztályt (jobb kattintás a hu.bme.tmit.obddashboard csomagra, majd New > Java Class) DashboardFragment néven.
  • Származtassa le az osztályt a android.support.v4.app.Fragment osztályból. Figyeljen hogy a support csomagból használja az osztályt!
  • Definiálja felül az onCreateView metódust! Használja a Ctrl+Space kombinációt, és az IDE kiegészíti a szintaktikát.
  • Használja a paraméterként kapott Inflater-t és infláljon a metódusban egy új példányt a korábban definiált nézetből az inflate metódussal.
  • Kérje el a speedMeter referenciáját a findViewById metódussal, és tárolja el egy tagváltozóban.
  • Valósítsa meg, hogy a létrehozott osztály implementálja  az OBDListener interface-t!
  • A valueChanged metódusban vizsgálja meg, hogy a kapott érték kulcsa "vehicle_speed" e. Ha igen, parsolja a kapott értéket float-tá és állítsa be az értéket a műszeren a setTargetValue metódussal.

Ahhoz hogy ki tudja próbálni a nézetet, az "Alkalmazás-logika" feladat Fragment kezelés részének elejét kell implementálnia, amikor az Activity onCreate metódusában létrehozza a DashboardFragment-et.

Érték-lista nézet

Az érték-lista nézet minden kapott paraméter legutóbbi értékét mutatja.

  • Hozzon létre nézetet, ami egy ListView-t tartalmaz!
  • Hozzon létre az egyes listaelemek számára egy másik nézetet, amin két TextView szerepel egy LinearLayout-ban!
  • Hozzon létre egy új osztályt ValuesFragment néven.
  • Származtassa le az osztályt a android.support.v4.app.Fragment osztályból. Figyeljen hogy a support csomagból használja az osztályt!
  • Definiálja felül az onCreateView metódust! Használja a Ctrl+Space kombinációt, és az IDE kiegészíti a szintaktikát.
  • Használja a paraméterként kapott Inflater-t és inláljon a metódusban egy új példányt a korábban definiált nézetből az inflate metódussal.
  • Kérje el a lista referenciáját!
  • Hozzon létre egy tagváltozót, ami egy SortedMap-ben tárol String típusú kulcsokhoz String típusú értékeket! A tagváltozót inicializálja üres map-pel az onCreateView metódusban!
  • Valósítsa meg, hogy a létrehozott osztály implementálja  az OBDListener interface-t!
  • A valueChanged metódusban vizsgálja meg, hogy a tárolt érték-lista tartalmaz-e értéket a kapott kulccsal. Ha igen, frissítse az értéket, ha nem, akkor hozzon létre újat.
  • Miután frissítette a listát, hozzon létre egy SimpleAdapter-t. Ezzel az adapterrel fogja a lista elemeit megadni. Vizsgálja meg a konstruktor paramétereit! A Context-et a  getContext metódussal érheti el. A második paramétert az elemek rendezett Map-jéből kell létrehoznia. A listaelemeket reprezentáló Map-ekben a kulcsok a "KEY" és a "VALUE", az értékek pedig a paraméter neve és a paraméter értéke. Az erőforrás-azonosító a listaelem számára létrehozott nézet azonosítója. A String tömb {"KEY", "VALUE"}, az int tömb pedig a két TextView azonosítója.
  • Állítsa be a létrehozott adapter a lista adaptereként!

Térkép nézet

A térkép nézeten a megtett utat fogja ábrázolni.

  • Származtass le a SupportMapFragment-et egy saját osztályban! A GoogleMaps újabb verziójában a szinkron hívásokat asszinkron hívások váltották föl. Ezek közül a mérés során a térkép eléréséhez és inicializálásához a getMapAsync metódust kell majd használnia.
  • A callback-ben mentsen el egy referenciát a kapott térképre, és végezze el a következő beállításokat
UiSettings settings = googleMap.getUiSettings();
settings.setAllGesturesEnabled(false);
settings.setMyLocationButtonEnabled(false);

Hozzon létre egy metódust, ami az addPolyline metódus segítségével a legutolsó ismert pontot - amennyiben létezik - összeköti a paraméterül kapott ponttal! A végén állítsa be a kamerát 10-es zoom level-re, és irányítsa az aktuális középpontra a moveCamera metódussal!

Alkalmazás-logika

Most hogy elkészültek a szükséges komponensek, be kell kötni a Fragment-eket az Activity-ben a navigációhoz, és csatlakozni kell a szolgáltatáshoz. A szükséges lépéseket az Activity forráskódjában fogja megtalálni TODO-k ként.

Fragmentek kezelése

Az Activity és a Fragment-ek életciklusát az Android keretrendszer manageli. A különbség a kettő közt, hogy Activity-t példányosítani sem szabad, viszont a Fragment-ek példányosítása dinamikus Fragment kezelés esetén a fejlesztő feladata. Ezt az onCreate metódusban, és a navigációs eseményekre reagálva kell megtenni. Figyelni kell azonban arra, hogy ne hozzon létre több példányt egy Fragment-ből, és ha a működésnek nem követelménye ne adjon hozzá több példányt a nézethez. Ennek elkerülésére kell az onCreate-ben ellenőrizni, hogy az Activity-t a rendszer állítja-e vissza (ekkor a Fragment-ek is visszaállításra kerülnek) vagy tényleges alkalazás-indításról van szó - ekkor ugyanis létre kell hozni a saját Fragment-eket.

Service csatlakozás

A felületet is módosító szolgáltatásokhoz az onResume metódusban illik csatlakozni, és az onPause metódusban elengedni a csatlakozást. Azonban ahogyan sok más elem is, a szolgáltatásokhoz való csatlakozás is asszinkron: a csatlakozás kezdeményezésének sikere után az onServiceConnected metódus tájékoztatja a szolgáltatás fogyasztóját, hogy a szolgáltatás a megadott binder-en keresztül elérhető.

Események feldolgozása

Miután az Activity feliratkozott a service eseményeire, periodikusan fog egy rögzített adathalmazból lokációt és értékeket kapni. Ezt mindig az éppen látható Fragment felé kell továbbítania. Az Android platform a Swing-hez hasonlóan egy külön szálat tart fent a felhasználó felület eseményeinek kezelésére. A felhasználói felület komponenseit csak erről a szálról lehet elérni, a háttérszálakról, csak feladatokat lehet rá ütemezni, amik akkor hajtódnak végre, amikor a felhasználói felület kezelése mellett marad szabad erőforrás. A feladat ütemezésének egyik módja a runOnUIThread metódus használata. A kapott értékeket ennek a run metódusában adhatja tovább a Fragment-nek, ahol aztán érvénybe lépnek a változások.

Műszerfal kiterjesztése

  • Adjon hozzá még két tárcsát a műszerfal layout file-jához!
  • Egészítse ki az osztályt úgy, hogy a két műszer a "torque_at_transmission" és az "engine_speed" értékeket jelenítse meg. Utóbbit 100-al le kell osztani
  • Hozzon létre a három műszerhez álló és fekvő elrendezést is!