Lect. Dr. Vasile Silviu Laurențiu STUDENT București 2017 UNIVERSITATEA DIN BUCUREȘTI FACULTATEA DE MATEMATICĂ ȘI INFORMATICĂ LUCRARE DE LICENȚĂ… [606521]

UNIVERSITATEA DIN BUCUREȘTI
FACULTATEA DE MATEMATICĂ ȘI INFORMATICĂ

LUCRARE DE LICENȚĂ

COORDONATOR ȘTIINȚIFIC
Lect. Dr. Vasile Silviu Laurențiu

STUDENT: [anonimizat]
2017

UNIVERSITATEA DIN BUCUREȘTI
FACULTATEA DE MATEMATICĂ ȘI INFORMATICĂ

LUCRARE DE LICENȚĂ
Aplicație Android pentru programarea filmelor

COORDONATOR ȘTIINȚIFIC
Lect. Dr. Vasile Silviu Laurențiu

STUDENT: [anonimizat]
2017

1
CUPRINS

Introducere ……………………………………………………………………………………………………………………………. 3
1. TEHNOLOGII FOLOSITE …………………………………………………………………………………………………. 4
1.1 Mobile ……………………………………………………………………………………………………………………. 4
1.1.1 Sistemul de operare Android ………………………………………………………………………………. 4
1.1.2 Kotlin ………………………….. ………………………….. …………………………………………………….. 5
1.2 Backend ……………………………………………………………………………………………………………………….. 9
1.2.1 Gunicorn ……………………………………………………………………………………………………………….. 9
1.2.2 Nginx …………………………………………………………………………………………………………………… 10
1.2.3 MySQL ……………………………………………………………………………………………………………….. 11
1.2.4 Python …………………………………………………………………………………………………………………. 12
1.2.5 Flask ……………………………………………………………………………………………………………………. 16
1.3 Modelul de arhitectură MVVM …………………………………………………………………………………….. 17
1.3.1 Modelul ……………………………………………………………………………………………………………….. 18
1.3.2 View-ul ……………………………………………………………………………………………………………….. 18
1.3.3 ViewModel-ul ………………………………………………………………………………………………………. 18
1.3.5. Avantajele arhitecturii MVVM ………………………………………………………………………………. 19
1.3.6 Concluzii ……………………………………………………………………………………………………………… 20
1.4 Sisteme de recomandare ………………………………………………………………………………………………. 20
1.4.1 Prezentare generală ……………………………………………………………………………………………….. 21
1.4.2 Filtrarea bazată pe conținut …………………………………………………………………………………….. 21
1.4.3 Filtrarea colaborativă …………………………………………………………………………………………….. 22
1.4.4 Sisteme de recomandare hibride ……………………………………………………………………………… 24
1.4.5 Premiul oferit de Netflix ………………………………………………………………………………………… 24
2. APLICAȚIA ……………………………………………………………………………………………………………………. 25
2.1 Prezentare generală ……………………………………………………………………………………………………… 25
2.2 Scopul ……………………………………………………………………………………………………………………….. 25
2.3 Funcții ……………………………………………………………………………………………………………………….. 26
2.4 Cazuri de utilizare ……………………………………………………………………………………………………….. 26
2.5 Diagrama de clase ……………………………………………………………………………………………………….. 27
2.6 Schema bazei de da te …………………………………………………………………………………………………… 28
3. PREZENTAREA APLICAȚIEI …………………………………………………………………………………………. 30

2
3.1 Prezentarea ecranelor …………………………………………………………………………………………………… 30
3.1.1 Noutăți …………………………………………………………………………………………………………………. 30
3.1.2 Filme …………………………………………………………………………………………………………………… 31
3.1.3 Program ……………………………………………………………………………………………………………….. 32
3.1.4 Hartă ……………………………………………………………………………………………………………………. 34
3.1.5 Setări …………………………………………………………………………………………………………………… 39
3.1.6 Bibliotecă …………………………………………………………………………………………………………….. 40
3.1.7 Detaliile filmului …………………………………………………………………………………………………… 41
3.1.8 Detaliile ecranizării ……………………………………………………………………………………………….. 43
3.1.9 Autentificare/Înregistrare/Profil ………………………………………………………………………………. 44
3.2 Baza de date locală ………………………………………………………………………………………………………. 46
3.3 Gradle ………………………………………………………………………………………………………………………… 47
3.4 Retrofit ………………………………………………………………………………………………………………………. 48
CONCLUZII ……………………………………………………………………………………………………………………….. 50
Bibliografie: …………………………………………………………………………………………………………………………. 51

3
Introducere

În această lucrare de licență am sa vă prezint o aplicație Android care ușurează/facilitează
urmărirea programului unui cinema și rezervarea unui film, precum și nevoia/dorința de a fi
la curent cu ultimele ecranizări apărute. Unul din scopurile aplicației este acela de a permite
utilizatorilor sa vadă în timp real filmele care rulează la diferite cinema-uri din țară prin
intermediul unei hărți interactive ce separă fiecare ecranizare pe cinema în urma unui
algoritm de clustering ; astfel facilitând achiziți onarea unui bilet online sau a unei rezervări.
Dez
voltând Movieplex am urmărit ca aplicația să fie disponibilă pe cât mai multe telefoane
inteligente ce rulează Android, de aceea versiunea minima necesară este 4.2 ( JellyBean ), ceea
ce cuprinde aproximativ 97% din dispozitive. Aplicația nu are nicio restricție de vârstă,
putând fi folosită de orice persoană cu un telefon, tabletă sau chromebook compatibil.
Dorința de a lucra în ecosistemul Android și dorința de a optimiza/fluidiza pașii pe care o
persoană trebuie să îi facă pentru a stabili la ce film dorește să meargă m-au determinat să
aleg această temă. Mi-am dorit să dezvolt o aplicație prin care o persoană poate afla rapid ce
filme sunt disponibile acum la cinema, precum și programul lor, ca să nu mai fie nevoie să
deschidă un navigator Internet/web pentru a putea ca apoi să intre pe site-ul cinema-ului și să
caute programul pentru locația dorită. De asemenea, mi-am dorit ca utilizatorii să poată vedea
și un mic feedback de la alte persoane : nota filmului , numărul de persoane care așteaptă ca
filmul să apară, numărul de oameni care au văzut un anumit film, etc. Cu toate aceste date am
format câteva top -uri pentru a -i ajuta pe oameni să aleagă mai ușor la ce film să meargă.
Mai mult, văzând cât de mult a avansat limbajul de programare Kotlin precum și numărul
programatorilor care îl folosesc pentru a face aplicații Android, mi-am dorit să îl încerc; mai
ales că majoritatea îl recomandă . Având în vedere că anul trecut Google l -a trecut oficial în
lista limbaj elor suportate pentru a face aplicații Android printre Java și C++, m -a făcut și mai
curios cu privire la acest limbaj nou. După ce am început să mă documentez și să citesc puțin
din documentația oficială, am fost atras de puterea și flexibilitatea limbaju lui, fiind astfel
nerăbdător să încep să codez în el. Ceea ce îmi place cel mai mult la Kotlin este faptul că nu
mai e nevoie să se specifice anumite tipuri de date sau unele construcții de obiecte din clase
anonime (Java 8 îmbunătățește asta dar Kotlin e la un nivel peste), făcând astfel codul mult
mai clar și mai ușor de înțeles pentru persoanele noi care încep să lucreze la un proiect
existent.
Movieplex are o parte de mobile si una de backend. Partea de mobile este scrisă folosind
limbajul Kotlin si respectă pattern-ul MVVM (Model View ViewModel ). P artea de backend
este scrisă în Python utilizând framework-ul Flask pentru request-uri și flask-sqlalchemy ce
funcționează ca un ORM între codul de Python și baza de date MySQL.
În
următoarele capitole vă voi vorbi despre fiecare tehnologie folosită, design-ul și
construcția aplicației și toate funcționalitățile pe care le pune la dispoziție aplicația.

4
1. TEHNOLOGII FOLOSITE

1.1 Mobile

Partea de mobile conține componentele ce pot fi manipulate de către utilizatori și este de fapt
o interfața user-friendly .

1.1.1 Sistemul de operare Android

Android Inc. A fost fondat in Palo Alto, California în Octombrie 2003 de către Andy Rubin,
Rich Miner, Nick Sears și Chris White. Rubin a descris acest proiect ca “un potențial u riaș în
dezvoltarea unor dispozitive mobile care sunt conștiente de locația utilizatorului și a
preferințelor lui”. Primele intenții ale companiei au fost să intre pe piața camerelor digi tale,
dar după ce au realizat că aceasta nu era suficient de mare pen tru scopul proiectului lor,
aceștia și-au reorientat atenția spre piața telefoanelor mobile , dorind sa concureze împotriva
sistemelor de operare Symbian si Microsoft Windows Mobile. [1]
Fig. 1. HTC Dream – primul telefon cu Andro id

Î
n Iulie 2005, Android Inc. a fost cumpărat de către Google pentru o sumă de cel puțin 50 de
milioane de dolari. Pe 23 Septembrie 2008, la un an după lansarea lui iPhone 3G, primul
telefon rulând Android a fost prezentat : HTC Dream, cunoscut și sub num ele de T-Mobile
G1.

5
Pe 5 Noiembrie 2007 s-a format asociația/consorțiul Open Handset Allience din care făceau
parte Google, producători de telefoane mobile precum HTC, Motorola și Samsung, operatori
de rețea precum Sprint și T-Mobile și producători de c ip-uri printre care se numărau și
Qualcomm și Texas Instruments. Misiunea lor a fost și este de a dezvolta “prima platforma
deschisă si completă pentru dispozitive mobile ”.
Începând cu 2008 sistemul de operare Android a primit numeroase actualizări care adăugau
atât funcționalități noi cât și rezolvau bug-uri. Fiecare versiune este denumită după un desert :
Cupcake (1.5), Donut (1.6), Eclair (2.0 – 2.1), Froyo (2.2), …, Nougat (7.0, 7.1), Oreo (8.0.0,
8.1.0).
Pe lângă sistemul de operare destinat telefoanelor și tabletelor, Google a mai dezvoltat și
Android TV pentru televizoare, Android Auto pentru mașini și Wear OS (înainte Android
Wear) pentru ceasuri, fiecare având o interfață diferită.
Aplicațiile pentru Android sunt scrise de regulă în Java. Limbajul de programare Java poate fi
combi
nat cu C/C++, împreună cu alte componente pentru a mări suportul pentru C++.
Limbajul Go este și el acceptat, dar are acces la un număr limitat de funcții/funcționalități. Pe
17 Mai 2017, Google a anunțat suport oficial pentru dezvoltarea aplicațiilor Android folosind
limbajul de programare Kotlin. La început aplicațiile erau scrise în Eclipse, iar pentru a putea
fi rulate era necesară descărcarea și instalarea mai multor biblioteci, ceea ce făcea
inițializarea/setup-ul mediului de lucru o munca greoaie. Google a venit cu un răspuns la
această problemă, lansând în Decembrie 2014 Android Studio, un IDE care permite
programatorilor scrierea de aplicații Android fără instalarea altor biblioteci suplimentare. De
atunci, IDE-ul a primit mai multe actualizări care au optimizat viteza cu care se creează și
rulea
ză aplicațiile.

Statistici[1][2]:
 Android-ul a fost cel mai bine vândut sistem de operare la nivel global din 2011
 Din Mai 2017 are 2 miliarde de utilizatori activi lunar (cel mai mare număr dintre
toate sistemele de operare)
 Din Martie 2018, magazinul Google Play conține peste 3.6 milioane de aplicații
 În anul 2017, 85.9% din totalul de smartphone-uri vândute rulau Android

1.1.2 K
otlin

De când a fost oficial aprobat și susținut de Google ca un limbaj de programare pentru a
dezvolta aplicații Android mi-am dorit să învăț să lucrez în el. În perioada aceea am început
să mă uit la multe filmulețe de prezentare și tutoriale de la dezvoltatori de aplicații Android,
dezvoltatori ce lucrau în el de ceva timp chiar și puținele talk-uri susținute de creatorii lui. În

6
acest timp am văzut cât de mult cod de umplutură elimină comparativ cu Java și asta m- a
făcut să îmi doresc și mai mult să lucrez în el.

Kotlin este un limbaj de programare orientată pe obiecte care rulează pe mașina virtuala Java
și care poate fi compilat și în cod JavaScript. El poate să folosească si infrastructura LLVM.
In Iulie 2011, JetBrains a anunțat proiectul Kotlin, un nou limbaj de programare pentru JVM
care era în lucru de peste un an. Unul din șefii de la JetBrains a spus că majoritatea limbajelor
nu aveau funcționalități pe care ei le căutau, singura excepție fiind Scala, dar care are o viteză
mica de compilare. Unul din principale scopuri ale Kotlin-ului era să compileze la fel de
repede ca Java. Pe 15 Februarie 2016 a fost lansată prima versiune stabila v1.0. [3][4]
Deși sintaxa nu este compatibilă cu Java, implementarea bibliotecii standard a limbajului
Kotlin este făcută de așa natură să poată compila și rula cu codul Java. Mai mult, Kotlin este
dependent de bibliotecile existente scrise în Java, unul din exemple fiind framework-ul
Collections. Kotlin folosește un tip de inferență “agresiv” de a determina tipul unei valori sau
expresii dacă acesta nu a fost menționat explicit.
Sintaxa :
Kotlin elimină restricțiile pe care le are Java cu privire la obiectele și metodele statice aflate
într-o clasă și permite declararea lor la nivelul pachetului, fără a mai fi nevoie de o clasa
redundantă. Pentru compatibilitate, este adăugată o adnotare specială pentru corpul
pachetului.
Kotlin îi lasă pe programatori să adauge funcții la orice clasă fără a mai crea o clasă derivată.
Acest concept numit și “metodă extinsă” este asemănător cu cel întâlnit în C# și permite unei
funcții să acceseze proprietățile unei clase fără a fi declarată în corpul acesteia. Un exemplu
de metodă extinsă ar fi acesta :
package MyStringExtensions
fun String.lastChar(): Char = get(length – 1)
>>> println("Kotlin".lastChar())

Spre deosebire de Java, Kotlin permite supraîncărcarea operatorilor :
// overloading '+' operator using an extension method
operator fun Point.plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}

>>> val p1 = Point(10, 20)
>>> val p2 = Point(30, 40)
>>> println(p1 + p2)
Point(x=40, y=60)

7
Kotlin permite declararea unei funcții în altă funcție :
class User(
val id: Int,
val name: String,
val address: String)
{

fun saveUser (user: User) {
fun validate (user: User, value: String, fieldName : String) {
if (value.isEmpty()) {
throw IllegalArgumentException (
"Nu se poate salva utilizatorul ${user.id}: lipsă
$fieldName" )
}
}

validate (user, user.name, "Mihai")
validate (user, user.address, "Adresa")
// Salvează utilizatorul în baza de date
}
}

Unul din singurele dezavantaje/defecte pe care le are Kotlin ar fi că o clasă este
finală/constantă implicit. Asta înseamnă ca dacă un programator uită să o marcheze “open”
explicit atunci ea nu va putea fi extinsă. Pe de altă parte, clasele abstracte sunt deschise
implicit, iar cuvântul che ie „open” nu este necesar.
Asemănător cu Java, Kotlin are 4 tipuri de modificatori acces : public, internal (asemănător cu
package), protected și private.
Kotlin permite specificarea unui valori implicite în declararea constructorilor spre deosebire
de Java unde pentru fiecare variabila nouă adăugată în constructor este nevoie de scrierea
unuia nou .

8

// Exemplu de o clasă folosind un constructor principal
class User(
val nickname : String,
val isSubscribed : Boolean = true)
{

}

// Exemplu de o clasă folosind 2 constructori
class MyButton : View {

// Constructor #1
constructor (ctx: Context) : super(ctx) {
// …
}

// Constructor #2
constructor (ctx: Context, attr: AttributeSet ) : super(ctx, attr) {
// …
}
}

Variabilele în Kotlin pot fi declarate ca fiind imutabile folosind cuvântul cheie “val” sau
mutabile folosind “var”. Kotlin face distincție între tipurile de date non null și nullable . La
cele din urmă se adaugă “?” după tipul de date. Astfel, când se dorește apelarea unei metode
a unui obiect care poate să aibă și valoarea null, este necesară adăugarea unui “?” înainte de
“.”:
var a: String = "abc"
a = null // eroare de compilare

var b: String? = "abc"
b = null // ok

// Se poate apela metoda pentru că se știe sigur că nu va cauza
NullPointerException
val l = a.length

val l = b.length // eroare: variabila 'b' poate fi null

val l = b?.length // daca b e null atunci si l va fi null, dar nu va cauza
NullPointerException
// Apelurile folosind ?. sunt foarte folos itoare în momentul când se
apelează mai multe metode în lanț:
bob?.department ?.head?.name

9
Kotlin mai introduce și operatorul “let” care poate fi folosit asemănător cu o instrucțiune
condițională :
val listWithNulls : List<String?> = listOf("A", null)
for (item in listWithNulls ) {
item?.let { println(it) } // afișează A și ignoră null
}

Kotlin mai pune la dispoziția programatorilor și alți operatori pentru a facilita dezvoltarea de
aplicații și programe, printre ele se numără “!!” pentru scoaterea unui tip de date non null
dintr-unul nullable (aruncă NPE în cazul în care dă peste null), as și as? pentru conversia
unui obiect la un tip de bază sau derivat (ul timul e safe call și nu cauzează NPE daca obiectul
care se dorește a fi convertit este null).
Statistici:
 În cadrul Developer Keynote-ului de la Google I/O 2018, Stephanie Cuthbertson , a
spus că 35% din programatorii profesioniști folosesc Kotlin.
 Conform AppBrain, 1.43% din numarul total de aplicații folosesc Kotlin, în timp ce
5.35% din numărul total de instalări sunt aplicații care au cod în Kotlin. Mai mult,
procentajele acestea cresc semnificativ daca ne raportăm la top 500 cele mai bune
aplicații în SUA : 14.63% din numărul total și 7.66% din numărul de instalări. Ca si
punct de reper, în magazinul Google Play sunt aproximativ : 3,709,157 de aplicații.
Printre aplicațiile din top 500 se numără : Netflix, Twitter, Pinterest, Reddit, Samsung
Health și OneN ote. [5][6]

1.2 Backend

Partea de server se ocupă cu prelucrarea informațiilor pe care utilizatorii le pot vedea în
aplicație, precum și stocarea lor.

1.2.1 Gunicorn

Am ales să folosesc Gunicorn ca și server de aplicație pentru că este ușor de configurat, nu
consumă prea multe resurse și poate interpreta cu ușurință fișierele de Python. Mai mult,
lucrează foarte bine cu Nginx : cel din urmă ascultă după cereri HTTP și le redirecțione ază lui
Gunicorn, care prin intermediul lui Flask rulează scripturile necesare și primește un răspuns
pe care îl trimite Nginx -ului pentru a -l da mai departe client -ului.

10
Gunicorn ( “Green Unicorn”) este un WSGI ( Web Server Gateway Interface – o convenție d e
nume pentru serverele web care redirecționează un request/cerere la o aplicație web sau la un
framework scris în Python) pentru limbajul de programare Python. Este un port al proiectului
Unicorn din Ruby. Server -ul gunicorn este compatibil cu majoritatea framework- urilor web,
este ușor de implementat, consumă puține resurse și este destul de rapid. [7][8]
Gunicorn este bazat pe un model de “worker” pre -fork, ceea ce înseamnă că un fir de
execuție principal ( master thread) pornește mai mulți muncitori care să se ocupe de cereri,
dar acesta nu controlează felul cum muncitorii își execută sarcina . Astfel fiecare muncitor
este independent.
Câteva din funcționalitățile gunicorn-ului sunt :
 Suport nativ pentru WSGI, web2py, Django și Paster
 Management automat al muncitorilor
 Configurare ușoara în Python
 Posibilitatea de a configura fiecare muncitor în parte
 Compatibil cu Python 2.6+ și Python 3.2+

1.2.2 Nginx

Am ales acest web server față de Apache, pentru că este ușor de configurat, rapid și puternic .
Lucrează bine împreună cu Gunicorn și nu consumă foarte multe resurse.
Nginx este un server web care poate fi folosit ca reverse proxy, load balancer (optimizează
resursele sistemului, minimizează timpul de așteptare, maximizează informațiile procesate și
elimină suprasolicitarea unei singure resurse), mail proxy și HTTP cache (un server care
salvează pagini web pentru a reduce întârzierile server-ului; cu alte cuvinte stochează paginile
accesate pentru a nu mai fi nevoie sa le reîncarce data viitoare când aceeași pagină este
cerută). Nginx a fost creat de Igor Sysoev și lansat în anul 2004. A fost inițial dezvoltat
pentru a rezolva problema C10k (optimizarea sockeților de rețea pentru a face față la un
număr mare de clienți simultan; c10k este un acronim de la 10 000 de conexiuni simultan).
[9]
Nginx poate fi lansat/instalat să servească conținut HTTP dinamic folosind FastCGI, aplicații
de tip WSGI sau alte tipuri de servere. Față de Apache, Nginx poate să facă față/servească de
patru ori mai multe cereri pe secundă și fără alte modificări, folosește mult mai puțină
memorie ( ~2.5MB pe 10 000 de conexiuni inactive de tip keep-alive). Această diferență de
performanță vine cu un cost important : flexibilitate. Posibilitatea de a suprascrie setări de
sistem valabile pentru toate serverele lipsește, în timp ce Apache pune la dispoziția
programatorilor un fișier .htaccess.

11
Pe lângă cele menționate deja, Nginx mai are și alte funcționalități :
 posibilitatea să servească fișiere statice și să auto-indexeze fișierele
 suport TLS/SSL prin intermediul OpenSSL
 compatibil cu IPV6
 suport pentru WebSockets și HTTP/1.1 Upgrade
 posibilitatea schimbării configurației fără a pierde conexiuni

În Noiembrie 2016, Nginx era al doilea cel mai folosit server web. Acesta era folosit de
37.7% din cele mai renumite 1 milion de site-uri, 49.7% din cele mai renumite 100 000 si
57% din primele 10 000.

1.2.3 MySQL

MySQL este cel mai popular sistem de baze de date relațională fiind ușor de folosit, scalabil,
rapid și pune la dispoziție o suită de programe pentru a-i ajuta pe programatori să
construiască și sa-și întrețină baza de date. Datorită numeroasele avantaje, MySQL a devenit
alegerea principală a multor companii mari : Google, Facebook, Twitter, Flickr, Youtube și
WordPress. [10][11]
MySQL este scrisă în C și C++, iar parser-ul în yacc. Funcționează pe majoritatea
platformelor printre care și : Microso ft Windows , Linux, macOS, FreeBSD, OpenSolaris și
Oracle Solaris.

Printre funcționalitățile acestui SGBD se numără :
 un subset larg de ANSI SQL 99
 suport cross-platform
 proceduri stocate
 trigere (declanșatori)
 cursoare
 select-uri imbricate
 vizualizări ce pot fi actualizate
 suport SSL
 caching-ul interogărilor
 suport unicode
Pe de altă parte, până la versiunea 5.7, în MySQL nu se putea declara mai mult de un trigger
care să se execute înainte sau după instrucțiunea INSERT. Mai mult, nu există posibilitatea
declarării de triggeri pentru vizualizări. Funcția UNIX_TIMESTAMP() va returna 0 după 19

12
Ianuarie 2038 03:14:07 UTC din cauză că numărul de secunde de după Ianuarie 1970 este
reprezentat pe 32 de biți (231-1 = 2 147 483 647). Această problemă a fost recunoscută și
urmează a fi rezolvată.

1.2.4 Python

Pe partea de server, limbajul pe care l-aș alege fără să mă gândesc este Python. Avantajele lui
principale sunt : implementarea unei funcționalități necesită puține linii de cod, limbajul este
foarte rapid, este ușor de învățat, codul este simplu de înțeles și completează perechea Nginx
– Gunicorn.
Python este un limbaj de programare de nivel înalt creat în 1991 de către Guido van Rossum.
Python pune accent pe lizibilitatea codului, în special pe numărul redus de spații goale. Pune
la dispoziție mai multe paradigme de programare precum : orientată obiect, imperativă,
funcțională și procedurală. El este disponibil pe majoritatea/toate platformele : Microsoft
Windows, macOS precum și toate distribuțiile de Linux. [12][13]
Primul lucru pe care un programator îl vede când începe sa codeze în Python este că acesta
nu delimitează blocurile prin “{}”, ci prin identarea cu spații . Mai mult, sfârșitul de linie nu
este marcat în nici un fel, comparativ cu C++/Java pentru care este necesar un “;” la finalul
oricărei expresii/linii. Complexitatea scăzută a codului îl face unul din limbajele preferate de
începători.
Python este un limbaj de programare interpretat care este compilat automat în bytecode
înainte de execuție; acesta este salvat pe disk și refolosit ori de câte ori este nevoie atâta timp
cât codul sursă nu este modificat. Datorită beneficiilor pe care le oferă, multe componente
importante din Linux sunt scrise în el, multe companii mari îl folosesc : Google (la
implementarea mul tor componente ale crawler -ului lor precum și a motorului de căutare ),
Wikipedia, Yahoo, NASA, Facebook, Amazon, Spotify, s.a.m.d .
Tipul variabilelor nu este specificat explicit:
changing = 9
print (changing )
9

red = 5
blue = 10
print (red, blue)
5 10

Deși în Python este posibilă programarea orientată pe obiecte, un “dezavantaj” ar fi faptul că
toate funcțiile și variabile definite într -o clasă sunt publice (lipsa encapsulării). Programatorii
sunt sfătuiți să folosească doar f uncțiile sau variabile puse la dispoziție de către biblioteca

13
folosită și să nu își bazeze codul pe alte metode decât cele recomandate întrucât acestea se pot
schimba în versiuni ulterioare. Spre exemplu, exisă posibilitatea suprascrierii funcțiilor
__setattr__, __getattr__ sau __delattr__ pentru a preveni modificarea, accesarea sau
ștergerea tuturor variabilelor definite într-o clasă.

import math
class MyComplex :
"""A complex number""" # Class documentation
classvar = 0.0 # A class attribute, not an instance one
def phase(self): # A method
return math.atan2(self.imaginary , self.real)
def __init__ (self): # A constructor
"""A constructor"""
self.real = 0.0 # An instance attribute
self.imaginary = 0.0
c1 = MyComplex ()
c1.real = 3.14 # No access protection
c1.imaginary = 2.71
phase = c1.phase() # Method call
c1.undeclared = 9.99 # Add an instance attribute
del c1.undeclared # Delete an instance attribute

print vars(c1) # Attributes as a dictionary
vars(c1)["undeclared2" ] = 7.77 # Write access to an attribute
print c1.undeclared2 # 7.77, inde ed

MyComplex .classvar = 1 # Class attribute access
print c1.classvar == 1 # True; class attribute access, not an
instance one
print "classvar" in vars(c1) # False
c1.classvar = -1 # An instance attribute overshadowing the
class one
MyComplex .classvar = 2 # Class attribute access
print c1.classvar == -1 # True; instance attribute acccess
print "classvar" in vars(c1) # True

14

>>> class Unchangable :
… def __setattr__ (self, name, value):
… print "Nice try"

>>> u = Unchangable ()
>>> u.x = 9
Nice try
>>> u.x

Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: Unchangable instance has no attribute 'x'

>>> class HiddenMembers :
… def __getattr__ (self, name):
… return "You don't get to see " + name

>>> h = HiddenMembers ()
>>> h.anything
"You don't get to see anything"

15
Un mic exemplu de programare funcțională în Python [14]:
unsorted = [('b', 6), ('a', 10), ('d', 0), ('c', 4)]

# Sort on the second tuple value (the integer).
print(sorted( unsorted , key=lambda x: x[1]))
>> [('d', 0), ('c', 4), ('b', 6), ('a', 10)]

values = [1, 2, 3, 4, 5]

# Note: We convert the returned map object to
# a list data structure.
add_10 = list(map(lambda x: x + 10, values))
add_20 = list(map(lambda x: x + 20, values))

print(add_10)
>> [11, 12, 13, 14, 15]

print(add_20)
>> [21, 22, 23, 24, 25]

values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Note: We convert the returned filter object to
# a list data structure.
even = list(filter(lambda x: x % 2 == 0, values))
odd = list(filter(lambda x: x % 2 == 1, values))

print(even)
>> [2, 4, 6, 8, 10]

print(odd)
>> [1, 3, 5, 7, 9]

values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Map.
add_10 = [x + 10 for x in values]
print(add_10)
>> [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

# Filter.
even = [x for x in values if x % 2 == 0]
print(even)
>> [2, 4, 6, 8, 10]

Începând cu anul 2003 Python a fost mai mereu una din primele 10 cele mai folosite limbaje
de programare, iar începând cu Ianuarie 2018 se află pe locul 4 după Java, C și C++. Python
poate fi folosit ca limbaj de scripturi pentru aplicații web (mod_wsgi pentru Apache, Django,
Flask, etc), în programarea științifică folosind bibliotecile NumPy, SciPy și Matplotlib, în
proiectele de inteligență artificială, în special NLP (procesarea limbajului natural). Mai mult,

16
acesta a fost folosit (sinonim) și în proiectarea anumitor componente din unele programe
software, de exemplu : GIMP, Blender, FreeCad și Inkscape.

1.2.5 Flask

Am ales acest framework întrucât aveam nevoie de sistem de gestiune/coordonare a cererilor
simplu, rapid și ușor de folosit. Comparativ cu Django spre exemplu, inițializarea unui simplu
Hell
o World este mult mai rapidă fiind necesar un număr mult mai mic de fișiere.
Flask a plecat de la o glumă de 1 Aprilie a lui Armin Ronacher și a ajuns să ajungă cel mai
popular micro-framework de pe Github începând cu anul 2016. Flask este văzut ca un micro-
framework pentru că nu conține niciun nivel de abstractizare a bazei de date, nu are validare
de formulare sau alte componente pe care Django de exemplu le conține. [15]
Unul din avantajele pe care le oferă acest framework o reprezintă faptul că nu consumă mult
spațiu, este ușor de instalat și este necesar foarte puțin cod pentru a începe dezvoltarea unui
API ( Application Programming Interface ) spre exemplu. Deși acestuia îi lipsesc componente
importante, biblioteci terțe pot fi descărcate și folosite pentru a mări puterea pe care o oferă
acest framework (Flask-SQLAlchemy ce acționează ca un ORM pentru baza de date, Flask-
SocketIO pentru o comunicare bidirecțională între server și client prin intermediul sockeților,
ș.a.m.d.).
Funcționalitățile pe care le oferă Flask sunt următoarele :
 conține server de dezvoltare și de depanare
 conține suport pentru testare unitară
 serviciu RESTful
 oferă suport pentru cookie-uri sigure (sesiuni pe partea de client)
 compatibil cu WSGI 1.0
 compatbil cu Google App Engine
 biblioteci terțe ce îmbunătățesc funcționalitatea framework-ului
Destul de multe companii mari folosesc Flask ca și serviciu REST. Printre ele se număra :
Pinterest, Linkedin, Netflix, Uber, Reddit și Lyft. [16]
Așa cum spuneam mai înainte, Flask este ușor de instalat și necesită semnificativ mai puțin
cod de scris față de Django pentru a rula un simplu Hello World :

17

from flask import Flask
app = Flask(__name__ )

@app.route ("/")
def hello():
return "Hello World!"

if __name__ == "__main__" :
app.run()

1.3 Modelul de arhitectură MVVM

Modelul de arhitectură Model-View-ViewModel se pară obiectele din cadrul unei aplicații în 3
tipuri: Model, View sau ViewModel. Cele 3 componente sunt separate între ele, dar
comunică unul cu celălalt. Astfel acestea pot fi modificate fără a influența funcționalitatea
celorlalte.
MVVM este o variație a MVP-ului cu diferența că cel din urmă abstractizează un view în așa
fel încât acesta nu este dependent de o platformă de interfață cu utilizatorul (UI platform).
Acest model a fost inventat de către 2 arhitecți de la Microsoft pentru a simplifica interfețele
bazate pe programarea orientată pe evenimente (ex. : cursul execuției unui pr ogram este dat de
acțiunile pe care le face un utilizator). MVVM a fost apoi introdus în Windows Presentation
Foundation (motorul grafic .NET al lui Microsft) și Silverlight (un framework asemănător cu
Adobe Flash care este acum depreciat) . [17][18]
Fig. 2. Schema Model-View-ViewModel
Google a introdus și susținut acest model odată cu introducerea claselor pentru ViewModel în
anul 2017, până atunci arhitectura recomandată fiind MVP. Diferența majoră dintre cele două
modele o reprezintă legătura strânsă pe care o are View-ul cu Presenter-ul în primul caz.
Pentru fiecare Activity/Fragment este necesar ca în Presenter-ul fiecăruia să existe o instanță
a primului. Aici intervin mai multe probleme :
 Odată cu creștere complexității aplicației, această relație trebuie întreținută de fiecare
dată când se modifică ceva.
 Dacă activitatea sau fragmentul sunt distruse (ex .: din cauza memoriei), atunci
presenter-ul rămâne cu instanța primeia, care acum este null/nil. Pentru rezolvarea
acestei probleme este necesară introducerea conceptului de LifecycleOwner și

18
LifecycleObserver și scrierea de cod care să elibereze din memorie Presenter-ul
respectiv.
Pe de altă parte, un ViewModel este “lifecycle aware” și referința acest uia este eliberată din
memorie atunci când activitatea sau fragmentul sunt distruse. Mai mult, un ViewModel de
cele mai multe ori nu conține o referință la context -ul în care a fost creat (dacă este declarat
corespunzător, acesta poate să dispună de un con text al aplicației, dar nu a activității sau
fragmentului care l -a creat).
În următoarele pagini voi vorbi despre fiecare, raportat la sistemul de operare Android.

1.3.1 Modelul

Modelul conține datele specifice unei aplicații (ex .: utilizatori, cărți, fi lme, produse, scoruri,
ș.a.m.d.) și logica necesară prelucrării acestora. Pentru Android, se poate folosi biblioteca
Room ce asigură o comunicare ușoară și sigură cu baza de date înfășurător . Deși are destule
funcționalități (chei primare, chei străine, me tode de INSERT/UPDATE/DELETE pe obiecte,
etc), acesta nu este un ORM ( Object Relational Mapper ) ci un înfășurător ( “wrapper” ) peste
SQLite. Un exemplu de model ar fi clasa Carte dintr -o aplicație de tip ebook reader.

1.3.2 View- ul

View-ul este interfața pe care o vede utilizatorul. Rolul principal al acestuia este să afișeze
informațiile din model, iar apoi, sa le permită dacă este cazul, modificarea lor. Acesta poate
să fie un Activity, Fragment sau orice alt view personalizat (custom). Obligațiile pe care le
are acesta este să se asigure că utilizatorul poate naviga fără probleme ; mai mult, view -ul
trebuie să “asculte” după modificări ce sunt făcute în ViewModel. Avantajul pe care această
metodă este că permite testarea unitară a celor două componente fă ră prea multe bătăi de cap.
În același timp, daca sunt modificări ce trebuie făcute la interfață, cealaltă componentă
(ViewModel) nu este afectată.

1.3.3 ViewModel-ul

ViewModel-ul este văzut ca un model pentru View, o abstractizare a acestuia. ViewModel-ul
extrage datele necesare din Model, aplică logica necesară (ex : salvarea unei imagini/cărți/joc,
adăugarea la lista de favorite, etc) și expune informațiile relevante prin intermediul design
pattern-ului Observable/Observer pentru ca View -ul să le poată accesa. În Android acest
lucru se poate face folosind clasele LiveData și MutableLiveData ; dacă view- ul “ascultă”

19
aceste variable, atunci el va fi anunțat de fiecare dată când acestea se modifică. ViewModel-
ul pune la dispoziție informațiile și nu este interesat de cine le accesează. Acest lucru îl
constituie un avantaj întrucât poate fi testat separat.

1.3.5. Avantajele arhitecturii MVVM

1.3.5.1 Viteză de dezvoltare

MVVM asigură o dezvoltare rapidă și paralelă. Un programator poate lucra la partea de
interfață în timp ce altul scrie modelul și ViewModel-ul.

1.3.5.2 Schimbările nu trebuie să afecteze tot modelul

De-a lungul perioadei de dezvoltare pot apărea schimbări la interfață (cel mai probabil)
întrucât nu se poate stabili de la bun început ce ecrane/text/font/culori vor fi necesare. În timp
clientul/compania pot schimba funcționalități existente sau pot adăuga altele noi. Aceste
modificări nu ar trebui să afecteze modelul aplicației daca nu este cazul întrucât acestea sunt
separate.

1.3.5.3 Reutilizarea codului

Având în vedere că cele trei componente sunt independente, acestea pot fi reutilizate și în alte
proiecte. Un View poate fi refolosit pentru mai multe funcționalități diferite (ex .: resetarea
parolei sau schimbarea parolei unui utilizator). ViewModel -ul poate fi refolosit dacă cel puțin
două view -uri utilizează aceleași informații. La modele este puțin mai greu întrucât fiecare
aplicație are alt rol și deci o altă logică a datelor. Totuși, modelul de utilizator poate fi
refolosit sau cel puț in adaptat în funcție de nevoi și pentru alte proiecte.

1.3.5.4 Modularizarea proiectului

Întrucât componentele sunt independente între ele, acestea nu trebuie să știe ce face cealaltă.

20
1.3.6 Concluzii

MVVM este un model de arhitectură performant ce oferă o separare binevenită a codului,
aceasta aducând la rândul ei o mentenanță mai ușoară indiferent de cât de complex devine
proiectul în timp. MVVM combină avantajele separării funcționalităților oferită de MVP și în
același timp permite observarea modificărilor datelor din model. Rezultatul obținut este o
arhitectură unde ViewModel-ul realizează cât mai multe operații folosindu-se de metodele
puse la dispoziție de Model, minimizându-se astfel logica din View.

1.4 Sisteme de recomandare

În ultimii ani sistemele de recomandare au devenit din ce în ce mai populare în zone precum :
filme, muzică, cumpărături/produse, știri, căutări, restaurante, întâlniri online (online dating)
ș.a.m.d. Acestea sunt folosite pentru a personaliza experiența pe care o au util izatorii care
navighează pe un site, folosesc o aplicație, caută locuri de vizitat, etc, astfel, aceștia găsesc
mai ușor informațiile de care au nevoie și în plus, cresc șansele de a cumpăra acel produs,
fiind astfel benefic pentru ambele părți. [19][20]
Oamenii au gusturi diferite, dar de regulă acestea se aseamănă cu ale persoanelor din jurul
lor, formând astfel un model. Sisteme de recomandare încearcă sa găsească aceste modele și
să prezică ce altceva unei persoane i-ar mai plăcea. Ca să înțelegeți cât de puternice și
indispensabile sunt sistemele de recomandare pentru unele companii, uitați câteva exemple :
Conform unui studiu făcut de McKinsey de prin anul 2012, 75% din filmele și serialele pe
care utilizatorii le urmăresc pe Netflix vin de la sistemul lor de recomandare. Mai mult,
conform executivilor Carlos A. Gomez-Uribe și Neil Hunt, sistemul de recomandare ajută
compania să economisească un miliard de dolari în fiecare an, bani pe care i-ar fi dat pe
marketing.
Amazon pe de altă parte nu folosește un singur sistem de recomandare, ci mai multe : le arătă
utilizatorilor ce alte produse au mai fost cumpărate cu ce au deja în coș, le sugerează produse
similare cu cele pe ca re le-au vizitat și le arată ce produse mai bune pot să cumpere ținând
cont de cele deja deținute. Mai mult, folosesc un sistem de recomandare ce sugerează produse
pe care utilizatorii le pot cumpăra, iar apoi le trimit mail -uri cu diferite oferte. Amazon a
declarat că 35% din venitul lor este de la sistemele de recomandare pe care le folosesc. De
asemenea, acestea le -au crescut încasările cu 29%, producând astfel un venit de 135.99
miliarde de dolari în 2016.
Un alt gigant al cumpărăturilor online care folosește sisteme de recomandare este BestBuy. În
anul 2015 au început să folosească această tehnologie, iar în al doilea trimestru din 2016
vânzările le crescuseră cu 23.7%.

21
Toată lumea folosește sau a folosit la un moment dat Youtube-ul pentru a asculta muzică,
spectacole înregistrate, competiții sportive, trailere pentru filme, etc. La CES 2018, CPO
(Chief Product Officer) Neal Mohan a dat o declarație cum că mai mult de 70% din ceea ce o
persoană urmărește pe Youtube vine de la unul din sistemele de recomandare pe care
compania le folosește. În cazul în care vine vorba de aplicația mobilă, în medie o sesiune
durează mai mult de 60 de minute, totul datorându-se sistemelor lor de recomandare.

1.4.1 Prezentare generală

Sistemele de recomandare sunt în general de două tipuri : filtrare colaborativă ( collaborative
filtering) și filtrare bazată pe conținut ( content -based filtering ). În următoarele pagini voi
descrie fiecare metodă raportată la tema aleasă.

1.4.2 Filtrarea bazată pe conținut

Filtrarea bazată pe conținut folosește un set de caracteristici ale unui film pentru a recomanda
alte filme asemănătoare. Cum funcționează asta? Destul de simplu. Sa presupunem că un film
are următoarele caracteristici : procentajul de acțiune, comedie, romanță, anul apariției , tipul
ecranizării (2D/3D/Imax) și durata. Utilizatorii dau note pe o scară de la 0 la 5 la filmele pe
care le-au văzut, conturându -se astfel profilul fiecăruia. Sistemul de recomandare se uită la
filmele văzute de fiecare utilizator și caută filme asemăn ătoare. Spre exemplu, daca o
persoană a dat rating de 4+ la filme cu un procentaj mare de acțiune, care sunt 3D și care au
apărut în ultimii 3 -5 ani, atunci înseamnă ca sunt șanse mai mici ca acea persoană sa îi placă
filme mai vechi ce au multă comedie sa u sunt pline de scene romantice. Astfel, algoritmul
sugerează filme noi, în care genul predominant e acțiunea și de preferat să fie și 3D (poate
utilizatorul are un televizor 3D acasă).
Dezavantajul principal al acestei metode este că este necesar un set de caracteristici cât mai
precis pentru fiecare film. Procentajul de acțiune/comedie/romanță este destul de greu de
obținut spre exemplu. Ar trebui ca cineva să urmărească fiecare film și să dea niște numere. O
variantă alternativă, în cazul în care aceste date nu sunt disponibile, ar fi integrarea acestei
opțiuni în aplicația originală : fiecare utilizator dă o notă și un procentaj pentru fiecare gen,
formându -se astfel un set de caracteristici care sunt destul de precise (cu excluderea outlayer –
elor dacă este cazul desigur, pentru ca altfel un număr mic de utilizatori ar putea să strice
precizia algoritmului dacă numerele pe care le dau sunt aleatoare).
Un alt dezavantaj ar fi că această metodă nu asigură descoperirea de noi filme de către
utilizatori. Fiecare va primi întotdeauna recomandări pentru aceleași tipuri de filme. Asta nu
le va permite utilizatorilor să încerce și alte filme din alte categorii pe care alte persoane cu
preferințe asemănătoare le plac. Aici intră în joc filtrarea colaborativă.

22
1.4.3 Filtrarea colaborativă

Acesta este algoritmul pe care l-am folosit în cadrul acestei teme de licență întrucât scalează
bine pe numărul meu de date și oferă o acuratețe mai mare. Ce nu am implementat în schimb
este colectarea explicită sau implicită a datelor, mai exact, utilizatorul nu este rugat/obligat să
dea note sau să marcheze ce filme a văzut, dar el are opțiunile acestea.
Filtrarea colaborativă construiește un model pe baza filmelor pe care un utilizator le-a văzut
și le-a dat note și recomandă alte filme pe baza asemănării intereselor dintre persoane. Acest
algoritm este printre cele mai folosite, mai ales pentru că are următoarea proprietate special ă:
acesta poate să învețe caracteristicile filmelor , ceea ce înseamnă că nu este necesar un set de
date cu cât mai multe informații despre fiecare film.
Acest algoritm se împarte în 2 tipuri :
 Filtrare colaborativă bazată pe memorie
 Filtrare colaborativă bazată pe model

Filtrarea colaborativă bazată pe memorie este și ea la rândul ei de două tipuri :
 Filtrare colaborativă film-film care ia un film, caută utilizatori care au văzut acel film,
iar apoi se uită la ce alte filme au mai văzut aceștia. Acesta poate fi descris în felul
următor: ”Alți utilizatori au văzut și …”
 Filtrare colaborativă utilizator-film ia un utilizator, caută alți utilizatori care au gusturi
asemănătoare (bazate pe o matrice de similaritate a notelor) și recomandă filme pe
care acești utilizatori le-au văzut.
Un algoritm de tipul acesta este ușor de implementat și produce rezultate acceptabile.
Dezavantajul principal este că nu scalează bine la scenarii din viața de zi cu zi și nu rezolvă
problema în care un utilizator intră în sistem pentru prima dată ( “cold start problem” ).
Filtrarea bazată pe model este mult mai scalabilă, se descurcă mult mai bine cu matrici rare
(când sunt date puține) , dar din păcate este și ea afectată de problema menționată anterior .

Filtrarea colaborativă bazată pe model este bazată pe factorizarea matricilor. Acest algoritm
este cel mai întâlnit, în principal ca unul de tip învățare nesupervizată. Așa cum spuneam și
mai devreme, factorizarea matricilor este foarte folosită pentru sisteme de recomandare
pentru că scalează mult mai bine în viața reală. Scopul principal al filtrării colaborative
bazate pe model este să învețe caracteristicile filmelor și preferințele utilizatorilor,
aproximând cât mai bine matricea rară cu care pornește algoritmul. Când avem de-a face cu o
matrice rară, aceasta poate fi spartă în două matrici, una reprezentând filmele și
caracteristicile lor, iar cealaltă reprezentând utilizatorii și preferințele lor (în funcție de
caracteristicile filmelor). Astfel, matricea originală poate fi obținută în urma produsului celor

23
două. Algoritmul încearcă să aproximeze matricea originală învățând cât mai bine
caracteristicile filmelor și preferințele utilizatorilor.
Un sistem de filtrare colaborativă bazată pe model de regulă face distincție între datele
colectate implicit și cele colectate explicit. Exemple de date colectate implicit :
 analizarea filmelor la care un utilizator se uită prin aplicație (citește descrierea, se uită
la trailer, etc)
 de câte ori un utilizator a intrat pe pagina unui film
 filmele pe care un utilizator le -a marcat că le-a văzut
 de câte ori merge un utilizator la cinema într-o săptămâna, respectiv lună , precum și la
ce ore
 integrarea unui sistem de socializare ce permite invitarea altor prieteni cu care un
utilizator poate merge la film. Analizarea și sugererarea de persoane cu care un
utilizator poate să meargă la un film bazată pe prietenii acestuia.
Exemple de date colectate explicit :
 solicitarea unui utilizator să dea o notă la un film
 solicitarea unui utilizator să sorteze o listă de filme în funcție de preferințele acestuia
 solicitarea unui utilizator să aleagă filmul care i-a plăcut cel mai mult dintre două
alese de aplicație
 solicitarea unui utilizator să se aboneze la filmele pe care vrea să le vadă și urmează
să apară
Chiar dacă această metodă poate învăța atât caracteristicile filmelor cât și preferințele
utilizatorilor, ea nu este lipsită de dezavantaje. Cel mai important dintre acestea este că
necesită multe informații despre un utilizator pentru a putea face recomandări bune, ceea ce
înseamnă că algoritmul nu va putea să recomande filme bune pentru o persoană care doar ce a
început să folosească aplicația. O soluție ar fi ca atunci când un utilizator nou intră in
aplicație, acestuia să îi fie afișat un ecran în care sa dea note la câteva filme pentru a putea să
îmbunătățească precizia algoritmului.
In ciuda acestor dezavantaje, filtrarea colaborativă bazată pe model este cel mai folosit
algoritm de către companiile mari. Facebook, MySpace, LinkedIn și alte rețele de socializare
folosesc acest tip de algoritm pentru a recomanda prieteni, grupuri sau alte conexiuni sociale,
urmărind rețeaua de prieteni pe care un utilizator o are. Twitter pe de altă parte folosește CF
pentru a sugera persoanelor pe cine să urmărească.

24
1.4.4 Sisteme de recomandare hibride

În urma cercetărilor din ultimii ani, s-a demonstrat că în anumite cazuri sisteme de
recomandare hibride sunt mult mai performante. Un sistem hibrid combină filtrarea bazată pe
conținut cu cea colaborativă pentru a mări precizia algoritmului final. O metodă de combinare
ar fi rularea celor doi algoritmi separat, iar apoi adăugarea proprietăților primului algoritm la
al doilea sau invers. De cele mai multe ori asta poate rezolva sau ameliora problema fiecărui
algoritm dacă este luat separat (ex .: cold boot , matrici rare, scalare).
Un exemplu foarte bun de sistem de recomandare este cel folosit de Netflix . Site-ul face
recomandări comparând filmele și serialele la care se uită mai mulți utilizatori cu aceleași
preferințe (filtrare colaborativă), dar și sugerează filme în funcție de preferințele fiecărui
utilizator (filtrare bazată pe c onținut).

1.4.5 Premiul oferit de Netflix

Între anii 2006 și 2009, Netflix a sponsorizat o competiție, oferind 1 milion de dolari echipei
care reușea să facă un sistem de recomandare care era cu cel puțin 10% mai bun decât cel al
companiei, folosind un set de date cu 100 de milioane de rating-uri (note/recenzii). Deși în
2009 a fost aleasă echipa câștigătoare, Netflix a considerat că algoritmul lor nu este cu mult
mai bun față de cel realizat în 2007. Cel din urmă folosea o combinație de 107 de metode
algoritmice diferite pentru a face o predicție cât mai precisă. Printre acestea se numără și
factorizarea SVD a matricilor ( Singular Value Decomposition matrix factorization ).

25
2. APLICAȚIA

2.1 Prezentare generală

De fiecare dată când doream să mă uit pe programul cinema-urilor, în special cel de la
CinemaCity, mă chinuiam cu site-ul cinema-urilor întrucât acesta nu era responsive . Mai
mult, dacă voiam sa vad descrierea sau orele când rulează un anumit film la mai multe
cinema
-uri trebuia să reiau procesul de fiecare dată când schimbam cinema-ul. Astfel, în vara
anului trecut am luat decizia să încerc sa îmbunătățesc această experienț ă.
Aplicația Movieplex poate fi rulată pe orice dispozitiv Android care are cel puțin versiunea
4.2. Din cauza anumitor probleme de compatibilitate între Android 5 și versiunile mai vechi,
aplicația nu va arăta la fel de bine pe telefoanele ce nu au cel puțin Lollip op instalat.

2.2 Scopul

Movieplex are scopul de a facilita procesul de cumpărare/rezervare de bilete la cinema-urile
disponibile prin punerea la dispoziție o interfață prietenoasă cu un număr mare de
funcționalități. Utilizatorii pe lângă faptul ca pot cumpăra și rezerva bilete, aceștia pot fi la
curent cu ultimele filme apărute (deschizând aplicația sau folosind sistemul de abonare pe
care îl oferă Movieplex ), pot vedea filmele care urmează să înceapă în scurt timp precum și
cele care doar ce au început prin intermediul unei hărți interactive ce separă fiecare ecranizare
pe cinema folosind un algoritm de clustering și nu în ultimul rând, aceștia au la dispoziție mai
multe acțiuni pe care le pot face : marchează filmele pe care le -au văzut, le dau note sau își
construiesc o bibliotecă cu filme pe care vor să le vadă prin intermediul opțiuni de adăugare
la favorite.
Movieplex poate fi folosită de orice persoană, neexistând practic o restricție în utilizarea ei.
Din păcate cei cu telefoane iOS nu pot rula aplicația, dar având în vedere că în primul
trimestru al acestui an 85.9% din toate telefoanele de pe piață rulau Android, nu cred că este
o problemă foarte mare [21]. Totuși dacă va exista o cerere destul de mare, atunci o aplicație
de iOS poate fi făcută sau se poate încerca dezvoltarea unei aplicații folosind Flutter (un sdk
pus la dispoziție de către Google ce permite construirea de aplicații ce rulează pe ambele
platforme).

26
2.3 Funcții

Aplicația are două tipuri de utilizator : cel conectat și cel neconectat. Pentru al doilea tip de
utilizator, Movieplex oferă următoarele funcționalități :
 Vizualizarea ultimelor filme apărute, cele care urmează sa fie lansate precum și un top
cu cele mai bune filme
 Vizualizarea programului cinema-urilor disponibile (câte unul pe rând sau mai multe
în paralel)
 Vizualizarea listei cu toate filmele apărute începând cu anul 2017, împreună cu toate
detaliile despre ele
 Căutarea unui film după nume, precum și ordonarea filmelor după nume sau data
apariției
 Vizualizarea listei cu filmele care urmează să înceapă sau au început de scurt timp (- 5
– 15 min)
 Setarea unui memento pentru o anumită ecranizare ( ex.: Avengers Infinity War la
București AFI T IMAX, Luni la 15 :20; utilizatorul va fi anunțat cu una respectiv două
ore înainte de a începe)

Pentru utilizatorul conectat, aplicația oferă în plus și următoarele funcționalități :
 Posibilitatea de a da o notă filmelor
 Posibilitatea de a marca filme văzute
 Posibilitatea de a se abona cu scopul de a fi anunțat când se lansează în cinema un
anumit film.
 Posibilitatea de a adăuga filmele la favorite
 Vizualizarea listei cu filme văzute și a listei cu filme favorite (pe car utilizatorul ar
vrea să le vadă)

2.4 Cazuri de utilizare

Cazurile de utilizare sunt o listă de acțiuni sau evenimente care definesc interacțiunile între
un rol și un sistem pentru a atinge un scop. Câteva exemple ar fi următoarele :

Utilizatorul neconectat deschide Movieplex
 Aplicația se deschide cu ecranul de noutăți
 Utilizatorul alege să vadă programul filmelor în București AFI

27
 Utilizatorul alege să vadă mai multe detalii despre o anumită ecranizare
 Utilizatorul alege să vadă mai multe detalii despre filmul respectiv
 Utilizatorul se întoarce la ecranizare
 Utilizatorul setează un memento pentru acea ecranizare

Utilizatorul neconectat își creează un cont nou
 Utilizatorul poate să își creeze un cont folosind un email și o parolă sau poate să se
conecteze cu Google sau Facebook
 După crearea contului utilizatorul se autentifică
 Utilizatorul deschide meniul și alege să vadă biblioteca cu filme
 Utilizatorul deschide ecranul cu detaliile unui film nelansat încă
 Utilizatorul se abonează la film urmând să fie anunțat când apare în cinema

Evaluarea unui film
 Utilizatorul conectat vede un film la cinema sau acasă
 Utilizatorul deschide aplicația și caută filmul văzut
 Utilizatorul conectat deschide ecranul de evaluare a filmului și dă o notă
 Utilizatorul este informat că nota a fost înregistrată cu succes

2.5 Diagrama de clase

Diagrama de clase prezintă structura sistemului, clasele care fac parte din el, precum și
relațiile dintre obiecte.

28
Fig. 3. Diagrama de clase

2.6 Schema bazei de date

Design-ul bazei de date se realizează determinând tipurile de date și relațiile dintre ele. De
regulă acesta este realizat de două persoane : una care cunoaște cum să separe tipurile de date,
iar cealaltă este un expert în domeniu. Relațiile între modele obținute trebuie stabilite de cele
două persoane întrucât expertul în domeniu de cele mai multe ori nu poate să își exprime clar
cerințele sistemului de care are nevoie întrucât nu cunoaște termenii specifici bazelor de date.
Cu alte cuvinte, proiectarea unei baze de date cuprinde următoarele etape :
 stabilirea scopului bazei de date
 clasificarea corectă a tipurilor de date
 distribuirea informațiilor pe tabelele corespunzătoare și împărțirea lor pe coloane
 stabilirea constrângerilor necesare: chei primare, chei străine precum și verificări

30
3. PREZENTAREA APLICAȚIEI

3.1 Prezentarea ecranelor

3.1.1 Noutăți

Aplicația se deschide cu ecranul de Noutăți ce prezintă ultimele filme apărute, cele care
urmează să fie lansate precum și un top cu cele mai populare 10 filme. Apăsarea pe orice
film, indiferent de categorie, va deschide un ecran în care utilizatorul poate vedea toate
detaliile despre filmul respectiv : trailer, anul apariției, durata, genul, descriere, rating,
numărul de vizualizări, actori, regizor, premiera și clasificarea (18+, 16+, acordul părinților,
etc). Tot în ecranul de noutăți, utilizatorul poate vedea actorii pentru filmele din categoriile
“Noi” și “În curând ”, și nota filmului pentru cele din categoria “Populare”.
Ca și detalii de implementare, fiecare listă este realizată folosind un RecyclerView cu
LinearLayoutManager , dar cu orientări diferite așa cum se observă. La primele 2 categorii,
pentru a nu rămâne lista derulată între 2 filme, am folosit un LinearSnapHelper custom ce
aliniază la stânga cel mai apropiat element din listă. Datele afișate sunt extrase din baza de
date folosind un framework-ul Paging Library împreună cu un ViewMode l și tipul de date
LiveData. Pe scurt cele 3 funcționează împreună pentru a afișa pe ecran datele din baza de
date pe măsură ce utilizatorul derulează printre filme. Pentru a nu cauza încetiniri din cauza
interogărilor pe baza de date, se folosește un pool de prefetch ce încarcă filme din baza de
date înainte ca utilizatorul să ajungă la ultimul deja încărcat.

31

Fig. 5. Ecranul de Noutăți
3.1.2 Filme

Ecranul de filme conține lista cu toate filmele disponibile în aplicație, mai exact, majoritatea
filmelor apărute începând cu anul 2017. Ultimele apărute sunt cele disponibile în cinema,
celelalte sunt afișate pentru a putea construi un model cât mai precis pentru sistemul de
recomandare. Pentru fiecare film sunt afișate câteva detalii minimale : poster-ul, numele și
numărul de persoane ce au văzut filmul. În cadrul acestui ecran, utilizatorii pot să caute un
film după nume sau pot sorta filmele după 4 mai multe criterii așa cum reiese și din imagine.
Apăsarea pe orice film duce utilizatorul la un ecran în care sunt afișate mai multe detalii
despre el.
La implementare nu sunt prea multe de zis. Am folosit tot un RecyclerView , iar împreuna cu
Paging Library , un ViewModel ce conține obiecte de tip LiveData, informațiile sunt afișate
pe ecran.

32

Fig. 6. Ecranul de Filme
3.1.3
Program

Acest ecran este al doilea cel mai important, daca nu chiar primul. În el este afișat programul
filmelor în cinema-urile selectate (față de site, aici se pot vedea mai multe cinema-uri în
paralel pentru a compara orele și a o alege pe cea mai potrivită). Fiecare element din listă
prezintă numele filmului, tipul cinema-ului ( CinemaCity , GrandCinema sau CineGlobe )
precum și locația (București Sun Plaza, București AFI, Constanța City Park, etc) și data și ora
ecranizării. Programul este împărțit pe zilele săptămâni pentru a nu încărca ecranul principal
prea mult. Tot în acest ecran, utilizatorii pot căuta un film după nume și pot vedea astfel la ce
ore rulează un anumit film într-o anumită zi la cinema-urile selectate. Mai mult, programul
poate fi sortat după câteva criterii în caz că se dorește o altă aranjare a acestuia. Apăsarea pe
o ecranizare deschide un ecran în care utilizatorii pot vedea câteva detalii despre filme
precum și informații despre ecranizare : la ce cinema rulează, ora și data dar și tipul
ecranizării. De aici utilizatorii pot cumpăra bilete sau pot face rezervări, cele din urmă fiind

33
disponibile doar pentru CinemaCity și dacă mai sunt cel puțin 30 de minute până la începerea
filmului.
Referitor la implementare, pentru afișarea datelor am folosit aceeași tehnologie ca și în
ecranele precedente. Zilele săptămânii sunt implementate ca un TabLayout , iar programul în
sine este realizat folosind Fragmente într-un ViewPager și FragmentStatePagerAdapter . Cel
din urmă distruge fragmentele care nu sunt vizibile și care sunt la o diferență de cel puțin 2
ecrane față de cel curent ( ex.: dacă astăzi este vineri, iar programul de duminică este selectat,
atunci programul de sâmbătă și luni se află în memorie pentru o tranziție fluidă, i ar cel de
vineri, marți, miercuri și joi sunt “distruse” și urmează a fi recreate dacă este necesar).

Fig. 7. Ecranul de Program

34
3.1.4 Hartă

În ecranul de hartă sunt afișate filmele care au început cu cel mult 10 minute în urmă sau
urmează să înceapă în următoarele 25 de minute. Prima dată când deschideți ecranul de hartă
Movieplex va cere permisiuni de locație. Locația nu va fi trimisă la server decât dacă optați
pentru acest lucru în ecranul de profil. Chiar dacă poziția utilizatorului este afișată pe hartă,
daca el nu are opțiunea activată aceasta nu va fi trimisa la server. Daca gps-ul este dezactivat
sau dacă utilizatorul nu a acceptat permisiunile de locație, atunci harta va fi centrată pe
Romania.
Am gândit acest ecran cu următoarea idee în minte : te afli într -un mall, ai ieșit cu prietenii
sau familia să mâncați ceva sau ați ieșit la cumpărături și trecând pe lângă cinema vă gândiți
că ați putea merge la un film sau vedeți un trailer care vă captează atenția și vă hotărâți să
mergeți la acel film ; astfel, deschideți aplicația, văd duceți pe ecranul de hartă, dacă aveți
gps-ul activat și permisiunile date atunci o să vă deschidă harta direct la locația voastră unde
puteți vedea ce filme urmează să înceapă în următoarele minute.
Apăsarea pe oricare pin duce la deschiderea unui popup/modal ce afișează o listă cu filmele
care rulează la cinema-ul respectiv. Daca utilizatorul apasă pe un film atunci un panou glisant
va fi afișat în partea de jos a ecranului. Acesta poate trage de el în sus pentru a vedea mai
multe informații despre film precum și detalii despre ecranizare. În cazul în care harta este
mișcată și se dorește recentrarea ei, se poate apăsa pe butonul din dreapta jos, harta urmând
astfel să fie repoziționată pe Romania.
Harta a fost implementată folosind framework- ul de la GoogleMaps . Cinema-urile a căror
locație se află în afara ecranului nu vor fi afișate pe hartă. Harta va fi actualizată cu noile
cinema-uri/pin-uri în momentul în care utilizatorul nu mai mișcă harta, economisindu-se
astfel resurse (ram, internet, cpu load atât mobile cât și server side ).

35

Fig. 8. Ecranul de Hartă

36

Această opțiune este vizibilă doar
pentru utilizatorii conectați
Se apasă aici pentru a deschide
ecranul de P rofil
Meniul
Fig. 9. Meniul de detalii film din
ecranul de hartă

37

override fun onCameraIdle() {
val apiService = ApiClient.getClient().create(ApiInterface:: class.java)

val currentScreen: LatLngBounds = mMap!!.projection.visibleRegion.latLngBounds
var date = SimpleDateFormat( "dd/MM/yyyy HH:mm" ,
Locale.getDefault()).format(Date())

val b: Bitmap = BitmapFactory.decodeResource(resources,
R.drawable.movie_marker);
val bulletSize = b.height

val call: Call<MovieTimesMapResponse> =
apiService.getMovieTimesForMap(currentScreen.southwest.longitude,
currentScreen.northeast.longitude, currentScreen.northeast.latitude,
currentScreen.southwest.latitude,
bulletSize.toFloat(), date)

call.enqueue( object : Callback <MovieTimesMapResponse> {
override fun onResponse(call: Call<MovieTimesMapResponse>?, response:
Response<MovieTimesMapResponse>?) {
if (response != null && response.isSuccessful) {
Log.e("response map" , Gson().toJson(res ponse.body()))

if (response.body()!!.status.equals( "ok")) {
val clusters: ArrayList<Cluster> = response.body()!!.data!!
context?.let { context ->
MapUtils.addMovieTimesOnMap(m Map, context, clusters)
}
} else {
context?.let { context ->
Utils.showToastMessage(context,
getString(R.string.something_went_wrong))
}
}
}
}

override fun onFailure(call: Call<MovieTimesMapResponse>?, t: Throwable?) {
t?.printStackTrace()
}
})
}

3.1.4.1 Algoritmul de clustering

Întrucât la un zoom mic pin-urile cinema-urilor s-ar suprapune, am implementat un algoritm
ce permite gruparea lor într-unul singur în aceste condiții. Aplicația de mobil trimite la server
zoom-ul curent împreună cu dimensiunea pin-ului (aceasta diferă pentru fiecare telefon chiar
dacă imaginea este aceeași, întrucât Android-ul o scalează în așa fel încât să aibă aceeași
dimensiune raportată la mărimea ecranului pentru fiecare telefon) și coordonatele longitudinii
stânga și dreapta precum și latitudinii sus și jos. Server-ul extrage din baza de date
ecranizările de la cinema-urile care se află în zona dată de coordonate și apoi aplică
algoritmul de clustering pe aceste elemente. La final returnează un răspuns ce conține
clusterele formate și numărul lor. Fiecare cluster din răspuns conține o listă cu ecranizările ce
se află pe pinul respectiv la zoom-ul curent.

38

movie_times = db.session. query(MovieTime) \
.join(MovieTime.location) \
.filter(Location. in_loc(locations)) \
.filter(MovieTime. around_hour( date)) \
.all()

distanceHoriz = Utils.distance_between_2_gps_coord (float(bottom), float(left),
float(bottom), float(right))
distanceVert = Utils.distance_between_2_gps_coord (float(bottom), float(left),
float(top), float(left))

avg_diff = (distanceHori z + distanceVert) / 2
radius = avg_diff / float(bullet_size)
clusters = Clustering. calculate_clusters (movie_times, 8 * radius)

def create_clusters (movie_times):
clusters = [Cluster() for i in range(len(movie_times))]

for i in range(len(clusters)):
clusters[i].id = i
clusters[i].lat = movie_times[i].location.lat
clusters[i].lng = movie_times[i].location.lng
clusters[i].count = 1
clusters[i].ok = False
clusters[i].movie_times. append(movie_times[i])

return clusters

def union_clusters (movie_times, radiu s):
clusters = []
clusters = create_clusters (movie_times)

for i in range(len(clusters) – 1):
ci = clusters[i]
if ci.ok is False:
for j in range(i + 1, len(clusters)):
cj = clusters[j]
if(cj.ok is False and ci.distance_to (cj) < radius):
ci.ok = True
ci.count += 1
cj.ok = True
ci.merge_cluster (cj)

return clusters

def calculate_clusters (movie_times, radius):
clusters = union_clusters (movie_times, radius)

final_clusters = []

for i in range(len(clusters)):
if(clusters[i].count > 1):
final_clusters. append(clusters[i])
elif clusters[i].count == 1 and clusters[i].ok is False:
final_clusters. append(clusters[i])

return final_clusters

def distance_between_2_gps_coord (lat1, lng1, lat2, lng2):
"""
Calculate the great circle distance between two points
on the earth (specified in decimal degrees)
"""
# convert decimal degrees to radians
lng1, lat1, lng2, lat2 = map(math.radians, [lng1, lat1, lng2, lat2])

# haversine formula
dlon = lng2 – lng1
dlat = lat2 – lat1
a = math.sin(dlat / 2) ** 2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon /
2) ** 2
c = 2 * math.asi n(math.sqrt(a))
r = 6371 # Radius of earth in kilometers. Use 3956 for miles
return c * r

39
3.1.5 Setări

Acest ecran momentan conține doar o singură opțiune și anume cea de schimbarea temei
aplicației. Movieplex dispune de 3 teme prestabilite și pentru schimbarea lor este necesară
reinițializarea aplicației întrucât nu se poate aplica un nou stil la view-urile existente într-o
manieră ce nu implică mult cod și este și prietenoasă pentru utilizator. Am ajuns să
implementez această funcționalitate întrucât nu mă hotăram ce paletă de culori să aleg și am
ajuns la concluzia că mai bine pun câteva teme și las utilizatorii să o aleagă pe cea care le
place cel mai mult. Încercând să implementez acest lucru, mi-am dat seama de ce nu prea
sunt aplicații care oferă posibilitatea schimbării temei. Motivul principal este faptul că este
necesară adăugarea unui nou fișier drawable pentru fiecare temă. Ca să vă dau un exemplu,
dacă vreți să faceți un buton cu colțuri rotunde aveți nevoie de un fișier drawable în care să
specificați ce border radius sa aibă fiecare colț. Acum, dacă ați dori să adăugați 2 teme atunci
trebuie să faceți 2 fișiere drawable cu colțuri rotunde, iar în fiecare să puneți ce culoare vreți.
Se poate sări în unele cazuri peste acest pas folosindu-se în layout atributul backgroundTint
setând astfel culoarea pe care o vreți, doar că acea culoare de fapt trebuie să fie un atribut
declarat de voi într-un fișier attrs.xml și apoi specificat pentru fiecare temă în parte. Problema
cu această metodă este că nu merge decât daca view-urile sunt declarate folosind Support
Library pentru Android. Din experiența mea, nu la toate view-urile acest lucru este posibil
acest lucru întrucât nu toate au acel atribut. Pe de altă parte, de la Android 5 în sus se poate
folosi atributul declarat in attrs.xml direct în fișierul drawable , dar asta înseamnă că aplicația
va funcționa doar pe 84.7% din dispozitive, iar la momentul actual Google recomandă ca
minimum api version să fie 17 (Android 4.2) adică 97.8% din telefoanele cu Android. [22]

40

Fig. 10. Ecranul de Setări
3.1.6 Bibliotecă

Acest ecran este disponibil doar utilizatorilor conectați și conține o listă cu filmele pe care
aceștia le-au văzut, precum și una cu filmele pe care aceștia le-au urmărit sau le urmăresc
(follow). Apăsarea pe oricare element deschide un ecran cu detaliile filmului respectiv.
Biblioteca poate fi sortată după mai multe criterii în cazul în care se dorește o alta așezare a
filmelor. Mai mult, utilizatorii pot să caute filme după nume.
La implementarea acestui ecran am folosit aceleași tehnologii ca și în celelalte ecrane care iau
informațiile din baza de date locală.

41

Fig. 11. Ecranul de Bibliotecă

3.1.7 Detaliile filmului

Acest ecran este cel care se deschide de fiecare dată când un utilizator vrea să vadă mai multe
informații despre un film. În fiecare ecran sunt afișate următoarele informații :
 Numele
 Anul lansării
 Genul
 Descrierea
 Nota
 Numărul de persoane care urmăresc filmul (vizibilă doar pentru filme nelansate încă)
 Numărul de vizualizări
 Actorii
 Regizorul

43
3.1.8 Detaliile ecranizării

Acest ecran se deschide atunci cânt utilizatorul vrea să vadă mai multe informații despre o
ecranizare. El conține câteva informații despre film :
 Numele
 Anul lansării
 Genul
 Descrierea
 Nota
 Numărul de vizualizări
și câteva detalii despre ecranizare:
 Cinema-ul la care rulează
 Data și ora
 Formatul proiecției
De aici utilizatorul poate să cumpere sau să rezerve bilete, cea din urmă fiind disponibilă doar
dacă mai sunt cel puțin 30 de minute până la începerea filmului. De asemenea, dacă filmul
urmează să înceapă peste mai mult de 2 ore, atunci opțiunea “Activează memento” este
disponibilă. Dacă este activată, atunci utilizatorul va fi notificat cu o oră, respectiv două o re
înainte ca filmul să înceapă pentru a avea timp să se pregatească. Spre deosebire de funcția
follow din ecranul de Detalii film, funcția “Activează memento” este disponibilă și dacă nu
există conexiune la internet întrucât se folosește de aceleași tipuri de date pe care le folosesc
aplicațiile de alarmă.

44

Fig. 13. Ecranul cu detaliile unei ecranizări

3.1.9 Autentificare/Înregistrare/Profil

Pentru a avea acces la anumite funcționalități ale aplicației, utilizatorii trebuie să se
autentifice. Aceștia își pot face un cont folosind o adresă de email și o parolă sau pot folosi
contul existent de Google sau Facebook pentru a se conecta. În cazul utilizării uneia din cele
2 rețele de socializare, la server este trimis token-ul obținut, pe post de parolă. Email-ul
împreună cu token-ul vor fi salvate și în baza de date locală, cel din urmă fiind criptat cu un
AES. La server parola sau token-ul vor fi trimise criptat, iar server-ul la rândul lui le hash-
uiește înainte de a le salva în baza de date.
În cazul în care utilizatorii și-au uitat parola aceștia pot sa-și scrie email-ul, iar server-ul va
trimite un email cu instrucțiuni pentru resetarea parolei.

45
După crearea contului și autentificare, utilizatorii au acces la ecranul de profil. Aici ei pot
vedea imaginea de profil, numele și adresa de e-mail folosită. De asemenea, accesând ecranul
de Editare profil apăsând pe butonul din dreapta sus, utilizatorii pot să își modifice imaginea
de profil, numele sau să își schimbe parola daca s-au autentificat cu email și parolă.

Fig. 14. Ecranul de autentificare (stânga) și ecranul de înregistrare (dreapta)

46

Fig. 15. Ecranul de profil (stânga) și ecranul de editare profil (dreapta)

3.2 Baza de date locală

Pentru baza de date mobilă am folosit framework-ul Room Library ce acționează ca un
wrappe
r/ “înfășurător” peste SQLite 3. Toate datele disponibile în aplicație sunt salvate în
baza de date prin intermediul lui Room. În felul acesta, dacă un utilizator deschide aplicația
atunci când nu are conexiune la internet, el va avea acces la toate informațiile de care are
nevoie. Singurul ecran care în care nu va fi afișat nimic va fi cel de hartă, întrucât acela
necesită o conexiune la internet pentru a putea obține filmele care rulează într -un anumit
interval de timp.
La deschiderea aplicației, se face un request pentru a descărca ultimele filme apărute precum
și noul program (dacă acesta nu există deja pe telefon). Aceste informații sunt salvate în baza
de date, iar cele care există deja vor fi înlocuite sau ignorate în funcție de caz (ex .: filmele
sunt înlocuite, dar ecranizările sunt ignorate întrucât se dorește păstrarea opțiunii memen to).

47
Întrucât Room nu este un ORM ( Object Relational Mapper ), îi lipsesc câteva funcții care ar fi
crescut productivitatea și ar fi redus numărul de linii de cod în anumite cazuri. Un exemplu ar
fi maparea automată a unui obiect complex declarat ca un câmp într-o clasă entitate. În cazul
nostru, entitatea MovieTime conține un obiect Movie care la rândul lui este o entitate. Un
ORM ar fi detectat asta și atunci când s-ar fi făcut SELECT pe MovieTime , fiecare obiect ar fi
avut și obiectul Movie asociat. În cazul lui Room, compilatorul aruncă o excepție spunând că
nu știe cum sa serializeze acel tip de date. Se pot folosi clase de tip POJO ( Plain Old Java
Object) pentru a realiza acest lucru, dar după aceea clasa MovieTime nu ar mai putea fi
entitate. Totuși se poate trece și peste această problemă declarând o clasă abstracta de tip
POJO cu obiectul Movie în ea, iar clasa de tip entitate MovieTime va extinde clasa abstracta.
Deși un SELECT acum va face ce trebuie, o altă întrebare apare : cum de a funcționat?
Răspunsul este simplu, dar din păcate extrem de urât : Room a creat un tabel cu fiecare câmp
din clasele entitate MovieTime și Movie. Astfel, entitatea MovieTime conține acum și toate
coloanele lui Movie. O problemă gravă din punct de vedere al proiectării baz ei de date. De
aceea această metodă nu este folosită. Varianta cea mai bună este ca clasa MovieTime să
conțină un câmp movieId, iar în momentul în care este nevoie de filmul asociat ecranizării, se
face o interogarea în baza de date pentru obținerea aceste i informații.
Thread {

AppDataba se.getAppDatabase(context).moviesDao().insertAll(data?. data?.movies!!)
AppDatabase.get AppDatabase(context).locationsDao().inse rtAll(data.data?.locations !!)

}.start()

@Dao
interface MoviesDao {

@Insert(onConflict = OnConflictStrategy .REPLACE)
fun insertAll(movies: ArrayList<Movie>)

@Query("SELECT * FROM movie WHERE id = :id" )
fun getMovieById(id: Int): Movi e?

@Query("SELECT * FROM movie WHERE currentAction is not NULL AND currentAction = :action
ORDER BY premiere DESC" )
fun getPagedMovies(action: String): DataSource.Factory<Int, Movie>

}

3.3 Gradle

Gradle este un manager de dependințe ce introduce un DSL ( domain -specific language ) bazat
pe Groovy față de Apache Maven ce folosește o construcție XML pentru declararea
configurațiilor proiectului. Gradle a fost dezvoltat pentru proiecte cu mai multe module care
pot să ajungă destul de mari. El dispune de un mod inteligent de a afișa când există o nouă
versiune pentru o anumită dependință.

48
Dependințele sunt declarate în felul următor :
dependencies {
implementation fileTree(dir: 'libs', include: [ '*.jar'])
implementation "org.jetbrains.kotlin:kotlin -stdlib-jdk7:$kotlin_version "
implementation 'com.android.support:appcompat -v7:27.1.1'
implementation 'com.android.support:design:27.1.1'
implementation 'com.android.support.constraint:constraint -layout:1.1.0'
implementation 'com.google.android.gms:play -services -maps:15.0.0'
implementation 'com.google.android.gms:play -services -location:15.0.0'
implementation 'com.google.android.gms:play -services -auth:15.0.0'
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter -gson:2.3.0'
implementation 'android.arch.persistence.ro om:runtime:1.0.0'
implementation "android.arch.paging:runtime:1.0.0 -rc1"
implementation "android.arch.lifecycle:extensions:1.1.1"
implementation 'com.github.bumptech.glide:glide:4.3.1'
implementation 'com.google.firebase:firebase -auth:15.0. 0'
implementation 'com.google.firebase:firebase -messaging:15.0.2'
implementation 'com.google.firebase:firebase -core:15.0.2'
implementation 'com.facebook.android:facebook -login:4.27.0'
implementation 'com.android.support:support -v4:27.1.1'
implementation 'com.android.support:recyclerview -v7:27.1.1'

implementation 'com.crashlytics.sdk.android:crashlytics:2.9.2'

// implementation 'com.android.support:support -vector-drawable:27.1.1'
kapt 'android.arch.persistence.room:compiler:1 .0.0'
annotationProcessor "android.arch.persistence.room:compiler:1.0.0"
annotationProcessor "android.arch.lifecycle:compiler:1.1.1"
annotationProcessor 'com.github.bumptech.glide:compiler:4.3.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso –
core:3.0.2'
}

3.4 Retrofit

Request-urile la server au fost făcute folosind framework-ul Retrofit . Acesta necesită crearea
unei interfețe în care sunt declarate metode ce vor fi apelate pentru diferite request-uri .
Folosind apoi un builder pus la dispoziție de framework , se creează un obiect de tipul
interfeței declarate (de fapt e o clasă anonima ce implementează interfața), iar prin
intermediul lui sunt apelate metodele dorite. Request-urile pot fi executate pe un alt thread
sau pe thread-ul pe care se află codul la momentul respectiv. În primul caz un callback va fi
necesar pentru a obține răspunsul de la server, iar în cel de-al doilea caz, răspunsul este
returnat prin apelarea metodei execute.

49

interface ApiInterface {

@GET("settings" )
fun getSettings( @Header("Authorization" ) auth: String, @Query("timestamp" ) timestamp:
Long): Call<SettingsResponse>

@POST("rate")
@FormUrlEncoded
fun rateMovie( @Header("Authorization" ) auth: String, @Field("movie_id") id: Int,
@Field("rating" ) rating: String, @Field("language" ) language: String): Call<MovieResponse>

}

val apiService = ApiClient.getClient().create(ApiInterface:: class.java)
val timestamp: Long = Date(). time / 1000

val call: Call< SettingsResponse> by lazy {
if (ActivityController. isUserLogin ) {
Log.e("sdhajkdsha" , ActivityController. user?.authToken !!)
apiService.getSettings(ActivityController. user?.authToken !!, timestamp)
} else
apiService.getSettings( "", timestamp)
}

call.enqueue( object : Callback<SettingsResponse> {

override fun onResponse(call: Call<SettingsResponse>?, response:
Response<SettingsRes ponse>) {

if (response. isSuccessful ) {
val data: SettingsResponse? = response.body()
Log.e("settings" , Gson().toJson(data))


}
}

override fun onFailure(call: Call<SettingsResponse>?, t: Throwable) {
t.printStackTrace()
}
})

50
CONCLUZII

În urma acestei lucrări de licență am urmărit două obiective :
 Să dezvolt o aplicație care să faciliteze cumpărarea și rezervarea de bilete la filme și
îmbunătățirea experienței de a fi la curent cu ultimele filme apărute.
 Învățarea limbajului Kotlin, stăpânirea lui cât mai bine, îmbunătățirea metodelor mele
de programare și folosirea ultimelor tehnologii apărute și recomandate de către
Google.
Ca planuri de viitor pentru această aplicație cred că ar fi destul de multe :
 Integrarea și parteneriatul cu CinemaCity pentru construirea unui sistem de reduceri
ce va crește numărul de clienți ai companiei.
 Integrarea unei platforme de socializare care să le permită utilizatorilor să vadă în
timp real câți oameni merg la un anumit film la o anumită oră în funcție de numărul
de bilete cumpărate și rezervate (ține tot de parteneriat cu CinemaCity ).
 Integrarea unui sistem de recenzii/comentarii asemănător cu cel de pe IMDB pentru a-
i ajuta pe utilizatori să aleagă mai ușor filmele la care doresc să meargă.
 Îmbunătățirea sistemului de recomandare pentru a trimite notificări utilizatorilor dacă
se află lângă un cinema și unul din filmele recomandate urmează sa ruleze.
 Îmbunătățirea sistemului de rating în așa fel încât să trimită notificări de tipul : “Ai
văzut cum va x? Spune-ne ce părere ai ” după ce utilizatorul vede un film la cinema,
fără ca acesta să fi marcat că se duce la film (implică locație și poate puțin AI).
 Adăugarea de conținut suplimentar cum ar fi actori și regizori cu scopul de a căuta
filme cu anu miți actori sau regizate de anumiți directori.
 Adăugarea de conținut live (sau înregistrări) de pe YouTube ce prezintă actori sau
regizori vorbind despre filmul pe care doar ce l-au lansat și punerea lor pe prima
pagină într-o categorie asemănătoare.
 Dacă este posibil, extinderea la nivel global cu CinemaCity sau mai mult, cu mai
multe cinema-uri din alte țări.

51
Bibliografie:

1. https://en.wikipedia.org/wiki/Android_(operating_system) , 27 Mai 2018
2. https://www.statista.com/topics/876/android/ , 27 Mai 2018
3. https://en.wikipedia.org/wiki/Kotlin_(programming_language) , 28 Mai 2018
4. https://kotlinlang.org/docs/reference/null-safety.html , 28 Mai 2018
5. https://www.appbrain.com/stats/libraries/details/kotlin/kotlin , 28 Mai 2018
6. https://www.appbrain.com/stats/number-of-android-apps , 28 Mai 2018
7. https://en.wikipedia.org/wiki/Gunicorn , 28 Mai 2018
8. https://www.fullstackpython.com/green-unicorn-gunicorn.html , 28 Mai 2018
9. https://en.wikipedia.org/wiki/Nginx , 28 Mai 2018
10. https://en.wikipedia.org/wiki/MySQL , 28 Mai 2018
11. http://www.oracle.com/technetwork/database/mysql/index.html , 28 mai 2018
12. https://en.wikipedia.org/wiki/Python_(programming_language) , 28 Mai 2018
13. https://en.wikibooks.org/wiki/Python_Programming , 29 Mai 2018
14. https://www.dataquest.io/blog/introduction-functional-programming-python/ , 29 Mai
2018
15. https://en.wikipedia.org/wiki/Flask_(web_framework) , 30 Mai 2018
16. https://stackshare.io/flask/in-stacks , 30 Mai 2018
17. https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel , 31
Mai 2018
18. https://android.jlelse.eu/why- to-choose-mvvm-over-mvp-android-architecture-
33c0f2de5516 , 31 Mai 2018
19. https://en.wikipedia.org/wiki/Recommender_system , 1 Iunie 2018
20. https://cambridgespark.com/content/tutorials/implementing-your-own-recommender-
systems-in-Python/index.html , 1 Iunie 2018
21. https://www.statista.com/statistics/266136/global-market-share-held- by-smartphone-
operating-systems/ , 2 Iunie 2018
22. https://developer.android.com/about/dashboards/ , 3 Iunie 2018

Similar Posts