Fără doar şi poate revista Today Software Magazine reprezintă unul dintre elementele ce se afirmă tot mai pregnant în comunitatea IT din Cluj Napoca şi nu numai, transformându-se într-o portavoce prin care profesionişti în diverse domenii din industria sofware îşi etalează expertiza profesională, partajează bune practici din domeniu şi caută împreună soluţii la probleme specifice industriei.
În urmă cu mai bine de patru luni Today Software Magazine a realizat un parteneriat cu 3Pillar Global pentru a implementa o soluție ce facilitează utilizatorilor iPhone/iPad accesul la articolele Today Software Magazine , dar și socializarea în jurul acestora prin mijlocirea partajării articolelor, amplificând puterea creativă a tuturor pentru beneficiul comun. Iată că această colaborate continuă cu o soluţie similară dar, de data aceasta, pe o platformă Android
https://play.google.com/store/apps/details?id=com.tpg.tsm.ui&hl=ro
Există multe asemănări şi diferenţe între cele două soluţii. Una dintre diferenţe este aceea că clientul realizat pe platformele iPhone/iPad a pornit de la zero în timp ce versiunea Android a avut un model funcţional la dispoziţie (lucru care nu a fost întotdeauna un avantaj). De asemenea, multitudinea de rezoluţii existente pentru terminalele ce folosesc Android a condus la modificări sau uneori la o regândire completă a interfeţei cu utilizatorul. Nu în ultimul rând, un interesant aspect este poate acela că întreaga echipă de dezvoltare a soluţiei, formată din patru persoane (un Technical Manager, doiprogramatori şi un tester), este parte a 3Pillar Global India care în șase săptămâni a acoperit tot efortul de implementare necesar demonstrând (dacă mai era nevoie) că diferenţa de fus orar nu mai reprezintă un factor perturbator în industria IT.
Echipa de dezvoltare a urmat principiile enunţate în Adaptive Product Lifecycle Management (un model de dezvoltare utilizat intern în 3Pillar Global şi care a fost definit în jurul metodologiei Agile Scrum), dezvoltarea fiind segmentată pe trei sprinturi (fiecare având câte o temă specifică) a câte două săptămâni.
Astfel, la finele fiecărui sprint a fost prezentat căte un demo util în ajustarea corespunzătoare a soluţiei pentru a acoperi cât mai bine nevoile de a accesa eficient conţinutul seriei Today Software Magazine.
Pentru urmărirea progresului proiectului s-a utilizat Jira, toate task-urile fiind create ca user story-uri şi fiind asignate membrilor echipei de dezvoltare. Ca sistem de control al fişierelor s-a utilizat GIT, fiind creat un branch de dezvoltare la începutul primului sprint. Pentru eliminarea conflictelor, acest branch era inclus în branch-ul master la finalul fiecărui sprint (evident după revizuirea colectivă a codului).
Discuţiile iniţiale s-au concentrat în principal pe respectarea design-ului şi a funcţionalităţilor implementate în versiunile precedente de iPhone/iPad ale soluţiei. Au existat, desigur, o serie de adaptări la specificul înterfeţei utilizator Android dar, în acelaşi timp, au fost propuse mai multe modificări ce au fost ulterior acceptate. Printre cele mai importante modificări implementate în raport cu versiunea iOS a clientului Today software Magazine amintim:
Imediat după lansarea în execuţie, aplicaţia afişează un splash screen ce permite selectarea limbii de utilizare: română sau engleză. După alegerea limbii aplicaţia afişează coperta ultimului număr al revistei şi lista articolelor acestui număr (preluate de pe web serverul TSM). În background se încarcă datele corespunzătoare articolelor într-o bază de date SQLite şi în memoria internă (dacă este cazul).
În partea stângă a ecranului, selectând meniul din action bar se poate consulta lista tuturor numerelor revistei (fiecare fiind reprezentată de coperta sa), iar în panoul din dreapta este afişată mereu lista articolelor numărului curent selectat.
Selectarea oricărui articol din listă va avea ca efect afişarea conţinutului acestuia precum şi detalii despre autor. Prin selectarea butonului Share, se poate opta prin intermediul unui meniu pop-up pentru partajarea articolului pe platforme ca Facebook, Twitter sau e-mail.
De asemenea, limba selectată pentru afişare poate fi modificată prin selecţia meniului Settings din action bar. La selectarea unei noi limbi aplicaţia va şterge toate datele stocate şi va reîncepe încărcarea conţinutului de pe serverul TSM folosind noua limbă selectată.
Pentru implementarea soluţiei au fost utilizate următoarele medii, instrumente şi biblioteci:
Următoarele secţiuni prezintă câteva dintre provocările rezolvate cu succes de echipa de dezvoltare. În fiecare caz este descrisă problema şi modul în care a fost rezovată.
În ecranul principal al aplicaţiei este prezentată lista articolelor numărului selectat împreună cu pozele autorilor. Dimensiunea imaginilor originale aflate pe server trebuia redusă pentru a evita erori de tipul OutOfMemory ce conduceau la întreruperea aplicaţiei pentru anumite scenarii particulare.
Pentru a soluţiona eficient problema s-a utilizat metoda bitmap sampling care reprezintă un procedeu prin care se reduce dimensiunea unei imagini. Provocarea în acest caz a constat în menţinerea raportului de aspect al imaginii originale, nealterându-se calitatea imaginii redimensionate. În continuare, este prezentat un fragment de cod care calculeze dimensiunea imaginii reduse:
public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
if(height<0 || width < 0)
return -1;
int inSampleSize = 1;
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
return inSampleSize;
}
public Bitmap sampleBitmap(BitmapFactory.Options options,int reqWidth, int reqHeight, TSMBaseActivity activity, String id, InputStream imageResponse, String imagePath) throws IOException {
int sampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
if (sampleSize >= 0) {
options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize;
if(haveNetworkConnection(activity.getBaseContext())){
try{
imageResponse=new HttpRequest(activity.getBaseContext()).getHttpInputStream(imagePath); //FileNotFoundException, MalformedUrlException
}catch(IOException e){
e.printStackTrace();
if(e instanceof SocketException || e instanceof SocketTimeoutException){
}
return null;
}
if(imageResponse!=null){
Bitmap bmp = BitmapFactory.decodeStream(imageResponse, null, options);
if(bmp==null)
return null;
saveToInternalSorage(bmp, activity,id);
return bmp;
}
}
}
return null;
}
O serie de articole de pe serverul TSM conţin filme Youtube inserate. Redarea acestor filme în componenta de vizualizare a paginii web reprezintă o provocare deoarece nu avem control asupra acesteia. De asemenea, implementarea variază în funcţie de dispozitiv (Gingerbread şi ICS). De exemplu, pe dispozitivele Gingerbread filmele Youtube se redau automat în varianta full screen, în timp ce pe dispozitive ICS sau mai vechi utilizatorii trebuie să selecteze manual opţiunea de full screen.
S-au implementat abordări distincte pentru dispozitivele Gingerbread şi ICS. Pentru Gingerbread s-a utilizat controlul Android implicit Videoview în timp ce pentru dispozitive ICS (sau mai vechi) s-a utilizat Java Reflection API pentru redarea similară a filmelor. Utilizând Reflection API se obţine referinţa la HTML5VideoFullScreen$VideoSurfaceView din webkit şi este redat filmul în full screen. Pentru ambele versiuni sunt redate mai jos exemple de cod:
VideoView customVideoView = (VideoView) customViewContainer.getFocusedChild();
Field mUriField = VideoView.class.getDeclaredField("mUri");
mUriField.setAccessible(true);
Uri uri = (Uri) mUriField.get(customVideoView);
Class _VideoSurfaceView_Class_ = Class.forName("android.webkit.HTML5VideoFullScreen$VideoSurfaceView");
java.lang.reflect.Field _HTML5VideoFullScreen_Field_ = _VideoSurfaceView_Class_.getDeclaredField("this$0");
_HTML5VideoFullScreen_Field_.setAccessible(true);
Object _HTML5VideoFullScreen_Instance_ = _HTML5VideoFullScreen_Field_.get(((FrameLayout) view).getFocusedChild ());
Într-o primă fază transferul asincron al datelor TSM şi prezentarea acestora în interfaţa utilizator dura aproximativ 20-25 minute. Pentru a reduce din timpul de transfer au fost create mai multe procese care transferau datele în paralel de pe server, crescând astfel viteza de download. Provocarea în acest context a constat din sincronizarea datelor între procese şi evitarea oricărui conflict (de exemplu, pozele autorilor nu puteau fi transferate până când nu se transferau informaţiile despre autori, deoarece calea imaginii de transferat era conţinută în aceste informaţii). Prin urmare, provocarea a constat în optimizarea timpului de transfer al datele şi în îmbunătăţirea performanţei generale a aplicaţiei.
Ca soluţie s-a implementat ThreadPoolExecutor şi toate task-urile au fost ataşate executorului. Iniţial s-au creeat 2 thread-uri care se executau în paralel. După ce execuţia acestora se finaliza, următoarele thread-uri erau extrase din coadă pentru transferul datelor în funcţie de acţiunile de navigare ale utilizatorului.
ThreadPoolExecutor executorPool = new ThreadPoolExecutor(2, 2,60, TimeUnit.SECONDS, new ArrayBlockingQueue(2), threadFactory);
executorPool.execute(new LoadIssueImages(handler, language,getBaseContext()));
executorPool.execute(new LoadIssueArticles(handler, language,getBaseContext()));
executorPool.execute(new LoadAuthorInformation(handler,language, getBaseContext()));
executorPool.execute(new LoadAuthorImages(handler, language,getBaseContext(), this));
Aplicaţia a fost testată pe următoarele dispozitive: Nexus 4 (Android 4.3) , Nexus 7(Android 4.1, 7"" Tablet) , HTC Desire (Android 2.3.3 , 480x800) şi Samsung Tab(7",10") , Galaxy Y(240x320).
Selecţia dispozitivelor s-a făcut pentru a acoperi un număr cât mai mare de potenţiali utilizatori de telefoane/tablete Android. Aplicaţia a fost testată în următoarele contexte: