Aceasta este a doua parte a seriei despre Audero Audio Player. În acest articol, vom crea logica de afaceri a jucătorului nostru. Voi explica și câteva dintre API-urile de la Cordova care au fost introduse în articolul precedent.
În această secțiune, vă voi arăta clasa numită Jucător
, care ne permite să ne jucăm, să ne oprim, să ne întoarcem și să ne întoarcem rapid. Clasa se bazează în mare măsură pe Media API; fără metodele sale, jucătorul nostru va fi complet inutil. În plus față de Media API, această clasă profită de alerta()
metoda API de notificare. Aspectul alertei variază între platforme. Majoritatea sistemelor de operare acceptate utilizează o casetă de dialog nativă, dar altele, cum ar fi Bada 2.X, folosesc browserul clasic alerta()
funcție, care este mai puțin personalizabil. Metoda anterioară acceptă până la patru parametri:
Rețineți că Windows Phone 7 ignoră numele butonului și utilizează întotdeauna valoarea implicită. Windows Phone 7 și 8 nu au o alertă încorporată în browser, deci dacă doriți să le utilizați alert ( 'mesaj');
, trebuie să alocați window.alert = navigator.notification.alert
.
Acum am explicat API-urile utilizate de către Jucător
, putem să vedem cum se face. Avem trei proprietăți:
mass-media
: referința la obiectul de sunet curentmediaTimer
: care va conține un ID de interval unic creat folosind setInterval ()
funcția pe care o vom trece la clearInterval ()
pentru a opri cronometrul sunetuluise joacă
: o variabilă care specifică dacă sunetul curent se joacă sau nu. Pe lângă proprietate, clasa are mai multe metode. initMedia ()
metoda inițializează mass-media
proprietate cu un Mass-media
obiect care reprezintă sunetul selectat de utilizator. Acesta din urmă este notificat folosind API de notificare în caz de eroare. Obiectivul playPause
, Stop()
, și seekPosition ()
metodele ar trebui să fie evidente, așa că voi trece mai departe. resetLayout ()
și changePlayButton ()
metodele sunt foarte simple. Acestea sunt utilizate pentru resetarea sau actualizarea layout-ului jucătorului în funcție de acțiunea efectuată de utilizator. Ultima metodă rămasă este updateSliderPosition ()
, care este similar cu cursorul de timp. Acesta din urmă are zero (începutul cursorului) ca valoare implicită pentru poziția curentă, setată utilizând value = "0"
atribut. Aceasta trebuie să fie actualizată în mod corespunzător în timp ce sunetul se joacă pentru a da utilizatorului feedback vizual privind timpul de joc scurs.
Am descoperit toate detaliile acestei clase, deci aici este codul sursă al fișierului:
var Player = media: null, mediaTimer: null, isPlaying: false, initMedia: function (calea) Player.media = new Media (path, function () console.log Player.media.release (); Player.resetLayout (); funcția (eroare) navigator.notification.alert ('Nu se poate citi fișierul media.', , 'Error'); Player.changePlayButton ('play'); console.log ('Nu se poate citi fișierul media (Code): + error.code;); , playPause: funcție (cale) if (Player.media === null) Player.initMedia (calea); dacă (Player.isPlaying === false) Player.media.play (); Player.mediaTimer = setInterval (functie () Player.media.getCurrentPosition (functie (pozitie) if (pozitie> -1) $ (' ()), funcția (eroare) console.log ('Nu se poate recupera poziția media:' + error.code); text (Utility.formatTime (0) ));, 1000); var counter = 0; var timerDuration = setInterval (functie () contra ++; daca (contra> 20) clearInterval (timerDuration); var durata = Player.media.getDuration () media-duration '), text (Utility.formatTime (durata)), $ (' # time-slider '). slider ('refresh'); altceva $ ('# media-duration'). text ('Unknown');, 100); Player.changePlayButton ( 'pauză'); altceva Player.media.pause (); clearInterval (Player.mediaTimer); Player.changePlayButton ( 'joc'); Player.isPlaying =! Player.isPlaying; , stop: funcția () if (Player.media! == null) Player.media.stop (); Player.media.release (); clearInterval (Player.mediaTimer); Player.media = null; Player.isPlaying = false; Player.resetLayout (); , resetLayout: funcția () $ ('# media-played'). text (Utility.formatTime (0)); Player.changePlayButton ( 'joc'); Player.updateSliderPosition (0); , updateSliderPosition: funcție (secunde) var $ slider = $ ('# time-slider'); dacă (secunde < $slider.attr('min')) $slider.val($slider.attr('min')); else if (seconds > $ slider.attr ('max')) $ slider.val ($ slider.attr ('max')); altfel $ slider.val (Math.round (secunde)); $ Slider.slider ( 'reîmprospătare'); , seekPosition: funcție (secunde) if (Player.media === null) retur; Player.media.seekTo (secunde * 1000); Player.updateSliderPosition (secunde); , changePlayButton: funcția (imageName) var background = $ ('player-play') .css ('background-image'). $ ('# player-play') css ('background-image', 'url (' + background.replace (/ images \ /.* \ .png $ / ) +)); ;
Această secțiune ilustrează AppFile
clasa care va fi utilizată pentru a crea, șterge și încărca sunetele utilizând API-ul Web Storage. Acest API are două domenii, Sesiune și Local, dar Cordova îl folosește pe acesta din urmă. Toate sunetele sunt stocate într-un element intitulat "fișiere", după cum puteți vedea, uitandu-te la _tableName
proprietăţi.
Rețineți că acest API poate să stocheze numai datele de bază. Prin urmare, pentru a se potrivi nevoii noastre de a stoca obiecte, vom folosi formatul JSON. JavaScript are o clasă pentru a face față acestui format numit JSON. Utilizează metodele analiza()
să analizeze un șir și să recreeze datele corespunzătoare și stringify ()
pentru a converti obiectul într-un șir. Ca o notă finală, nu voi folosi notația punctului API deoarece Windows Phone 7 nu o acceptă, deci vom folosi setItem ()
și getItem ()
metode pentru a asigura compatibilitatea pentru toate dispozitivele.
Acum că aveți o imagine de ansamblu a modului în care vom stoca datele, să vorbim despre datele pe care trebuie să le salvăm. Singurele informații de care avem nevoie pentru fiecare sunet găsit sunt numele (Nume
proprietate) și o cale absolută (fullpath
proprietate). AppFile
clasa are, de asemenea, o "constantă", numită EXTENSII
, unde vom seta extensiile care vor fi testate în funcție de fiecare fișier. Dacă se potrivesc, fișierul va fi colectat de către aplicație. Avem o metodă de adăugare a unui fișier (adauga fisier()
), o metodă de ștergere a unui fișier (sterge fisierul()
), o metodă care șterge întreaga bază de date (sterge fisierele()
) și, în sfârșit, două metode care regăsesc fișierul din baza de date: getAppFiles ()
pentru a prelua toate fișierele și getAppFile ()
pentru a recupera doar unul. Clasa are, de asemenea, patru metode de comparare, două statice (comparaţie()
și compareIgnoreCase ()
) și două non-statice (compara cu()
și compareToIgnoreCase ()
). Ultima metodă este cea utilizată pentru a prelua indexul unui anumit fișier, getIndex ()
. AppFile
vă permite să efectuați toate operațiile de bază de care aveți nevoie.
Codul care implementează ceea ce am discutat poate fi citit aici:
funcția AppFile (nume, cale completă) var _db = window.localStorage; var _tableName = 'fișiere'; this.name = nume; this.fullPath = fullPath; this.save = funcția (fișiere) _db.setItem (_tableName, JSON.stringify (files)); this.load = funcția () returnează JSON.parse (_db.getItem (_tableName)); AppFile.prototype.addFile = funcția () var index = AppFile.getIndex (this.fullPath); fișiere var = AppFile.getAppFiles (); dacă (index === false) files.push (aceasta); alte fișiere [index] = aceasta; this.save (fișiere); ; AppFile.prototype.deleteFile = funcție () var index = AppFile.getIndex (this.fullPath); fișiere var = AppFile.getAppFiles (); dacă (index! == false) files.splice (index, 1); this.save (fișiere); returnează fișierele; ; AppFile.prototype.compareTo = funcția (altul) return AppFile.compare (aceasta, alta); ; AppFile.prototype.compareToIgnoreCase = funcția (altul) return AppFile.compareIgnoreCase (aceasta, alta); ; AppFile.EXTENSIONS = ['.mp3', '.wav', '.m4a']; AppFile.compare = funcție (appFile, altele) if (other == null) return 1; altfel dacă (appFile == null) returnează -1; returnați appFile.name.localeCompare (alt nume.info); ; AppFile.compareIgnoreCase = funcție (appFile, altele) if (other == null) return 1; altfel dacă (appFile == null) returnează -1; returnați appFile.name.toUpperCase (). localeCompare (other.name.toUpperCase ()); ; AppFile.getAppFiles = funcție () var fișiere = nou AppFile () load (); retur (fișiere === null)? []: fișiere; ; AppFile.getAppFile = funcție (cale) var index = AppFile.getIndex (cale); dacă (index === false) reveniți null; altfel var file = AppFile.getAppFiles () [index]; returnați AppFile nou (file.name, file.fullPath); ; AppFile.getIndex = funcția (calea) var files = AppFile.getAppFiles (); pentru (var i = 0; i < files.length; i++) if (files[i].fullPath.toUpperCase() === path.toUpperCase()) return i; return false; ; AppFile.deleteFiles = function() new AppFile().save([]); ;
utility.js
fișierul este foarte scurt și ușor de înțeles. Are doar două metode. Unul este folosit pentru a converti milisecunde într-un șir formatat care va fi afișat în player, în timp ce celălalt este o implementare JavaScript a bine-cunoscutei metode Java se termină cu
.
Iată sursa:
var Utility = formatTime: funcție (milisecunde) if (milisecunde <= 0) return '00:00'; var seconds = Math.round(milliseconds); var minutes = Math.floor(seconds / 60); if (minutes < 10) minutes = '0' + minutes; seconds = seconds % 60; if (seconds < 10) seconds = '0' + seconds; return minutes + ':' + seconds; , endsWith: function(string, suffix) return string.indexOf(suffix, string.length - suffix.length) !== -1; ;
Această secțiune discută ultimul fișier JavaScript al proiectului, application.js
, care conține cerere
clasă. Scopul său este de a atașa evenimentele la elementele paginii aplicației. Aceste evenimente vor profita de clasele pe care le-am văzut până acum și le vom permite jucătorului să funcționeze corect.
Codul funcției ilustrate este prezentat mai jos:
var Application = initApplication: funcția () $ (document) .on ('pageinit', '# files-list-page', funcția () Application.initFilesListPage (); $ (document) .on ('pageinit', '# aurelio-page', funcția () Application.openLinksInApp ();); $ (document) .on ('pagechange', funcție (eveniment, proprietăți) if (properties.absUrl === $ .mobile.path.makeUrlAbsolute ('player.html')) Application.initPlayerPage (JSON.parse properties.options.data.file));); , initFilesListPage: function () $ ('# update-button'). ();, 150);); $ (document) .on ('endupdate', function () Application.createFilesList ('fișiere-list', AppFile.getAppFiles ()); ); Application.createFilesList ("lista fișierelor", AppFile.getAppFiles ()); , initPlayerPage: funcție (fișier) Player.stop (); . $ ( '# Mass-name') text (file.name); . $ ( '# Mass-media-cale'), text (file.fullPath); $ ('# player-play') faceți clic pe (funcția () Player.playPause (file.fullPath);); . $ ( '# Jucător-stop') click (Player.stop); $ ('slidestop', funcție (event)) Player.seekPosition (event.target.value);); ), updateIcons: function () if $ (fereastra) .width ()> 480) $ (' ) .removeAttr ("data-iconpos");); altceva $ ('a (date-icon), buton [data-icon]'); , openLinksInApp: functie () $ ("a [target = \" blank] ") .attr ('href'), '_target');); , updateMediaList: funcția () window.requestFileSystem (LocalFileSystem.PERSISTENT, 0, funcția (fileSystem) var root = fileSystem.root; AppFile.deleteFiles (); Application.collectMedia (root.fullPath, true) (eroare) console.log ('Eroare sistem de fișiere:' + error.code);); , collectMedia: funcție (cale, recursivă, nivel) if (level === undefined) level = 0; var directorEntry = new DirectoryEntry (", calea); if (! directoryEntry.isDirectory) console.log ('Calea furnizată nu este un director') return; var directorReader = directoryEntry.createReader () directoryReader.readEntries funcția (intrări) var appFile; var extensie; pentru (var i = 0; i < entries.length; i++) if (entries[i].name === '.') continue; extension = entries[i].name.substr(entries[i].name.lastIndexOf('.')); if (entries[i].isDirectory === true && recursive === true) Application.collectMedia(entries[i].fullPath, recursive, level + 1); else if (entries[i].isFile === true && $.inArray(extension, AppFile.EXTENSIONS) >= 0) appFile = nou AppFile (intrări [i]. Nume, intrări [i] .fullPath); appFile.addFile (); console.log ('Fișier salvat:' + intrări [i] .fullPath); , funcția (eroare) console.log ('Nu se poate citi directorul. Errore:' + error.code); ); dacă (nivel === 0) $ (document) .trigger ('endupdate'); console.log ('Calea curentă este analizată:' + cale); , createFilesList: funcția (idElement, fișiere) $ ('#' + idElement) .empty (); dacă fișierele == null || files.length == 0) $ ('#' + idElement) .append ('Nu se afișează fișiere. Considerați o actualizare a fișierelor (butonul din dreapta sus)?
„); întoarcere; funcția getPlayHandler (fișier) return function playHandler () $ .mobile.changePage ('player.html', data: fișier: JSON.stringify (fișier)); ; funcția getDeleteHandler (fișier) return funcția deleteHandler () var oldLenght = AppFile.getAppFiles () lungime; var $ parentUl = $ (acest) .closest ('ul'); fișierul = new AppFile (", fișier.fullPath); file.deleteFile (); dacă (oldLenght === AppFile.getAppFiles () lungimea + 1) $ (this) ; $ parentUl.listview ('refresh'); altceva console.log ('Media nu a fost ștearsă, ceva nu a mers bine';) navigator.notification.alert ('Media nu a fost șters. , funcția () , "Eroare");; var $ listElement, $ linkElement; files.sort (AppFile.compareIgnoreCase); pentru (var i = 0; i < files.length; i++) $listElement = $('
În partea anterioară a acestei serii, am menționat că un punct interesant al paginii creditelor a fost atributul target = "_ blank"
aplicate la link-uri. Această secțiune va explica de ce openLinksInApp ()
metodă a cerere
clasa are sens.
Odată, Cordova a folosit pentru a deschide linkuri externe în același Cordova WebView care rula aplicația. Atunci când o legătură a fost deschisă și utilizatorul a dat clic pe butonul "înapoi", ultima pagină afișată a fost afișată exact așa cum a fost înainte de al părăsi. În versiunea mai nouă, acest lucru sa schimbat. În zilele noastre, legăturile externe sunt deschise, în mod implicit, utilizând Cordova WebView dacă adresa URL se află în lista albă a aplicației. Adresele URL care nu se află pe lista albă sunt deschise utilizând API-ul InAppBrowser. Dacă nu gestionați link-urile în mod corect sau dacă utilizatorul pune în legătură un link care este afișat în InAppBrowser sau în sistem și apoi alege să revină, toate accesoriile jQuery Mobile sunt pierdute. Acest comportament se întâmplă deoarece fișierele CSS și JavaScript sunt încărcate de pagina principală, iar cele următoare sunt încărcate utilizând AJAX. Înainte de a descoperi soluția, să aruncăm o privire la ceea ce este InAppBrowser.
InAppBrowser este un browser web care este afișat în aplicația dvs. atunci când utilizați apelul window.open.
Acest API are trei metode:
addEventListener ():
Vă permite să ascultați trei evenimente (loadstart
, loadstop
, și Ieșire)
și atașați o funcție care rulează de îndată ce acele evenimente sunt declanșateremoveEventListener ()
: Îndepărtează un ascultător atașat anterior.închide()
: Utilizat pentru a închide fereastra InAppBrowser.Deci, care este soluția? Obiectivul openLinksInApp ()
, împreună cu lista albă specificată în fișierul de configurare, este de a prinde clicurile pe toate legăturile externe recunoscute prin utilizarea target = "_ blank"
atribut și deschideți-le folosind window.open ()
metodă. Cu această tehnică, vom evita problema descrisă și jucătorul nostru va continua să se uite și să funcționeze așa cum era de așteptat.
În cea de-a treia și ultima tranșă a acestei serii, vom vedea ultimele fișiere rămase, astfel încât să puteți finaliza proiectul și să vă jucați cu el.