Aplicatie Android Antifurt

Descrierea temei

Evoluția tehnologiei din ultimii ani ne-a transformat radical viața și modul în care interacționăm unii cu ceilalți. O atenție deosebită se acordă telefoanelor mobile, sau smartphone-urilor, care au ajuns astăzi la o capacitate de procesare și stocare echivalentă cu cea a unui calculator de acum nu mai puțin de 10 ani.

Această evoluție agresivă are, bîneînțeles, consecințe și în viața noastră de zi cu zi. Lucruri care în trecut aveau anumite urmări, astazi le putem privi cu alți ochi. Un exemplu îl constituie furtul unui smartphone. Cu ajutorul tehnologiei de astăzi putem prinde mult mai ușor răufăcătorul decât am fi făcut-o acum mai bine de 10 ani.

Această lucrare știînțifică își propune să prezinte o aplicație antifurt pentru dispozitive mobile pe care rulează sistemul de operare Android.

Aplicația de față a fost implementată cu gândul de a putea recupera smartphone-urile furate pe care rulează Android OS. De asemenea, aplicația este utilă și în cazul pierderii dispozitivului.

Pentru a realiza acest lucru, se trimite către telefonul furat un SMS cu un cod secret iar aplicația va trimite informații cu data, ora și locația dispozitivului mobil către un server la intervale regulate de timp.

Cuprins

Notații și abrevieri

APK – Android Application Packege

SDK – Software Development Kit

PHP – Hypertext Preprocessor

OS – Operating System

JVM – Java Virtual Machine

URL – Uniform Resource Locator

DVM – Dalvik Virtual Machine

IDE – Interactive Development Environment

GPS – Global Positioning System

DB – Database

ADT – Android Development Tools

UI – User Interface

SMS – Short Message Service

PDU – Protocol Description Unit

OȘI – Open Systems Interconnection

HTTP – Hypertext Transfer Protocol

WWW – World Wide Web

RFC – Request For Comments

MIME – Multipurpose Internet Mail Extensions

TCP – Transmission Control Protocol

OOP – Object Oriented Programming

USB – Universal Serial Bus

PDA – Personal Digital Assistant

SGBD – Sistem de Gestiune a Bazelor de Date

1. Introducere

După cum se prea bine știe, evoluția tehnologiei informației a luat un avânt considerabil odată cu terminarea celui de-al Doilea Război Mondial și începerea Războiului Rece iar rivalitatea dintre cele două mari superputeri, Statele Unite Ale Americii și Uniunea Sovietică a avut ca efect pozitiv dezvoltarea tehnologiei pe toate planurile.

În cele ce urmeaza ne vom concentra asupra apariției și evoluției calculatoarelor.

Primul calculator modern a fost ENIAC (Electronic Numerical Întegrator And Computer), proiect început în anul 1943 și terminat în anul 1946. De fapt, era o mașină Turing capabilă de a fi reprogramată pentru a rezolva o gamă largă de probleme numerice. Puterea sa de calcul era de aproximativ 5000 de operații pe secundă, extrem de mare pentru acea perioadă și totodată depășea celelalte calculatoare existente până atunci ca și putere de procesare de circa 1000 de ori. În ceea ce privește dimensiunea acestui calculator, ocupa o încăpere de dimensiuni mari.

Pași importanți în dezvoltarea calculatoarelor îi constituie apariția primului calculator Apple cu numele Apple-I în anul 1976 fiind conceput de către Steve Wozniak, co-fondator al companiei Apple Inc.

În ceea ce priveste telefoanele mobile, acestea au apărut mai tarziu.

spectaculoasă de-a lungul timpului. Dacă în cazul primelor telefoane mobile putem vorbi despre sisteme de operare simple, considerate primitive din perspectiva celor care există astăzi, acum avem de-a face cu sisteme de operare complexe, full-featured. Aceste sisteme de operare rulează pe smartphone-uri, care constituie ultima treaptă a evoluției telefoanelor mobile.

Printre sistemele de operare folosite pentru smartphone-uri de-a lungul timpului putem da ca exemple:

Symbian, 2000, primul SO pentru smartphone-uri, apărut odată cu lansarea telefonului Ericsson R380.

BlackBerry OS, 1999, iar primul spartphone realizat de către BlackBerry Inc a apărut în anul 2002.

iOS, 2007, SO realizat de către Apple Inc

Android, 2008, Open Handset Alliance

Google este o corporație multinațională americană specializată pe produse și servicii care cuprind aria de activitate din domeniul Internetului. Aici includem publicitatea online, căutarea, cloud computing și software-ul. Cea mai mare parte a profitului este obținută din AdWords., un serviciu de publicitate online care plasează reclamele lângă rezultatele căutarii pe motorul de căutare.

Primul telefon mobil a fost realizat de către Motorola în anul 1973 iar prima rețea de telefonie mobilă a fost lansată în Japonia de către NTT (Nippon Telegraph and Telephone) în anul 1979. Doi ani mai târziu, acest sistem a fost lansat și în țări precum Danemarca, Suedia, Norvegia și Finlanda de către NMT (Nordic Mobile Telephone). Mai târziu, și alte țări le-au urmat exemplul.

În ceea ce privește sistemul de operare al telefoanelor mobile, acestea au avut o evoluție

Compania a fost fondată pe data de 4 septembrie 1998 de către Larry Page și Sergey Brin în timpul doctoratului lor la Universitatea Stanford. A fost înregistrată în California, Statele Unite ale Americii și totodată a fost deschis un nou cont bancar în numele companiei nou create.

Android este un sistem de operare pentru telefoane mobile care are la bază o versiune modificată a kernel-ului de Linux, pentru eficiența rulării pe un dispozitiv cu resurse limitate de energie, putere de procesare și memorie și care momentan este dezvoltat de către Google. Acesta este proiectat în special pentru dispozitive mobile cu touchscreen-uri, cum ar fi smartphone-urile, tabletele, iar versiuni modificare ale acestui OS pot fi aplicate și pe televizoare (Android TV), autoturisme (Android Auto) sau ceasuri de măna (Android Wear).

Acest OS a fost dezvoltat initial de către Android Inc. iar primul release a avut loc pe 23 septembrie 2008.

În iulie 2005 Google Inc a achiziționat Android Inc., tranzacție estimată la circa 50 de milioane de dolari.

Android OS se află sub Incidența unor licențe open-source, Apache License 2.0 iar pentru modificările kernel-ului avem licența GNU GPL v2. Prin urmare, acest sistem de operare poate fi dezvoltat de oricine dorește, atât timp cât respectâ termenii și condițiile licențelor menționate anterior.

Core-ul sistemului de operare este scris în limbajele C și C++, iar interfața grafică în Java. Kernel-ul este monolitic, fiind de fapt un kernel Linux modificat.

Versiunile de Android sunt denumite dupa dulciuri și deserturi (excepție fac primele 2 release-uri).

O scurtă istorie a versionarii Android este următoarea:

1.1 Alpha

1.2 Beta

1.5 Cupcake

1.6 Donut

2.0 Eclair

2.2 Frozen Yogurt („Froyo”)

2.3 Ginger Bread

3.0 Honeycomb

4.0 Ice Cream Sandwich

4.1 Jelly Bean

4.4 KitKat

5.0 Lollipop

2. Alegerea platformei

Alegerea tehnologiilor utilizate în cadrul proiectului de licență a fost motivată de mai mulți factori.

În primul rand, popularitatea de care se bucură sistemul de operare Android pe piață, rulând pe aproximativ 1,1 miliarde de dispozitive în anul 2014, lucru reflectat și în cota de acestuia piață de aproximativ 81,5% precum și dezvoltarea intensă a acestuia de către Google Inc. care îl face un sistem de operare stabil și modern. De asemenea update-urile dese care îi sunt trimise pentru protecție și rezolvarea bug-urilor din sistem pentru a-l aduce la zi cu software-ul îl fac un SO stabil și sigur.

Alt motiv este dat de faptul că dezvoltarea aplicațiilor Android se realizează cu ajutorul limbajului Java, care deține și acesta o cotă importantă din piață. Multe aplicații, pe lânga cele din Android sunt scrise în acest limbaj. De asemenea, Java este larg răspândit în cadrul aplicațiilor din segmentul financiar, web, al prelucrării datelor de dimensiuni foarte mari, al sistemelor tranzacționale sau al aplicațiilor știintifice.

Un avantaj extrem de important al limbajului Java îl constituie portabilitatea.

Cererea de programatori cu experiență în Java, precum și celor cu un background solid în scrierea aplicațiilor mobile m-a determinat să aleg aceste tehnologii pentru proiectul meu de licență.

Luând în considedare că atât limbajul Java, cât și Android OS-ul au o parte de o dezvoltare rapidă, ceea ce le face unele dintre cele mai moderne tehnologii de pe piață, mi-a întărit și mai mult convingerea enunțată mai sus.

În ceea ce privește ideea aplicației în sine, am considerat necesar să existe o modalitate cât mai simplă și mai silențioasă ca o persoana căreia i-a fost furat smartphone-ul să-l poată recupera și ca autorul faptei sa fie găsit. În acest mod, deținătorul de drept al dispozitivului mobil îi poate identifica locația în timp real.

Pentru a detalia cât mai clar motivația mea de a alege aceste tehnologii pentru lucrarea de licență, voi prezenta în cele ce urmează câteva caracteristici atât pentru aistemul de operare Android, cât și ale limbajul Java.

Limbajul Java a fost creat de Sun Microsystems Inc. iar prima versiune a acestuia a apărut în anul 1995. Ultima versiune stabilă în momentul scrierii documentului de față, 1.8.0_45, a fost lansată pe 14 aprilie 2015.

Mai târziu, compania Sun Microsystems a fost achiziționată de către Oracle Inc. iar în momentul de față dezvoltarea limbajului Java este realizată de către cea din urmă.

Limbajul a fost scris în C și C++ iar licențele sub jurisdicția căroră intră sunt GNU General Public License și Java Community Process.

Un avantaj major al acestui limbaj de programare, spre deosebire de multe altele de pe piață, îl constituie portabilitatea, ceea ce înseamnă faptul că programe scrise în Java pot rula pe orice arhitecturi sau sisteme de operare care conțin suport pentru runtime. Acest lucru funcționează în felul următor: codul Java este compilat și se obține un cod intermediar, numit Java bytecode și nu cod mașină specific arhitecturii sistemului pe care e compilat programul. Instrucțiunile din Java bytecode sunt asemănătoare codului mașină, dar sunt destinate și executate de o mașină virtuală JVM) scrisă special pentru hardware-ul sistemului.

Java este un limbaj de programare orientat-obiect. OOP-ul reprezintă o metodă de programare în care un program este privit ca având obiecte care interacționează unele cu celalalte prin intermediul acțiunilor.

Utilizatorii finali folosesc Java Runtime Environment (JRE) instalat pe mașinile fizice pentru aplicații Java de sine stătătoare sau aplicații Java web (de exemplu, applet-uri).

Platforma Android are la bază Java și permite folosirea feature-urilor versiunilor 6 și 7 ale acestui limbaj de programare, utilizează o librarie standard diferită – Apache Harmony, un bytecode și o mașină virtuală diferite și este proiectat pentru dispozitive cu memorie redusă precum smartphone-uri sau tablete.

Google și Android Inc. au căzut de acord asupra faptului de a folosi limbajul Java ca și limbaj fundamental pentru Android OS. Deși sistemul de operare este construit peste kernel-ul de linux și scris în cea mai mare parte în C, Android SDK-ul utilizează limbajul Java pentru aplicațiile Android.

Cu toate acestea, Android nu utilizează Java Virtual Machîne, în schimb folosește bytecode-ul Java ca un pas intermediar pentru a fi rulat pe Dalvik virtual machine.

Proiectul meu de licență a fost dezvoltat sub Eclipse Luna IDE, versiunea 4.4.1, iar pentru lucrul efectiv pe platforma Android am instalat plugin-ul Android ADT precum și Android SDK-ul.

În funcție de versiunea Android OS-ului căruia îi este destinată aplicația, trebuie instalat din SDK Manager API-ul corespunzător. În cazul meu, aplicația este destinată versiunii 4.4.x (KitKat) iar API-ul aferent are numărul 19. Motivația alegerii acestui API și implicit a acestei versiuni este dată de smartphone-ul meu, debugging-ul fiind mult mai ușor când aplicația este testată pe un telefon fizic decât pe un emulator.

În ceea ce privește site-ul web unde vor fi trimise datele de către aplicație în cazul furtului telefonului mobil, mi-a fost recomandat un site – http://www.000webhost.com care permite creerea și administrarea gratuită a unui subdomeniu, lucru de care aveam nevoie.

Site-ul personalizat este http://george2015.net76.net iar acesta cuprinde partea de server a aplicației mele. Acest site nu are interfață grafică, singura funcționalitate fiind aceea de a primi, parsa și a popula baza de date cu informații primite de la aplicație.

Partea de server cuprinde două componente principale: scriptul php (cu numele default.php) care parsează cererile HTTP GET primite, le verifică informațiile conținute, și populează baza de date corespunzător iar baza de date care va conține informațiile primite anterior.

Am ales limbajul php deoarece este foarte popular, stabil, este distribuit cu o licență open-source și îmi este foarte familiar.

Baza de date este MySQL, iar motivele pentru care am ales-o sunt asemănătoare cu cele pentru care am ales limbajul de scripting php și datorită faptului că acestea două sunt foarte des folosite împreună.

Experiența din cadrul facultății cu acest limbaj de scripting precum și cu acest SGBD m-au determinat să le aleg pentru proiectul de licență.

3. Noțiuni teoretice

Limbajul Java este foarte popular în aria aplicațiilor web. Cu toate aceastea, este privit de cele mai multe ori ca un limbaj de programare de uz general. În anul 1991, odată cu apariția primei versiuni a acestui limbaj, care înca nu purta denumirea de Java, a fost proiectat pentru aparate de uz casnic, precum mașini de spălat sau televizoare.

În general, un program Java este tradus într-un limbaj intermediar care este identic pentru toate dispozitivele (sau toate calculatoarele), după care un program mic, ușor de scris, traduce acest limbaj intermediar în limbajul mașină aferent calculatorului pe care rulează. Acest limbaj intermediar se numește Java byte-code sau mai simplu, byte-code.

După cum am menționat și anterior, Java este un limbaj orientat-obiect. Prin urmare, este format din entitați care sunt denumite, nu surprinzător, obiecte.

Aceste obiecte sunt capabile să realizeze acțiuni. Aceste acțiuni se numesc metode. Obiectele de același fel mai sunt numite și obiecte care aparțin aceleiași clase.

În Java, toate metodele aparțin unei clase. Un program simplu în acest limbaj reprezintă o clasă cu o metodă main iar când programul este rulat, sistemul de run-time apelează automat metoda main.

Ca și deosebire față de C++, în ceea ce privesțe noțiunile mai avansate de programare orientate pe obiect, o constituie absența moștenirii multiple. Pe de altă parte, putem simula moștenirea multiplă cu ajutorul interfețelor sau a claselor abstracte.

În linii mari, există două tipuri de programe Java: applet-uri și aplicații. O aplicație, denumită și program aplicație, reprezintă de fapt un program standard. Acestea sunt destinate să ruleze pe un calculator, la el ca orice alt program.

Pe de altă parte, un applet reprezintă o aplicație a cărei destinație este un browser web, deci poate fi trimisă către o altă locație din Internet și rulată acolo.

În această secțiune voi descrie componentele unei aplicații Android precum și partea teoretică ce stă în spatele acesteia.

Primul lucru cu care se începe în momentul în care se dorește a se scrie o aplicație Android este creerea unui nou proiect. Pentru a crea un nou proiect se selectează din IDE: File -> New -> Android Application Project. Se completează numele proiectului după care se alege versiunea API-ului în câmpurile: Minimum Required SDK, Target SDK și Compile With. În secțiunea “Theme” se alege tema dorită. După ce s-au completat toate câmpurile, butonul “Next>” va fi activat. În următoarele ferestre, dacă se doresțe să fie păstrate setările prestabilite, se va apăsa “Next>” până la final după care se apasă “Finish”.

Înainte de a trece mai departe, trebuie explicată o noțiune fundamentală din Android și anume Activitatea.

O activitate reprezintă o fereastră care conține interfața grafică a aplicației și cu care utilizatorul interacționează. O aplicație Android poate avea zero sau mai multe activități.

În funcție de dimensiunile și rezoluția ecranului dispozitivului pe care va rula aplicația, pentru a gestiona spațiul cât mai eficient, o activitate poate fi împarțită în mai multe subunități denumite fragmente.

Pentru a crea o activitate se declară o nouă clasă Java care extinde clasă de bază Activity. Secvența urmatoare de cod reprezintă o activitate simplă. Codul este extras din conținutul clasei NetworkingActivity.java și a fost generat automat la crearea proiectului Android.

import android.app.Activity;

import android.os.Bundle;

public class NetworkingActivity extends Activity {

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

}

}

Această clasă încarcă componentele UI din fișierul XML care este localizat în folder-ul res/layout. În exemplul de mai sus, fișierul se numește main.xml și este încărcat mai precis la linia:

setContentView(R.layout.main);

Fiecare activitate din cadrul aplicației trebuie declarată în AndroidManifest.xml. În urmatorul exemplu de cod XML avem declararea propriu-zisa a activitații între tag-urile <activity> și </activity>

<activity

android:name=".NetworkingActivity"

android:label="@string/app_name" >

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

Trebuie menționat faptul că în momentul lansării în execuție a aplicației Android, există întotdeauna o activitate principală.

Android OS-ul permite ca o activitate să pornească o altă activitate iar în acest caz, cea de-a doua activitate spunem că va fi secundară. Altă activitate este pornită pentru a realiza anumite task-uri. Acest lucru se realizează prin intermediul unei intenții. În cazul unui astfel de scenariu, prima activitate este salvată pe stivă după care cea de-a doua activitate este pornită.

În continuare vom defini alt concept fundamental în Android, și anume, Intenția.

În linii mari, o intenție reprezintă o abstractizare a unei operații ce trebuie executată. O intenție poate lansa o nouă activitate prin apelul metodei startActivity() sau lansa, de exemplu, un mesaj de broadcast prin intermediul apelului metodei broadcastIntent().

Principalele componente ale unei intenții sunt:

acțiunea

informația

3.1 Anatomia unei aplicații Android

Din ierarhia prezentată in Fig.1 de mai jos, în fereastra Package Explorer din Eclipse, se poate observa mai multe foldere. O scurtă descriere a celor mai importante dintre acestea este:

src

conține sursele cu extensia .java.

sursele Java sunt listate sub pachetul aferent proiectului

gen

conține fișierul important R.java, acesta fiind generat automat și care face reține referințe către toate resursele din proiect

Android 4.4.2

conține doar fișierul android.jar în care sunt incluse toate librăriile cu extensia .class necesare unei aplicații Android

bin

conține toate fișierele generate de către ADT în timpul procesului de build.

res

conține toate resursele folosite în aplicație.

AndroidManifest.xml

aici sunt specificate permisiunile aplicației, precum și alte componente

va fi explicat în detaliu ulterior

Fig. 1

În fișierul main.xml sunt definite componenele grafice ale activitații care o invocă.

În cadrul acestui fișier, un buton simplu este generat de următoarea secvență:

<Button

android:id="@+id/saveButton"

android:onClick="save"

android:text="@string/save" />

Dacă ne uităm cu atenție la ultima linie îngroșată din secvența de mai sus, aceasta face referire către fișierul strings.xml din res/values. Prin urmare, „@string/save” face referire către string-ul cu tag-ul „save” din strings.xml, care conține textul „Save”. În cazul de față, acesta este:

<string name="save">"Save"<string>

Este recomandat ca toate constantele de tip String folosite în aplicație să fie stocate în strings.xml și să fie referite de aici. În acest mod, dacă situația o va cere și aplicația va trebui localizată în alta limbă, singurul lucru de făcut va fi copierea întregului folder „values” și modificarea fișierului strings.xml astfel încât să conțină cuvintele în limba dorită.

Un fișier foarte important într-un proiect Android este AndroidManifest.xml căruia îi vom acorda o atenție specială.

Un exemplu de conținut al acestui fișier este:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.example.networking"

android:versionCode="1"

android:versionName="1.0" >

<uses-sdk

android:minSdkVersion="19"

android:targetSdkVersion="19" />

<uses-permission android:name="android.permission.RECEIVE_SMS" />

<application

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme" >

<receiver android:name="SMSReceiver">

<intent-filter>

<action android:name="android.provider.Telephony.SMS_RECEIVED" />

</intent-filter>

</receiver>

<activity

android:name=".NetworkingActivity"

android:label="@string/app_name" >

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

</application>

</manifest>

Fișierul AndroidManifest.xml conține mai multe informații despre aplicație:

Definește numele pachetului aplicației (în cazul de față com.example.networking)

Numărul versiunii aplicației este 1 (atributul android:versionCode). Această valoare este folosită pentru a identifica numărul versiunii aplicației respective. În acest mod putem afla când o aplicație are nevoie de upgrade.

Numele versiunii aplicației este “1.0” (atributul android:versionName). Respectă formatul <major>.<minor>.<point>. Rolul ei este de a afișa utilizatorului numărul versiunii.

Atributul android:minSdkVersion al tag-ului <uses-sdk> face referire la cea mai veche versiune a sistemului de operare pe care aplicația poate rula.

Pictograma aplicației este imaginea cu numele ic_launcher localizată în folder-ul drawable.

Numele aplicației este dat de string-ul numit app_name definit în strings.xml

În momentul acesta există o singura activitate în cadrul proiectIului – NetworkingActivity.java

Tot aici se definesc și clasele ascultător sub tag-urile <receiver>. În cazul nostru avem listener-ul SMSReceiver.

Permisiunile constituie o componentă foarte importantă a acestui fișier. În exemplul de mai sus, solicitam permisiunea de a citi mesajele text primite.

3.2 Persistența datelor în Android

În linii mari, în Android, există patru modalitați de a realiza persistenta datelor:

Metoda shared preferences

Salvarea în memoria internă

Salvarea în memoria externă

Un sistem de management al bazelor de date prin suportul adus de SQLite

3.2.1 Metoda shared preferences

Platforma Android furnizează un obiect al clasei SharedPreferences pentru salvarea datelor simple care nu necesită multă memorie pentru stocare. Utilizarea acestui obiect este ideală în momentul în care datele constau din perechi cheie-valoare. Se pot salva și date primitive precum: boolean, int, long dar și obiecte de tip String. Datele vor ramâne stocate chiar dacă aplicația este închisă iar acestea sunt salvate într-un fișier XML.

Se apelează una din următoarele două metode pentru a obține un obiect de tipul SharedPreferences:

getPreferences() – utilizată când este necesar doar un singur fișier de stocare în cadrul proiectului. Din acest motiv, nu ii trebuie asignat un nume.

getSharedPreferences() – utilizată în momentul în care sunt necesare mai multe fișiere de stocare identificate prin nume, fiind dat ca parametru funcției.

Pașii pentru a scrie datele:

Se apelează metoda edit() pentru a se obține un obiect SharedPreferences.Editor

Se adaugă valori prin metodele oferite de acest obiect. Exemplu: putBoolean(), putString().

Se comit noile modificări prin apelul funcției commit().

Pentru a citi valorile, se utilizează funcții precum: getBoolean(), getString().

3.2.2 Salvarea în memoria internă

În timp ce un obiect de tipul SharedPreferences permite salvarea unor date care sunt cel mai bine memorate în perechi cheie-valoare, uneori este necasar ca datele să fie salvate în sistemul de fișiere. Un exemplu adecvat în acest sens îl constituie salvarea textelor de dimensiune mare. În cazul acesta se recomandă salvarea datelor în sistemul de fișiere.

Pentru a realiza acest lucru se pot folosi clasele din pachetul java.io.*.

Înainte de a lua decizia de a salva datele în memoria înternă, trebuie sa se țină cont de următoarele lucruri:

În mod implicit, fișierele salvate sunt accesibilie doar din aplicația care le-a generat

Memoria interna este disponibila mereu

În momentul dezinstalării aplicației, sistemul de operare va șterge toate fișierele pe care aplicația le-a creat în memoria internă

Având în vedere cele menționate anterior, salvarea datelor în memoria internă a telefonului este utilă în momentul în care se dorește ca atât utilizatorul, ca și alte aplicații să nu aiba acces la fișierele create.

3.2.3 Salvarea în memoria externă

Această modalitate de salvare a datelor se pretează în cazul unor dimensiuni foarte mari a acestora. Nu există o limită care odata depașită să fie recomandată aceasta modalitate de stocare. În principiu, este la latitudinea programatorului când alege această metodă în defavoarea stocării interne. Un prim factor prin care această modalitate de stocare este preferată se datorează capacitații mult mai mare de stocare a cardurilor SD în comparație cu memoria internă a telefonului. Un alt avantaj al acestei metode de salvare a datelor îl constituie posibilitatea de a partaja datele cu alți utilizatori prin simplul fapt de a scoate cardul SD din telefon și a-l adăuga altui telefon.

Constituie modalitatea cea mai bună de a salva date care nu necesită restricții în ceea ce privește drepturile de acces și care se doresțe a fi partajate cu alte aplicații.

Înainte de a se lua decizia de a salva datele în memoria externă, trebuie luate în considerare următoarele lucruri:

Este posibil ca datele să nu fie disponibile tot timpul. Un caz în acest sens îl constituie scenariul în care memoria externă este montată pe un card SD după care este extrasă din dispozitiv.

În cazul scenariului anterior, datele pot fi citite de oricine intră în posesia cardului, deci nu există niciun control asupra celor care pot intra în posesia informațiilor stocate.

Un lucru important pentru a utiliza această metodă de salvare a fișierelor îl constituie

obținerea permisiunilor necesare. Trebuie cerută permisiunea WRITE_EXTERNAL_STORAGE în fișierul AndroidManifest.xml în felul urmator:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

3.2.4 Crearea și utilizarea bazelor de date

Cele trei metode prezentate anterior se pretează în situația în care dorim să salvăm date simple. Experiența de zi cu zi ne-a arătat că există situații în care informația este structurată relațional, iar în acest caz este utilă o bază de date pentru a o stoca. De exemplu, dacă se doresțe salvarea notelor tutoror studenților Facultății de Automatică și Calculatoare, este mult mai eficientă utilizarea unei baze de date deoarece aceasta poate fi interogată pentru a se obține rezultatele unui anumit student sau alte informații despre acesta. În plus, folosirea unei baze de date impune specificarea unor relații între diferite seturi de date având ca rezultat integritatea acestora.

Android folosește baze de date SQLite iar o motivație din spatele acestei alegeri o reprezintă dimensiunea redusă a acesteia.

Baza de date creată pentru o aplicație poate fi folosită doar de aplicația care a creat-o.

Pachetul ce trebuie adăugat proiectului pentru a folosi această modalitate de stocare a datelor este android.database.sqlite

3.3 Clasa BroadcastReceiver

O clasă definită de către programator care extinde clasa de bază BroadcastReceiver permite acesteia să asculte diferite evenimente din sistem. Aceste evenimente pot fi specifice sistemului (de exemplu, primirea unui mesaj text) sau trimise din alte zone ale aplicației prin intermediul apelulului metodei sendBroadcast(). În momentul în care un eveniment este primit de către o clasă ascultător, se apelează metoda suprascrisă onReceive().

Pentru a asculta evenimente de un anumit tip, o instanță a unei clase care extinde BroadcastReceiver poate fi înregistrată în doua moduri: dinamic, prin apelul metodei Context.registerReceiver() sau static prin adăugarea numelui clasei între tag-urile <receiver> si </receiver> în fișierul AndroidManifest.xml

În cadrul proiectului meu, am utilizat ambele metode pentru a înregistra clase de tip receiver. Un exemplu de clasă receiver din proiectul meu este SMSReceiver iar înregistrarea acesteia în AndroidManifest.xml am realizat-o în felul următor:

<receiver android:name="SMSReceiver">

<intent-filter>

<action android:name="android.provider.Telephony.SMS_RECEIVED" />

</intent-filter>

</receiver>

Acțiunea ascultată de acest receiver se adaugă în tag-ul <action> și în cazul de față se numește SMS_RECEIVED.

Un avantaj al claselor derivate din BroadcastReceived îl constituie faptul că acestea ascultă evenimente chiar dacă aplicația nu rulează în fundal sau nu este vizibilă utilizatorului.

3.4 Tratarea mesajelor text primite

Deoarece un eveniment foarte important în cadrul aplicației mele îl constituie ascultarea mesajelor text primite, voi acorda o atenție tratării SMS-urilor primite în cadrul aplicației.

Pentru a asculta un mesaj text primit se creează o clasă care extinde clasa de bază BroadcastReceiver și i se adaugă evenimentul SMS_RECEIVED ca fiind cel care îl ascultă.

De fiecare dată când telefonul mobil primește un mesaj text, se apelează metoda onReceive() a clasei nou create.

public void onReceive(Context context, Intent intent ) { … }

Textul SMS-ului primit este stocat în obiectul intent al metodei de mai sus.

Fiecare SMS este memorat în format PDU într-un vector de obiecte de tip Object. În cazul în care SMS-ul conține mai puțin de 160 de caractere, atunci vectorul va avea un singur element. Dacă SMS-ul are mai mult de 160 de caractere, atunci mesajul text va fi împarțit în mai multe component și memorat în câte o intrare din vector.

Pentru a extrage conținutul unui SMS se utilizează metoda statică createFromPdu() a clasei SmsMessage.

Apelul metodei getOriginatingAddress() returnează un obiect de tip String care reprezintă numărul de telefon al expeditorului mesajului text. În cazul în care acesta nu poate fi obținut, functia va întoarce null. Numărul de telefon obținut, în caz de succes, conține și prefixul internațional al țării expeditorului.

În final, pentru a extrage textul propriu-zis al mesajului text se apelează metoda getMessageBody() care returnează un String în caz de succes, iar null în caz contrar.

3.5 Execuția unui task asincron

Pentru a creea un task asincron se declară o clasă interioară care extinde clasa de bază AsyncTask. Această clasă de bază permite execuția în fundal a unor secvențe de cod fără a fi necesară utilizarea thread-urilor. Afișarea rezultatelor se realizează în thread-ul UI.

Este recomandat a se utiliza o clasă derivată din AsyncTask atunci când avem de efectuat operații ce nu necesită un timp îndelungat de execuție – maxim câteva secunde ar fi ideal.

În exemplul de mai jos, clasa sendDataToServerTask extinde clasa de bază AsyncTask și specifică cele 3 tipuri generice, care în cazul de față sunt String, Void și String:

private class sendDataToServerTask extends AsyncTask<String, Void, String>

În momentul în care clasa AsyncTask este derivată, noua clasă trebuie să suprascrie următoarele metode utilizând tipurile generice menționate anterior:

doInBackground() – această metodă primește un vector de obiecte aferente primului tip generic definit anterior. În cazul de față, acesta este String. Codul din această metodă este executat în cadrul thread-ului din fundal. Pentru a oferii informații cu privire la progresul task-ului creat, se apelează metoda publishProgress() care la rândul ei apelează onProgressUpdate(). Tipul returnat de către această metodă corespunde celui de-al treilea tip generic definit anterior. În cazul nostrum acesta este String.

onProgressUpdate() – aceasta metodă este apelată în thread-ul UI și în metoda publishProgress(). Ca și parametru formal are un vector de tipul corespunzător celui de-al doilea tip generic – Void. Este folosită pentru a informa utilizatorul cu privire la progresul acțiunii din fundal.

onPostExecute() – este apelată în thread-ul UI în momentul în care metoda doInBackground() și-a terminat execuția. Acceptă un argument din cel de-al treilea tip generic definit – String.

3.6 Trimiterea unei intenții

Exista situații în care este necesar ca unele componente ale aplicației să fie informate cu privire la producerea unui eveniment.

Pentru a realiza acest lucru, se creează un obiect al clasei Intent în care se specifică acțiunea care va fi trimisă. Pentru a trimite date către clase de tip ascultător, trebuiesc specificate atât datele cât și tipul lor.

Android OS-ul va identifica clasele ascultător care asculta astfel de evenimente și le va notifica.

Această modalitate de partajare a informației în sistem este foarte folosită deoarece este foarte rapidă și ușor de implementat și de înțeles.

Clasele ascultător pentru evenimente create de către dezvoltator trebuie definite, așa cum am menționat și anterior, în AndroidManifest.xml, unde se va specifica numele acțiunii/acțiunilor pe care le ascultă. Această modalitate poartă denumirea de înregistrare statica. Exista și posibilitatea de a le înregistra dinamic în cadrul fișierelor sursă.

În secvența următoare de cod se poate vedea un exemplu de creere a unei intenții, setarea unei acțiuni pentru aceasta, precum și trimiterea ei într-un mesaj de broadcast.

Intent broadcastIntent = new Intent();

broadcastIntent.setAction(“my_action”);

context.sendBroadcast(broadcastIntent);

Pentru a înregistra o clasă ascultator care va fi notificată de catre sistemul de operare la primirea unui eveniment de tipul “my_action”, trebuie sa aibă specificat în AndroidManifest.xml (în cazul în care se dorește o declarare statică a acesteia) numele acțiunii.

<receiver android:name="MyReceiver">

<intent-filter>

<action android:name="android.provider.Telephony.my_action " />

</intent-filter>

</receiver>

3.7 Localizare

Indiscutabil, localizarea reprezintă una dintre cele mai importante facilitați ale sistemului de operare Android. Evoluția tehnologiei în general a permis ca locația să poată fi obținută într-un mod foarte simplu.

Fără îndoială, experiența utilizatorului este foarte mult îmbunătățită și pot obține foarte multe facilitate în momentul în care utilizatorul are acces la coordonatele sale. Un exemplu în acest sens sunt afișarea direcțiilor către o anumită destinație.

Localizarea se poate realiza prin mai multe metode, fiecare având propriile avantaje și dezavantaje:

Echipamente GPS. Având în vedere numărul mare de sateliți care orbitează în jurul Pământului, precum și dotarea majorității smartphone-urilor și tabletelor cu GPS, se poate folosi această metodă pentru a determina locația curentă a dispozitivului.

Dezavantajul acestei metode îl constituie faptul că se pretează în cazul locațiilor exterioare, când cerul este senin. Folosirea acestei metodei de localizare în imobile sau tuneluri nu furnizează o locație exactă deoarece semnalul sateliților nu pătrunde în astfel de cazuri.

Triangularea folosind celulele de telefonie mobile. Această metodă se bazează pe faptul că un smartphone se află în permanență conectat la celulele de telefonie mobila aferente operatorului cartelei SIM. Cunoscând locația exactă a acestor antene, precum și a intensitații semnalului de la fiecare, se poate aplica algoritmul de triangulare pentru a se determina locația dispozitivului. Avantajul acestei metode constă în utilizarea redusă a bateriei dispozitivului, resursă extreme de importantă. Pe de alta parte, nu oferă exactitatea metodei GPS deoarece intensitatea semnalulului poate varia foarte mult. Un alt dezavantaj al acestei metode îl constituie blocarea utilizarii API-ului din Android pentru culegerea informațiilor de la celulele de telefonie mobilă pentru determinarea locațîei. Un exemplu de producător este Samsung. Această metoda este foarte utilă în spațiile dens populate, cum ar fi orașele mari sau metropolele unde sunt foarte multe celule de telefonie mobilă prezente.

Triangularea Wi-Fi. Diferența dintre această metodă și cea prezentată anterior constă în faptul că dispozitivul se conectează la o rețea Wi-Fi și iși determină locația interogând bazele de date ale provider-ului. Dintre cele trei metode prezentate, aceasta este cea mai puțin precisă.

Sistemul de operare Android oferă dezvoltatorilor posibilitatea de a obține locației prin intermediul clasei LocationManager. Pentru a avea acces la datele și metodele acestei clase, se include în proiect pachetul android.location.*.

Clasa LocationManager furnizează mai multe informații cu privire la locație, printre cele mai importante fiind latitudinea, longitudinea, timestamp-ul, altitudinea și viteza. Pentru obține și stoca aceste date, trebuie creat un obiect al clasei Location.

Primul pas pentru a folosi API-ul pus la dispoziție de Android pentru a determina locația este de a obține o referință către clasă LocationManager prin intermediul apelului metodei getSystemService().

LocationManager locManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);

Pentru a determina ultima locație se apelează metoda getLastKnownLocation() a clasei LocationManager care are urmatoarea semnatură:

Location android.location.LocationManager.getLastKnownLocation(String provider)

Parametrul formal al funcției de mai sus poate lua, spre exemplu, valoarea LocationManager.NETWORK_PROVIDER, caz în care se va afla ultima locație furnizată de către operatorul de date mobile sau LocationManager.GPS_PROVIDER.

Printre datele de interes major care pot fi obținute se numară latitudinea, longitudinea și timestampu-ul locațîei. Acestea se pot obține prin apelul metodelor getLatitude(), getLongitude() și getTime() a clasei Location.

De cele mai multe ori se dorește aflarea locației actuale, folosindu-se metoda requestLocationUpdates() cu urmatoarea semnatură:

void android.location.LocationManager.requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener)

3.8 Servicii în Android

Un serviciu este definit ca o aplicație care rulează în fundal și cu care utilizatorul nu interacționează.

Un exemplu de serviciu îl constituie ascultarea unei melodii. În timp ce spre exemplu, utilizatorul navighează pe Internet, o melodie poate fi redată în fundal fără ca aceasta să interacționeze cu utilizatorul. În cazul acesta, spunem că muzica rulează ca un serviciu. Un alt exemplu îl constituie aplicațiile care culeg date despre modul în care utilizatorul folosește dispozitivul pentru o mai bună experiență a acestuia. Acestea rulează în fundal și colectează datele necesare fără a fi nevoie ca utilizatorul să interacționeze cu ele.

Serviciile sunt utile în momentul în care nu dorim să afișăm o ferestră utilizatorului.

Pentru a defini un serviciu se creeaza o nouă clasă care extinde clasă de bază Service.

public MyService extends Service { … }

În noua clasă trebuie suprascrise trei metode ale clasei de bază:

public IBinder onBind(Intent arg0) { … } . Aceasta metoda leagă o activitate de un serviciu și permite activității să acceseze datele și metodele din interiorul serviciului.

public int onStartCommand(Intent intent, int flags, int startId) { … }. Această metodă este apelată în momentul în care serviciul este pornit prin apelul funcției startService(). Codul care va rula în fundal în cadrul serviciului va fi adăugat în aceasta metodă. Tot aici se returnează constanta START_STICKY pentru ca serviciul să ruleze până în momentul în care este oprit explicit.

public void onDestroy() { … }. Este apelata când serviciul este oprit prin apelul metodei stopService().

Toate serviciile create trebuie declarate în AndroidManifest.xml în felul următor:

<service android:name=".MyService">

Dacă se dorește ca serviciul să fie accesibil și altor aplicații, atunci în AndroidManifest.xml se va adaugă un filtru cu o acțiune precum:

<service android:name=".MyService">

<intent-filter>

<action android:name="com.example.MyService " />

</intent-filter>

</service>

Pentru a porni un serviciu, folosim metoda startService() iar pentru a-l opri, stopService():

startService( new Intent(getBaseContext(), MyService.class) );

stopService( new Intent(getBaseContext(), MyService.class) );

3.9 Invocarea serviciilor web folosind protocolul HTTP

HTTP este un protocol de nivel aplicație pentru sisteme distribuite, colaborative și hypermedia. Este folosit de către World Wide Web înca din anul 1990. Prima versiune a purtat numele de HTTP/0.9 și reprezenta un protocol simplu pentru transferul datelor în Internet. HTTP/1.0 a fost standardizat în cadrul RFC 1945, îmbunătățind protocolul prin permiterea mesajelor în format MIME, conținand metainformații despre datele transferate și modificatori în ceea ce privește cererile și răspunsurile. Cu toate acestea, HTTP/1.0 nu lua în considerare suficient de mult efectele ierarhiei serverelor proxy, a caching-ului, nevoia pentru conexiuni persistente sau sistemele virtuale. Aceste dezavantaje au fost eliminate în cadrul ultimei versiuni de HTTP 1.1 standardizată în cadrul RFC 2616.

Având în vedere răspandirea protocolului HTTP și luând în considerare și evoluția smartphone-urilor din ultimii ani, ținând cont și de faptul ca au acces la Internet, fie prin intermediul serviciilor de date mobile oferite de către operatorul de telefonie mobilă, fie prin intermediul rețelelor Wi-Fi, comunicația între dispozitiv și un server web găzduit în Internet este mult mai facilă. Se pot efectua o multitudine de operații, printre care descărcarea unor pagini web de pe un server, descărcarea datelor binare sau chiar transmiterea datelor către un server.

Un alt motiv al răspandirii acestui protocol îl constituie folosirea protocolului TCP pentru a realiza o conexiune persistentă între un client și un server.

O condiție obligatory pentru a utiliza protocolul HTTP este de a obține acces la Internet, iar această permisiune trebuie cerută în AndroidManifest.xml prin:

<uses-permission android:name="android.permission.INTERNET" />

Pentru a avea acces la API-ul de lucru cu HTTP-ul din Java, trebuie incluse pachetele java.net.* și java.io.*.

Cele doua concepte fundamentale pe care este bazat protocolul HTTP sunt cererea și răspunsul.

Cererea este transmisă de către client server-ului și constituie o solicitare pentru a obține unele resurse. Acele resurse sunt identificate printr-un URL.

Răspunsul este transmis de către server-ul web clientului, ca și consecință a cererii primite.

În imaginea de mai jos este reprezentată o comunicație simpla între un client Android și un server Web gazduit în Internet. Clientul face o cerere HTTP către server iar acesta îi răspunde. Printre alte informații, răspunsul conține un element foarte important, denumit codul de răspuns sau de stare. Informația utilă se gasește în corpul răspunsului HTTP.

Fig. 2

Codurile de stare HTTP, precum și cele mai importante din fiecare categorie sunt:

1xx – informaționale

100 – continuă

101 – schimbare protocol

2xx – răspuns reușit

200 – ok

201 – creat/realizat

202 – acceptat

3xx – redirectări

300 – diferite/multiple alegeri

301 – modificat/mutat permanent

302 – gasit

305 – folosește proxy

4xx – erori ale utilizatorilor

400 – cerere greșită

401 – neautorizat

402 – necesita plată

403 – interzis

404 – negasit

5xx – erori de server

500 – eroare interna de server

501 – neîndeplinit/nerecunoscut

503 – serviciu nedisponibil

Folosind clasele din pachetul java.net.* și java.io.* se poate iniția o conexiune HTTP cu un server web. Primul lucru care trebuie luat în considerare este necesitatea unui obiect al clasei InputStream care permite citirea unor octeți dintr-un flux de date. Odată creat acest obiect, creem altul derivat din clasa HttpURLConnection pentru a deschide o conexiune HTTP cu un URL. În cadrul obiectului din clasa HttpURLConnection setăm metoda HTTP folosită și inițiem conexiunea către server cu ajutorul metodei connect(). Determinarea codului de răspuns al server-ului web cu privire la cererea HTTP inițiată se află cu ajutorul metodei getResponseCode(). Dacă totul a decurs normal iar răspunsul primit de către client este 200-OK, atunci se pot citi octeți din fluxul de date. Prin apelul metodei getInputStream() se obține un obiect de tipul InputStream iar citirea se poate realiza foarte ușor folosind clasele din pachetul java.io.*.

String URL = http://www.google.ro; // exemplu trivial

int response = -1;

InputStream in = null;

URL url = new URL(URL);

URLConnection connection = url.openConnection(); /* se încearcă deschiderea unei conexiuni HTTP */

if( !( connection instanceof HttpURLConnection) )

throw new IOException("Not an HTTP connection");

try {

HttpURLConnection httpConnection = (HttpURLConnection)connection;

httpConnection.setAllowUserÎnteraction(false);

httpConnection.setÎnstanceFollowRedirects(true);

httpConnection.setRequestMethod("GET"); /* Tipul metodei HTTP – GET */

httpConnection.connect(); /* se conectează*/

response = httpConnection.getResponseCode(); /* codul răspunsului primit de la server */

if( response == HttpURLConnection.HTTP_OK) {

in = httpConnection.getInputStream();

}

}

catch(Exception ex) {

throw new IOException("Error connecting");

3.10 Android ListView

Componenta vizuală ListView din Android afișează un grup de elemente vizuale care pot fi derulate, numite și ListItem-uri. Un exemplu de ListView este chiar agenda telefonului. Aceasta se gasește în calea android.widget.ListView.

Un adaptor Android reprezintă o legatură între afișare (ex. ListView) și datele pentru acea afișare. Un adaptor gestioneaza datele și le adaptează astfel încât să potrivească în fiecare rând al afișării.

În Android, cu ajutorul metodei setAdapter() legăm adaptorul de afișare.

Imaginea de mai jos prezintă o perspectivă mult mai clară asupra acestor concepte.

Fig. 3

4. Descrierea aplicației

4.1 Descrierea funcționalitații generale a aplicației și a serverului

În continuare voi prezenta modul de utilizare al aplicației precum și câteva detalii tehnice pentru a înțelege ce se întamplă în spate.

În primul rând trebuie menționat faptul că aplicația stochează o listă cu numere de telefon în memoria telefonului. Dintre modalitățile de stocare a datelor din Android prezentate în secțiunea precedentă am ales opțiunea “Internal Storage”, cu alte cuvinte stocarea se va realiza în memoria înternă a dispozitivului și nu pe cardul SD. Motivația din spatele acestei alegeri o constituie persistența datelor. De exemplu, în cazul unui restart al smartphone-ului, aceste date vor rămâne intacte, spre deosebire de metoda SharedPreferences în care datele se vor șterge la un reboot. Totodată nu am folosit modalitatea de stocare externă a datelor, deoarece în această situație datele pot fi partajate. Am dorit ca această listă cu numere de telefon să fie stocată astfel încât să poată fi accesată numai din dispozitivul care conțîne aplicația.

După introducerea unui nou număr de telefon în câmp-ul de sub textul “Phone Number:” , la apăsarea butonului “Save” acesta este verificat dacă respectă urmatoarele două condiții:

Conține 10 cifre

Are prefixul “07”

În cazul în care butonul “Save” este apăsat deși nu s-a întrodus niciun număr în câmpul

corespunzător, pe ecran se va afișa mesajul “No number”. Pe de altă parte, dacă cele două condiții de mai sus nu sunt respectate de către noul număr, acesta nu va fi salvat și va fi afișat mesajul “Invalid number”. În final, dacă condițiile sunt respectate mesajul “Number saved successfully” va fi afișat pe ecran. Acest lucru conduce la salvarea noului număr de telefon în fișierul definit în Constants.NUMBER_LIST_FILE. Numărul de telefon va fi adăugat fără prefixul internațional aferent rețelelor de telefonie mobilă din România (spre exemplu, +40765236674 va fi 0765236674 și nu invers). Daca se dorește resetarea câmpului de introducere a numarului, se va apăsa butonul „Reset”.

O facilitate interesantă a aplicației constă în afișarea unui label în prima activitate împreună cu textul: “Status: ACTIVE/INACTIVE”. Apariția status-ului “ACTIVE” este dată de prezența a cel puțin unui număr de telefon în listă, caz în care se consideră că aplicația este activă. Dacă nu avem niciun număr de telefon în listă, se va considera că aplicația este inactivă, deoarece nu avem de unde să primim un mesaj text în cazul unui eventual furt pentru ca aplicația să localizeze dispozitivul. În cazul acesta, mesajul afișat este: “Status: INACTIVE”

Pentru a vizualiza numerele de telefon care deja sunt în listă, se va apasă butonul „Show number list” și se va deschide o nouă fereastră (activitate) în care vor fi listate numerele de telefon încarcate din memorie. Această listare se realizează cu ajutorul componentei grafice ListView, fiecare număr reprezentând câte un element al acestei liste.

Aplicația permite, de asemenea, și ștergerea unui numșr din listă. Pentru a obține acest lucru, se apasă pe numărul selectat și se va genera automat o casuță de dialog. La apăsarea butonului „Yes” numărul se va șterge din ListView, precum și din fișierul unde este stocat în memorie. Apăsarea butonului “No” are ca efect anularea deciziei de a șterge un număr.

La primirea unui mesaj text de la un număr din listă iar mesajul să conțină și codul secret prestabilit, aplicația va intra în modul „furt”.

Recepționarea oricărui SMS primit este realizată de catre clasa listener SMSReceiver care ascultă evenimente de acest tip.

La primirea unui SMS, se verifica doua lucruri:

Transmițătorul – dacă acesta este prezent în lista descrisă mai sus

Textul SMS-ului – dacă conține codul secret

În momentul în care cele două condiții anterioare sunt îndeplinite, aplicația intră în modul „furt” iar clasa listener de mai sus va emite un mesaj de broascast în sistem cu textul „has_been_stolen”. În acest moment se va scrie în fișierul a cărui nume se gasește în obiectul de tip String definit în Constants.STOLEN_FLAG_FILE valoarea “yes”. Acest eveniment va fi ascultat de către altă clasă listener – TimerShifter, care mai ascultă și alte două tipuri de evenimente: activarea GPS-ului și a conectivitații la Internet – fie prin intermediul datelor mobile ale operatorului cartelei SIM, fie prin intermediul unei conexiuni WiFi.

În momentul în care cele trei condiții sunt îndeplinite, acestea fiind totodată necesare și suficiente pentru a trimite date către server: telefonul a fost furat, există conectivitate la Internet (date mobile sau WiFi) și GPS-ul este activat; clasa TimerShifter va trimite un mesaj de broadcast cu textul “start_timer”. Dacă condiția anterioară nu este îndeplinită, se va trimite un mesaj de broadcast cu textul “stop_timer”. Aceste mesaje de broadcast sunt recepționate de către un receiver din MainActivity.java, care pornește/oprește un timer. Acest timer apelează o metodă care va culege informațiile necesare și le va încapsula într-un URL care va fi trimis pe rețea către server. Acest lucru se va realiza la intervale de timp prestabilite. Pentru testare, am folosit intervalul de 30 de secunde.

Pentru trimiterea datelor către server, am definit clasa SendDataToServerTimer care extinde clasa de bază TimerTask. În cadrul metodei run() a acestei clase, am apelat funcția sendDataToServer() care trimite datele de care avem nevoie prin rețea către baza de date.

Am utilizat această clasă deoarece aveam nevoie de un mecanism specific Java care să execute un task la intervale regulate de timp.

Pornirea timer-ului se realizează prin următoarea secvență de cod. Precondițiile sunt cele menționate mai sus: dispozitivul să fie furat, să existe conexiune la Internet și GPS-ul să fie pornit.

Timer timer = new Timer();

timer.scheduleAtFixedRate(new SendDataToServerTimer(), 0, Constants.TIMER);

De remarcat faptul că această aplicație este independentă de cartela/cartelele SIM prezente pe dispozitiv, lucru extrem de important când avem de-a face cu un furt.

În acest mod se poate localiza în timp real telefonul furat. De asemenea, un lucru interesant este dat de faptul ca în cazul în care aplicația trimite aceleași coordinate într-un anumit interval din cursul nopții și mai multe nopți la rând, putem găsi domiciliul răufăcătorului.

Următoarele printscreen-uri reprezintă prima activitate a aplicației, precum și cea de-a doua:

În ceea ce privește formatul cererii HTTP, acesta are forma:

http://george2015.net76.net/default.php?imei=X&timestamp=X&latitude=X&longitude=X

Câmpurile aferente imei-ului, timestamp-ului, latitudinii și longitudinii vor fi determinate în momentul în care se va decide să se trimita date către server.

Latitudinea, longitudinea precum și timestamp-ul aferent momentului detrminării acestora vor fi determinate de către funcția getLocation() a clasei LocationTracker.

În ceea ce privește arhitectura din spatele server-ului, pe lânga detaliile prezentate în secțiunea „Alegerea Platformei” mai trebuiesc menționate unele detalii.

În momentul primirii unei cereri HTTP GET cu formatul descries mai sus, un script php realizează o verificare a câmpurilor pentru a le valida.

Dacă cererea HTTP trece de acest pas, se va accesa baza de date cu următoarele caracteristici. Unele denumiri au fost generate automat de către sistemul de management al site-ului:

DB username: a665949_geo

DB password: password

DB: a665949_licenta

DB server: mysql12.000webhost.com

Odată ce conectarea catre DB a reușit și obținem handler-ul către aceasta, putem insera noile valori cu ajutorul comenzii:

INSERT INTO Tracking( IMEI, timestamp, latitude, longitude ) VALUES ( `$IMEI`, `$timestamp`, `$latitude`, `$longitude` );

Unde:

$IMEI este codul IMEI primit prin cererea HTTP

$timestamp reprezintă data și ora

$latitude reprezintă latitudinea

$longitude reprezintă longitudinea

Structura bazei de date este următoarea:

Imei -> int(20)

Timestamp -> datetime

Latitude -> double

Longitude -> double

Imaginea urmatoare reprezintă un printscreen al structurii bazei de date preluată de pe server:

Fig. 6

4.2 Prezentarea fișierelor sursă ale proiectului

Fișierele sursă create de-a lungul implementării lucrării de licența sunt urmatoarele:

MainActivity.java

Cuprinde codul aferent activitații cu care pornește aplicația și unele funcții auxiliare. Fișierul XML aferent interfeței grafice corespunzătoare acestei activități este main.xml. Printscreen-ul acestei activități se află în Fig.4.

Conține clase și metode deosebit de importante pentru funcționalitatea aplicației și implementează funcționalitatea din spatele fiecărui element grafic: butoane, edittext-uri.

private InputStream OpenHttpConnection(String URL)

Această metodă are ca parametru un String care reprezintă URL-ul resursei din Internet și returnează un obiect de tipul InputStream. Prin intermediul acestui obiect se pot descărca date de la acea locație. Acesta este o metodă eficientă de a accesa un serviciu web sau în general orice altă resursă din Internet.

Dacă răspunsul primit din partea server-ului conține codul 200 – OK, atunci vom obține obiectul de tipul InputStream si-l vom returna.

Pentru a deschide o conexiune HTTP cu acest URL, se utilizează un obiect de tipul HttpURLConnection.

Pentru întreaga operațiune se utilizează clasele din pachetul java.net.*

private String DownloadTextFromURL(String URL)

Prin intermediul acestei metode aflăm răspunsul unui serviciu web cu privire la o cerere.

Folosește metoda anterioară pentru a obține un obiect de tipul InputStream.

În cazul aplicației mele, accesarea link-ului conduce la parsarea de către scriptul php aflat pe server a noilor informații și în cazul validității acestora, va trimite ca răspuns “succes”, iar în caz contrar “error”.

Acest răspuns este citit după accesarea URL-ul și este returnat de către funcție.

private class sendDataToServerTask extends AsyncTask<String, Void, String>

Procesul de trimitere a datelor către server este asincron, deoarece este posibil ca într-un anumit moment să fim în încapacitatea de a obține informațiile necesare. De exemplu, GPS-ul sau datele mobile pot fi dezactivate și nu putem obține locația în această situație.

În cadrul metodei suprascrise doInBackground() am apelat metoda de mai sus, DownloadTextFromURL().

În ceea ce privește metoda onPostExecute(), aici am adăugat codul de verificare dacă acțiunea de trimitere a datelor către server a avut succes sau nu.

public Coordinate getMyLocation().

Apelul acestei metode întoarce un obiect în care sunt încapsulate latitudinea, longitudinea și timestamp-ul aferente locației.

Pentru a obține aceste rezultate se instanțiază un obiect al clasei LocationTracker și se apelează metoda getLocation() a acestuia. Această metodă va fi explicată mai jos.

Pentru a se stabili dacă se pot obține aceste informații, se apelează funcția canGetLocation() a clasei LocationTracker.

public String computeURL(…)

Această metodă are ca parametri formali latitudinea, longitudinea, timestamp-ul și contextul aplicației și întoarce un obiect de tipul String care conține URL-ul cu informațiile care se doresc a fi trimise la server.

Tot aici se obține codul IMEI al telefonului și se convertește timestamp-ul care reprezintă numărul de secunde de la 1 Ianuarie 1970 într-un format human-readable de forma yyyy-mm-dd HH:mm:ss.

public void sendDataToServer()

Aici se obține un obiect de tipul Coordinate prin apelul metodei getMyLocation()

Dacă operația de mai sus a avut succes se instanțiază un obiect global al clasei sendDataToServerTask, se generează URL-ul care va fi accesat folosind informațiile din obiectul de tip Coordinate obținut mai sus și apelul functiei computeURL.

Pasul următor constă în execuția task-ul asincron prin apelul metodei execute()

În final, se verifică dacă operația a avut succes sau nu prin înspectarea conținutului obiectului global de tip String result.

class SendDataToServerTimer extends TimerTask.

Prin intermediul acestei clase interioare derivată din clasă TimerTask se poate obține execuția unei secvențe de cod sau funcții la intervale regulate de timp prin suprascrierea metodei run(). În cazul de față, în metoda run() am apelat funcția sendDataToServer().

public void onCreate(Bundle savedInstanceState)

Această metodă este apelată automat atunci când aplicația este pornită si devine vizibilă utilizatorului.

Se setează componenta UI a activitații prin apelul metodei setContentView().

Se înregistrează în sistem receiver-ul care va asculta evenimente cu acțiunea “start_timer” și “stop_timer” iar la primirea unei astfel de acțiuni, se va porni/opri timer-ul.

Receiver-ul este declarat global în această sursa pentru a fi capabil de a reception evenimente chiar și atunci cand aplicația nu este vizibilă utilizatorului.

public void save(View view)

Aceasta este metoda care se apelează în momentul în care se apasă butonul “Save”.

Aici se extrage numărul de telefon introdus în câmpul aferent acestuia, se verifică și în cazul în care respectă cele două reguli se adaugă în fișierul cu numele definit în Constants.FILE_NAME.

public void showNumberListPressed(View view)

Funcție care se apelează când butonul “Show number list” este apăsat

Are ca efect pornirea celei de-a doua activitati a aplicației prin apelul metodei startActivity()

SecondActivity.java

Cuprinde codul din spatele celei de-a doua activitați care se poate observa în Fig.5

În cadrul metodei onCreate(), deci la pornirea activitații, se citește lista de numere de telefon din memorie, mai exact din fișierul cu numele definit în Constants.NUMBER_LIST_FILE și se populează elementele componentei grafice ListView cu aceste numere.

Fiecare element din acest ListView – deci fiecare număr de telefon, avem un listener care va apela metoda onItemClick() la atingerea unui număr de telefon.

În momentul apelării metodei de mai sus, se va apela metoda deleteItem(), care va deschide o fereastră de dialog cu întrebarea “Do you want to delete “phone number” ?” și cu opțiunile “Yes” și “No”

Alegerea opțiunii “Yes” are ca efect ștergerea numărului de telefon selectat din fișierul din memorie, precum și din ListView

Alegerea optiunii “No” indică faptul că utilizatorul s-a răzgandit asupra deciziei sale.

Mai jos este prezentată fereastra de dialog care este afișată în momentul selectării unui număr de telefon:

Fig. 7

SMSReceiver.java

Această clasă ascultă evenimente de tipul “RECEIVE_SMS” după cum a fost definită în AndroidManifest.xml

În momentul primirii unui mesaj text, se apelează metoda onReceive() a clasei SMSReceiver.

În cadrul metodei onReceive() este extras transmițătorul mesajului și se verifică dacă este prezent în listă și totodată conținutul textului mesajului este verificat dacă conține codul secret, definit în Constants.SECRET_CODE.

Dacă cele doua condiții de mai sus sunt îndeplinite, atunci se va scrie în fișierul cu numele definit în Constants.STOLEN_FLAG_FILE valoarea “yes” și se va trimite un mesaj de broadcast cu valoarea “has_been_stolen”

TimerShifter.java

După cum a fost înregistrat și în AndroidManifest.xml, această clasă listener ascultă evenimente de tipul CONNECTIVITY_CHANGE, pentru determinarea activării/dezactivării datelor mobile precum și a conexiunii WiFi și PROVIDERS_CHANGE pentru determinarea statusului GPS-ului și nu în ultimul rând ascultă evenimente de tipul “has_been_stolen” trimise de către clasa SMSReceiver în momentul primirii unui SMS de la un număr din lista care conține și codul secret.

În cadrul metodei onReceive(), care se apelează în momentul primirii oricarui dintre cele trei evenimente enunțate anterior, se va verifica starea GPS-ului, precum și a conectivității la Internet.

Pasul următor constă în verificarea conținutului fișierului Constants.STOLEN_FLAG_FILE. Dacă acesta conține valoarea “yes” și cel puțin una din condițiile de mai sus este îndeplinită, atunci se va trimite un mesaj de broadcast cu acțiunea “start_timer” în sistem. Dacă nu avem conectivitate la Internet sau GPS-ul este dezactivat, atunci mesajul de broadcast trimis va avea acțiunea: “stop_timer”. Ascultătorul acestor evenimente este implementat în MainActivity.java

Helper.java

Clasa de față conține câteva funcții de uz general, printre care:

public static String readFileFromMemory(Context context, String fileName)

citește fișierului referit prin numele fileName și întoarce conținutul acestuia

public static void writeFileFromMemory(Context context, String data, String fileName)

scrie String-ul referit prin data în fișierul referit prin numele fileName

public static boolean checkNumber(String phoneNumber)

Validează numărul de telefon conform celor două criterii: să conțină 10 cifre și să aibă prefixul “07”

Coordinate.java

Obiecte ale acestei clase poate stoca date vitale pentru determinarea poziției smartphone-ului: latitudinea, longitudinea și timestamp-ul.

Obiectele acestei clase reprezintă output-ul funcției pentru determinarea locației.

LocationTracker.java

Implementează interfața LocationListener

Cea mai importantă metodă a clasei este getLocation() care actualizează datele membru latitude, longitude și timestamp cu valorile adecvate. Obținerea acestor valori se realizează prin apelul metodelor getLatitude(), getLongitude(), respectiv getTimestamp().

Determinarea coordonatelor locale se realizează prin instanțierea clasei LocationManager și apelarea metodei requestLocationUpdates() pentru a obține informații cu privire la locație din rețea – date mobile sau GPS. În cazul în care operația a avut succes, se apelează metoda getLastKnownLocation() pentru a obține ceea ce dorim, iar rezultatul este un obiect de tipul Location.

Constants.java

În acest fișier sursă sunt definite constantele cu care se lucrează în aplicație

Avantajul alegerii stocării tuturor constantelor într-un fișier este dat de faptul că o modificare pe o constantă realizată aici va avea efecte în cadrul tuturor aparițiilor acelei constante în proiect și nu va fi necesar să se modifice constanta în fiecare zonă în care apare în cod, lucru total neeficient, cu atât mai mult în cazul proiectelor de dimensiuni mari

4.3 Descrierea modulelor aplicației

În continuare voi prezenta cele mai importante module ale aplicației, precum și felul în

care acestea interacționează cu restul aplicației pentru a obține rezultatul dorit.

Sa luăm în considerare scenariul în care un mesaj text este recepționat.

După cum am menționat anterior, clasa SMSReceiver tratează evenimentele de acest tip. Pentru a obține acest efect, această clasă extinde clasa de bază BroadcastReceiver având totodată nevoie de permisiunea RECEIVE_SMS.

În imaginea următoare se poate observa modalitatea în care un mesaj text este tratat la

primirea în sistem. Voi explica modul în care aplicația va ajunge să intre în starea de “furt” dacă condițiile sunt îndeplinite.

Fig. 8

Ordinea evenimentelor este următoarea:

Un mesaj text este primit

Clasa SMSReceiver este notificată de acest fapt.

Se extrage numărul de telefon al celui care a trimis mesajul text, se elimină prefixul internațional (în cazul României acesta este +4) și se reține în variabila sender

Se extrage conținutul mesajului text și se reține în variabila msgBody

Se verifică dacă valoarea sender este prezentă în lista cu numere de telefon memorată în fișierul Constants.NUMBER_LIST_FILE iar rezultatul este memorat în variabila isPresent

Se verifică dacă valoarea reținută în variabila Constants.SECRET_CODE este prezentă în variabila msgBody iar rezultatul este memorat în variabila containsSecretCode

Dacă atât variabila isPresent, cât și variabila containsSecretCode rețin valoarea true, se inițializează variabila isStolen cu true

Următorul pas constă în verificarea valorii reținute de către variabila isStolen.

În cazul în care aceasta reține valoarea true, în fișierul definit în Constants.STOLEN_FLAG_FILE se va scrie valoarea “yes” și se va trimite un mesaj de broadcast în sistem cu actiunea has_been_stolen.

Ascultătorul care primește evenimente cu acțiunea has_been_stolen se numește

TimerShiter. Funcționalitatea acestei clase ascultător este prezentată în urmatoarea imagine:

Fig.9

Această clasă ascultator, pe lângă evenimente de tipul has_been_stolen generate de către SMSReceiver.java, mai ascultă evenimente de tipul CONNECTIVITY_CHANGE, care presupun o conexiune activă la Internet, fie prin intermediul datelor mobile, fie în cadrul unei rețele WiFi și evenimente de tipul PROVIDERS_CHANGED care sesizează schimbarea statusului GPS-ului dispozitivului.

Ca și clasa ascultător explicată anterior, SMSReceiver, clasa de față extinde la rândul ei clasa de bază BroadcastReceiver iar pentru a fi capabilă să detecteze schimbări în ceea ce privește conectivitatea la Internet sau activarea/dezactivarea GPS-ului, noi permisiuni au fost adăugate în AndroidManifest.xml.

În momentul primirii unui eveniment aferent celor trei tipuri descrise anterior se va apela metoda onReceive() a clasei TimerShifter.

În cadrul acestei metode se vor realiza 4 verificări iar rezultatul va fi reținut într-o variabilă logică.

În următorul pas se verifică conținutul fișierului cu numele definit în Constants.STOLEN_FILE. Daca valoarea găsită în fișier este “yes” atunci variabila logică isStolen va reține true iar în caz contrar va reține false.

În ceea ce privește celelalte trei variabile, acestea rețin, după cum urmează: dataMobileFlag statusul conexiunii la Internet furnizată de către operatorul de telefonie mobilă, wifiFlag conectivitatea la Internet prin intermediul unei rețele WiFi iar gpsFlag reține statusul GPS-ului dispozitivului.

Se poate observa în imaginea de mai sus, două variabile logice: firstCondition și secondCondition care sugerează, conform săgeții, acțiunea mesajului de broadcast care va fi trimis în sistem în cazul valorii true a uneia dintre ele. Dacă valoarea variabilei firstCondtion este true, atunci se va trimite un broadcast cu mesajul “start_timer” iar dacă valoarea variabilei secondContion este true, atunci broadcast-ul trimis va fi “stop_timer”.

Să ne concentrăm asupra acestor două variabile extrem de importante, în funcție de care decidem dacă vom porni sau opri timer-ul. Prin timer fac referire la clasa care apelează în metoda run() funcția care trimite datele cu locația, timestamp-ul și codul IMEI al telefonului către server.

Din punct de vedere logic, pentru a trimite date către server, și implicit, a porni timer-ul trebuiesc îndeplinite următoarele condiții: dispozitivul să fie furat (variabila isStolen), GPS-ul să fie activat (gpsFlag) și să avem conectivitate la Internet (dataMobileFlag sau wifiFlag sa conțină valoarea true). Prin urmare, variabila firstCondition poate fi rezumata astfel:

firstCondition = isStolen && gpsFlag && ( dataMobileFlag || wifiFlag );

În ceea ce privește variabila secondCondition, avem nevoie de o metodă de a ști când să oprim timer-ul. În principiu, oprim timer-ul în momentul în care știm că nu este posibil să trimitem date catre server.

Logic, există două condiții în care putem realiza aceasta acțiune: fie dispozitivul este furat dar nu avem GPS-ul activat, caz în care nu ne mai interesează conectivitatea la Internet, sau dispozitivul este furat, avem GPS-ul activat, dar nu avem conectivitate la Internet. Programatic, putem rezuma această concluzie în următoarea condiție:

secondCondition = ( isStolen && !gpsFlag ) || ( isStolen && gpsFlag && ( !dataMobileFlag || !wifiFlag ) );

În funcție de valorile de adevăr ale celor două condiții de mai sus, decidem ce mesaj de broadcast să trimitem în sistem, start_timer sau stop_timer.

Receiver-ul care ascultă aceste două evenimente este definit în MainActivity.java. Spre deosebire de cele două clase ascultător prezentate mai sus, SMSReceiver și TimerShifter, acesta este declarat dinamic în sensul că acțiunile îi sunt definite în codul clasei și nu în fișierul AndroidManifest.xml.

În secvența următoare sunt declarate acțiunile care sunt ascultate de acest receiver:

IntentFilter filter_start = new IntentFilter();

filter_start.addAction("start_timer");

IntentFilter filter_stop = new IntentFilter();

filter_stop.addAction("stop_timer");

După cum se poate observa, se dorește ca acțiunile ascultate sa fie: start_timer și stop_timer.

Înregistrarea efectivă a acestui ascultător în sistem se realizează prin urmatoarea secvență de cod:

registerReceiver(receiver, filter_start);

registerReceiver(receiver, filter_stop);

Unde receiver reprezintă noua clasă ascultător.

În cadrul metodei onReceive() a clasei ascultător, apelată, după cum am menționat și anterior, la primirea unui mesaj de tipul celui ascultat, avand în vedere că ascultă două tipuri de mesaje, se verifică tipul mesajului primit:

public void onReceive(Context context, Intent intent)

{

Strîng actionReceived = intent.getAction();

// ….

În acest moment putem fi siguri că string-ul actionReceived conține una din următoarele mesaje: “start_timer” sau “stop_timer”.

Discriminăm în funcție de cele două mesaje în felul următor:

if(actionReceived.equals("start_timer"))

{

if(timer == null)

{

timer = new Timer();

timer.scheduleAtFixedRate(new SendDataToServerTimer(), 0, Constants.TIMER);

}

}

else if(actionReceived.equals("stop_timer"))

{

if( timer != null )

{

timer.cancel();

timer.purge();

timer = null;

}

}

Am decis să folosesc aceste două mesaje deoarece nu ar fi avut rost să încercăm trimiterea unei cereri HTTP când știm că nu există posibilitatea de a ajunge la destinație. Un alt motiv îl constituie utilizarea bateriei. Prin simplul fapt că se încercă accesarea rețelei prin cererea HTTP, acest lucru are implicații în utilizarea bateriei telefonului și este de dorit ca acest lucru să nu se întample. Vom trimite cererea HTTP doar în momentul în care suntem siguri că există șanse să ajungă la destinație.

Fig.10

În cadrul imaginii de mai sus se poate observa numele unei funcții în reprezentarea Timer-ului: sendDataToServer(). Ceea ce realizează timer-ul anterior este de a apela această funcție la intervale regulate de timp, interval definit în Constants.TIMER și care reprezintă o valoare în milisecunde.

În cadrul funcției sendDataToServer() se încercă obținerea locației dispozitivului prin apelarea metodei getMyLocation(). Dacă s-a reușit obținerea locației, atunci se va instanția clasa sendDataToServerTask(), se va obține URL-ul prin apelul metodei computeURL() și se va executa task-ul prin apelul execute(). Apelul acestei metode conduce la apelarea funcției doInBackground() din cadrul clasei sendDataToServerTask. După cum am menționat și în altă secviune, conținutul acestei metode se execută în fundal. În cazul de față, se execută funcția DownloadTextFromUrl() care are ca și parametru formal URL-ul obținut anterior prin apelul metodei computeURL().

Denumirea functiei DownloadTextFromUrl() se referă la rezultatul obținut după ce se trimit date la server. Rezultatul poate fi succes sau error.

În funcția anterioară se încercă creerea unei conexiuni HTTP cu URL-ul specificat și uploadarea datelor pe server. Dacă operația a avut succes, script-ul php din spatele server-ului întoarce mesajul “succes” iar dacă nu, “error”.

O metodă importantă o constituie computeURL() care are urmatoarea semnatură:

computeURL(Context, long, double, double);

Aceasta are rolul de a construi URL-ul care va fi accesat având efectul de a actualiza baza de date de pe server cu noi valori.

În cadrul compunerii URL-ului care îl va returna, doi dintre parametrii funcției vor fi adăugați așa cum sunt primiți, după ce vor fi convertiți către String: latitidinea și longitudinea.

În ceea ce prive ste timestamp-ul, acesta este primit sub forma unui long, care reprezintă numărul de secunde de la 1 Ianuarie 1970. Conversia se realizează în formatul yyyy-mm-dd HH:mm:ss. Pentru a realiza această formatare, se utilizează clasa SimpleDateFormat.

Codul IMEI al dispozitivului se obține prin apelarea funcției getDeviceId() peste un obiect al clasei TelephonyManager.

Aceste patru variabile vor fi concatenate cu unele constante ce formează URL-ul, definite în Constants.java.

Pentru a ilustra mai bine funcționalitatea acestei metode, se poate vedea imaginea următoare, cu un exemplu de parametrii formali și cu rezultatul de ieșire.

Fig.11

4.4 Evaluare

Aplicația a fost testată și evaluată pe mai multe scenarii pentru a fi sigur că are o funcționalitate corespunzătoare. Scenariile pe care ne vom concentra se leagă de ordinea în care sunt primite evenimentele de furt, activare GPS sau activare Internet. Pentru testare am folosit un intervat de timp mai mic de a trimite date la server și anume de 30 de secunde.

Primul scenariu considerat a fost cel în care în primă fază un mesaj text ce conține codul secret este primit pe smartphone. Aplicația a intrat în modul furt și așteaptă ocazia să trimită date către server. Am activat datele mobile urmate de GPS și după intervalul de 30 de secunde am observat în baza de date de pe server noi intrări corespunzătoare coordonatelor obținute, codului IMEI precum și a timestamp-ului. Acest lucru înseamnă că timer-ul a fost pornit și a apelat funcția sendDataToServer(). Dezactivarea GPS-ului sau a datelor mobile a avut ca efect oprirea trimiterii informațiilor. Am așteptat mai mult de 1 minut, timp în care s-ar fi trimis cel puțin o cerere HTTP și am reinspectat baza de date. Nu erau valori noi, prin urmare am ajuns la concluzia că timer-ul a fost oprit. Reactivarea GPS-ului sau datelor mobile a avut efectul dorit. Baza de date este populată cu intrari noi.

Același rezultat l-am obținut și dacă în scenariul de mai sus am utilizat o conexiune WiFi în locul datelor mobile pentru a avea acces la Internet.

Alt scenariu, diferit din punctul de vedere al ordinii evenimentelor aferente activării conexiunii la Internet, a GPS-ului și a primirii generării evenimentului de furt este următorul: Datele mobile sau Wifi-ul sunt deja activate, precum și GPS-ul. Primirea unui mesaj text de la un număr din listă ce conține codul secret are ca efect pornirea timer-ului iar dupa 30 de secunde în baza de date se observă noi Intrări. La fel ca în scenariul anterior, dezactivarea accesului la internet, a GPS-ului sau a amandurora conduce la oprirea timer-ului deci la oprirea trimiterii datelor.

Am realizat și cateva teste pentru a valida persistența datelor iar prîn acest lucru mă refer în special la numerele salvate în listă. După cum am menționat și anterior, numerele sunt salvate într-un fișier în memoria telefonului.

Pentru a fi pe deplin sigur de acest comportament, pur și simplu am restartat telefonul apoi am deschis aplicația să verific numerele de telefon salvate. Am apăsat butonul “Show Number List” iar numerele erau intacte.

În ceea ce privește salvarea unui număr în listă, am încercat mai multe variante de numere scrise greșit. Câteva exemple au constat în a introduce un număr de 9 sau mai puține/mai multe cifre sau care să nu aibă prefixul 07. De fiecare dată a apărut pe ecran mesajul “Invalid number”. În cazul în care nu aveam niciun număr în camp și apăsam “Save” se afișa mesajul “No number”.

În ceea ce privește butonul “Reset” funcționalitatea acestuia este garantată, având în vedere și rolul simplu pe care îl are. Fiecare test pe care l-am facut a trebut cu brio.

În timpul testelor efectuate pe parcursul dezvoltării aplicației, am observat că în momentul în care trimitea prima cerere HTTP, uneori chiar și pe a doua, coordonatele, precum și timestamp-ul aveau valori nule. Pentru a preveni încărcarea acestor date pe server, am adaugat o condiție în metoda sendDataToServer() în care verific dacă valoarea latitudinii este 0.0.

Ghid de utilizare

Aplicația de față are un mod foarte intuitiv de utilizare. Mai jos sunt descriși pașii necesari pentru a o utiliza. Testele au fost realizate pe un smartphone Samsung Galaxy S4.

Am denumit aplicația simplu “Licență 2015”. În cazul unei dezvoltari ulterioare, o voi

redenumi corespunzător pentru a o adăuga pe Google Play.

După compilarea proiectului, va rezulta un fișier cu extenșia apk. Am copiat acest fișier de pe

calculator în memoria telefonului prin intermediul cablului USB în folder-ul “Downloads”.

Fig.12

Se apasă pe iconița pentru a se instala aplicația și va apărea urmatoarea imagine cu permisiunile:

Fig.13

După cum se poate observa, permisiunile sunt următoarele:

Citire stare și ID telefon -> folosită pentru determinarea codului IMEI al dispozitivului

primire SMS -> pentru a trata mesajele text primite

localizare ( bazată pe rețea ) inexactă

localizare (GPS) exactă

acces integral la Internet -> pentru a trimite date către server

vizualizare stare rețea -> pentru a determina când avem acces la Internet prin intermediul datelor mobile

vizualizare stare Wi-Fi -> a determina când avem acces la Internet prin intermediul unei rețele Wi-FI

Dacă utilizatorul acceptă aceste permisiuni, tot ce mai are de făcut este să apese butonul “Instalați” și aplicația se va instala în câteva secunde.

Dupa instalare va apărea o iconiță în meniu cu numele aplicației.

Fig.14

Fig.15

Fig.16

Fig.17

Fig.18

În ceea ce privește partea de server a aplicației, pentru a accesa baza de date se accesează link-ul http://members.000webhost.com/ . Se introduc credențialele și codul din imagine, pentru o securitate sporită a datelor și se apasă butonul “Submit”.

Fig. 19

Pasul următor constă în navigarea către Control Panel-ul site-ului. Se apasă butonul “Go to CPanel”:

Fig. 20

Se derulează pagina nou apăruta în jos până la secțiunea “Software/Services” după care se va selecta “MySQL”.

Fig. 21

În secțiunea “Manage MySQL Databases” se va accesa link-ul cu numele “phpMyAdmin”:

Fig. 22

Accesarea link-ului de mai sus conduce la afișarea listei cu bazele de date curente. Se apasă butonul “Enter phpMyAdmnn” aferent bazei de cate la care dorim accesul:

Fig.23

În situația de față, baza de date este prezentată în imaginea de mai jos:

Fig. 24

Mai jos este prezentat un exemplu de conținut pentru această bază de date. Se observă în prima coloană codul IMEI al telefonului care a trimis mesajul. În celelalte trei coloane se observă data și ora momentului precum și latitudinea și longitudinea dispozitivului.

Fig.25

Concluzii și direcții ulterioare de dezvoltare

Aplicația prezentată de-a lungul acestei lucrări științifice este foarte ușor de utilizat, chiar și

pentru un utilizator care nu are deprinderi foarte bune de lucru cu sistemul de operare Android. Unul dintre avantajele acestei aplicații constă în faptul ca poate fi utilizată pe orice dispozitiv

pe care ruleaza Android, indiferent dacă este un smartphone sau tabletă.

Aplicația de față a fost dezvoltată cu gândul de a oferi o posibilitate ca utilizatorii să-și

recupereze dispozitivele Android în cazul unui potențial furt al acestora sau chiar al pierderii lor.

Datorită mijloacelor reduse existente prin care un astfel de dispozitiv aflat într-una din

situațiile prezentate mai sus, aplicația doreste a creste sansele recuperarii sale.

În momentul de față, aplicația funcționează corespunzător, aceasta trecând mai multe teste

realizate pentru diferite scenarii.

Pe viitor, aplicația va suferi mai multe modificări în ceea ce privește interacțiunea cu

utilizatorul, precum și utilizarea mai multor algoritmi pentru a obține rezultatele finale.

Datorită faptului că în momentul dezvoltării aplicației, smartphone-ul pe care s-au realizat

testele fiind produs de Samsung, nu s-au putut obține informații cu privire la celulele de telefonie

mobilă datorită unor limitări impuse de către producător. În lipsa acestor limitări, locația s-ar fi

putut determina pe baza algoritmului de triangulare, care folosește informațiile prezentate mai sus,

ne mai fiind necesară activarea conexiunii la Internet precum și a GPS-ului pentru a obține locația.

Aceasta reprezintă prima idee pentru o dezvoltare ulterioară a aplicației. Se poate determina

programatic producătorul dispozitivului pe care rulează aplicația și în functie de rezultat, să se decidă

pe baza a căror criterii sa se stabilească locația.

În ceea ce privește interacțiunea cu utilizatorul, o dezvoltare ulterioară a aplicației ar consta în

personalizarea ecranului aplicației.

Altă idee interesantă constă în utilizarea camerei foto frontale pentru a realiza o fotografie

celui care deblocheaza dispozitivul. Acest lucru se întamplă, evident, în cazul în care aplicația intră în

modul furt. Fotografia rezultată va fi stocată în memoria internă a telefonului și va fi încărcată

ulterior pe server, în momentul în care este disponibilă o conexiune la Internet. Acest lucru se va

realiza silențios iar cel ce foloseste dispozitivul nu va sesiza acest lucru.

Altă idee interesantă în situația unui eventual furt este următoarea: stocarea datelor cu

privire la apelurile primite și efectuate, precum și stocarea tuturor mesajelor text în aceeași manieră. Acestea vor fi salvate, la fel ca în cazul fotografiilor realizate cu ajutorul camerei frontale, în

memoria internă a dispozitivului și trimise pe server în momentul în care avem acces la Internet.

În final, în funcție de nivelul de dezvoltare al aplicației, precum și al facilităților oferite, pot lua

decizia dacă aplicația o voi urca pe Google Play pentru a o împartăși și cu alte persoane.

BIBLIOGRAFIE

[1] Curs Proiectarea și Dezvoltarea Serviciilor Distribuite, Facultatea de Automatică și Calculatoare, Universitatea Politehnica din București, profesor Bogdan Niculescu

[2] Curs Programare Orientata pe Obiecte, Facultatea de Automatică și Calculatoare, Universitatea Politehnica din București, profesor Carmen Odubășteanu

[3] http://developer.android.com

[4] http://tools.ietf.org/html/rfc2616

[5] http://www.codelearn.org

[6] http://www.android.com

Similar Posts