Primeri koda:
File:PythonOnSymbianBookExampleCode CallNotes.zip
File:PythonOnSymbianBookExampleCode SmsAutoReply.zip
Ukoliko razmislimo o akcijama koje poduzmemo da načinimo ili primimo telefonski poziv, ili da pošaljemo ili primimo tekstualnu poruku, shvatamo da su one vrlo jednostavne.
Funkcionalnosti ugrađene u Python module koji podržavaju telefoniju i poruke to reflektuju.
Funkcionalnost je implementirana pomoću callbacks-a, funkcija koje se pozivaju od strane operativnog sistema kada telefon prima telefonski poziv ili poruku.
U ovom poglavlju se razmatra podrška i za telefoniju i poruke. Promatramo podršku Pythona i demonstriramo je sa kompletnim primerima. Poglavlje završavamo sa pregledom kako Python dozvoljava pristup evidenciji (logs) koju telefon čuva o pozivima i porukama koji su načinjeni, poslati ili primljeni.
Operacije telefona, kako ih ljudi čine, su vrlo jednostavne. Možemo pozvati broj telefona. Možemo se javiti dolaznom pozivu. Možemo prekinuti poziv. Python-ova podrška telefoniji prikazuje ovu jednostavnost. Python dodaje mogućnost praćenja telefona u vezi poziva, slično akciji koju mi činimo kada isčekujemo važan poziv.
Python-ova funkcionalnost telefonije je ugrađena u telephone modul. Ovaj modul se mora uvesti (import-ovati) da bi pristup ovoj funkcionalnosti bio moguć.
Da bismo uputili telefonski poziv koristeći dial() funkciju, koristimo pozivajući sled (sekvencu):
telephone.dial(<number>) |
Parametar <number> je string koji sadrži broj telefona koji pozivamo. Biće pozvan onakav kakav je zadat, stoga svi delovi broja moraju biti uključeni (pozivni broj za državu, grad itd.). Na primer, ovo će pozvati broj u SAD-u:
telephone.dial(u"+15075551122") |
<number> mora biti kodiran u Unicode-u.
Ukoliko se telefonski poziv odvija kada se načini poziv funkcije dial(), postojeći telefonski poziv se stavlja automatski na čekanje i novi telefonski poziv postaje aktivan.
Za prekid (okončanje) telefonskog poziva koristimo hang_up() metod bez parametra. Pozivanje ovog metoda prekida se trenutni poziv ukoliko je taj poziv iniciran pozivom metode dial().
Evo jednog primera. Počnimo sa lokalnim pozivom:
import telephone |
Ovo govori uređaju da načini telefonski poziv, što je prikazano na sledećoj slici (Slika 21).
Slika 21 - Symbian uređaj poziva broj koristeći dial() funkciju
U ovom stadijumu, sledeći kod prekida telefonski poziv:
telephone.hang_up() |
Evo nekoliko važnih napomena o Python-ovom shvatanju i upotrebi telefonski poziva:
Traceback (most recent call last): |
>>> telephone.dial("5551234") |
Druga jednostavna operacija koju ljudi rade jeste javljanje na telefon. Modul telephone ovo olakšava pozivom metode answer(). Kao što je javljanje na telefon kada nema telefonkog poziva pomalo suludo, jednostavno pozvati answer() je takođe besmisleno ukoliko zaista ne primamo poziv. Da bismo se propisno javili na poziv, telefon mora biti pripremljen ili konfigurisan da to uradi, a onda mora biti sposoban da prepozna kada stiže poziv.
Da bismo pripremili telefon, telephone modul obezbeđuje incoming_call() metod. Pozivanje ove funkcije 'namešta' telefon za prihvatanje poziva. Poziv ovoj metodi se vraća trenutno, ali Python runtime sistem sada osluškuje u potrazi za pristižućim pozivom. Kada poziv stigne, na njega se može javiti pozivanjem answer() metoda. Postoji nekoliko načina da nateramo našu aplikaciju da pozove answer() metod: na primer kao odgovor na korisnikov pritisak na taster unutar interfejsa korisničke aplikacije; ili kao reakciju na promenu stanja telefona (o tome će biti reči kasnije).
Javljanje na telefonski poziv zahteva samo spajanje dve strane poziva. Nakon toga nema više interakcija između aplikacija.
Modul telephone obezbeđuje način za praćenje aktivnosti telefona. Modul identifikuje 12 stanja u kojima telefonska linija može biti, kao što je prikazano u sledećoj tabeli (Tabela 9 ):
Tabela 9 – Stanja telefonske linije
Python ime |
Vrednost stanja |
Opis |
EStatusUnknown |
0 |
Stanje pozivanja je nepoznato |
EStatusIdle |
1 |
Nema aktivnih poziva |
EStatusDialling |
2 |
Na telefonskoj liniji se događa pozivanje |
EStatusRinging |
3 |
Telefon zvoni |
EStatusAnswering |
4 |
Na telefon se javlja |
EStatusConnecting |
5 |
Telefonski poziv se spaja na remote phone |
EStatusConnected |
6 |
Telefonski poziv je spojen na remote phone |
EStatusReconnectPending |
7 |
Nešto je izazvalo gubitak kanala i poziv se ponovo uspostavlja |
EStatusDisconnecting |
8 |
Telefonski poziv se prekida |
EStatusHold |
9 |
Trenutni poziv je na čekanju |
EStatusTransferring |
10 |
Trenutni poziv se prebacuje |
EStatusTransferAlerting |
11 |
remote phone se upozorava da se poziv prebacuje |
Modul telephone omogućuje da stanje bude praćeno preko callback function-je. Aplikacija mora specifikovati (odrediti) i registrovati funkciju koju operativni sistem treba da pozove svaki put kada telefon promeni stanje između gore navedenih stanja.
Pogledajmo primer. Funkcija ispisuje novo stanje kad god se stanje promeni.
import telephone |
Na nekoliko stvari treba obratiti pažnju u ovom primeru.
Izlaz gore navedenog koda je prikazan ispod za poziv koji je upućen telefonu, prihvaćen od strane korisnika telefona i prekinut od strane pozivatelja:
The phone has changed states. |
Pogledajmo primer telefonskog interfejsa.
Recimo da osoba koja se bavi prodajom čuva beleške o svojim klijentima u bazi podataka kontakata na svom telefonu. Ta osoba smatra da bi bilo korisno da se te informacije automatski izvuku kada je klijent pozove na telefon (Slika 22).
Slika 22 – Screenshot primera prodavca sa informacijama o klijentu
Ova aplikacija se izvršava u pozadini i izbaci informacioni prozor kada nađe informacije. Aplikaciju možemo staviti u pozadinu tako što je pokrenemo, pa onda pritisnemo taster aplikacije da prikažemo ikonu aplikacije. Aplikacija nastavlja da se izvršava u Python runtime enviroment dopuštajući nam interakciju sa drugim aplikacijama.
Ovaj primer možemo prilično lako implementirati. Zapravo, naredni kod ima više pretrage kontakata nego obrade poziva.
import telephone, contacts |
Pogledajmo ovaj kod u delovima. Import izjava otkriva šta nam je potrebno da bi ova zamisao funkcionisala:
import telephone, contacts |
Glavni deo koda je implementiran u funkciji displayNotes(), koja je callback za sistem kada se stanje promeni. Kao što smo primetili u prethodnom poglavlju, ova funkcija je definisana sa parametrom, nazvanim infoTuple ovde, koji ima informaciju o pozivu. Prvo što moramo da uradimo jeste da izvučemo stanje telefona:
# Pribavi stanje telefona |
Želimo raditi nešto samo kada telefon zvoni, pa je stoga ovo jedino stanje na koje funkcija reaguje:
# Reaguj samo kada telefon zvoni |
Kod nastavlja sa otvaranjem baze podataka kontakata i traži kontakt koristeći broj telefona u infoTuple promenljivoj:
# Počni otvaranjem baze podataka kontakata i traženjem broja telefona |
Ostatak displayNotes() funkcije pribavlja beleške o kontaktu, potom gradi TopWindow prozor da to prikaže. Ovaj kod možete sami ispitati.
Kod konačno registruje displayNotes() funkciju kao callback za telefonske pozive:
# Konačno, instaliraj funkciju kao callback telefona |
Kao i operacije sa telefonijom, operacije rukovanja sa SMS i MMS porukama su jednostavne. Možemo slati poruke; možemo ih primati; i možemo ih skladištiti. Svaka od ovih operacije se rukuje kroz Python modul.
Modul messaging rukuje slanjem SMS poruka. Metod koji se koristi za slanje SMS poruke u messaging modulu je sms_send() metod, čiji je format prikazan ispod:
sms_send(<number>, <message>,[<encoding>, <callback>, <name>]) |
Parametri su sledeći:
Poruka može biti u jednom od devet stanja, od 'kreirana' do 'poslata'. Modul messaging ima konstante koje predstavljaju svako stanje, kao što je prikazano u sledećoj tabeli (Tabela 10 ):
Tabela 10 – Konstante koje predstavljaju moguća stanja poruke
Python ime |
Vrednost stanja |
Opis |
ECreated |
0 |
Poruka je kreirana |
EMovedToOutBox |
1 |
Poruka je premeštena u Outbox |
EScheduledForSend |
2 |
Poruka čeka da je telefon pošalje |
ESent |
3 |
Poruka je poslata |
EDeleted |
4 |
Poruka je izbrisana iz Outbox-a |
EScheduleFailed |
5 |
Poruka ne može biti 'zakazana' za slanje |
ESendFailed |
6 |
Poruka ne može biti poslata |
ENoServiceCentre |
7 |
Stanje za poruku u emulatoru telefona, pokazuje da servis nije moguć |
EFatalServerError |
8 |
Poruka je naišla na grešku od strane telefonovog servisa grešaka poruka |
Veze između ovih stanja se prikazane na sledećem grafikonu (Grafik 4):
Grafik 4 – Veze između različitih stanja poruke
Linije grafikona prikazuju putanje promena stanja za poruke. Na primer, poruka bi mogla biti kreirana, premeštena u Outbox, 'zakazano' joj je slanje, onda je poslata. Poruka bi takođe mogla biti kreirana, premeštena u Outbox, 'zakazano' joj je slanje i onda je izbrisana.
Pogledajmo ovaj primer slanja poruke. Kao i sa primerom telefoniranja, naš callback jednostavno ispiše string koji prikazuje stanja naše poruke kako se menjaju od kreiranja do slanja.
import messaging |
Koristeći ovaj kod sa pravim brojem telefona (pravi primatelj) dobili bismo sledeći izlaz:
The message has changed states. |
Treba primetiti nekoliko stvari o proceduri slanja i sms_send() metodi:
MMS poruke su slične SMS porukama. Sadrže kratko tekstualno telo, ali takođe mogu sadržati priloge. U pitanju su medijski prilozi poput slika, videa, zvučnih fajlova i sl.
Slanje MMS poruke sa Python-om nije ni približno detaljno ili informativno kao slanje SMS poruka. Modul messaging podržava MMS poruke kroz jedan medot, mms_send(), koji ima sledeći format:
mms_send(<number>, <message>, [<attachment>]) |
Parametri su vrlo slični onima za sms_send(). <number> je telefonski broj primaoca. <message> je string čija je vrednost poruka za slanje. Opcioni <attachment> je putanja u telefonskom fajl sistemu za fajl koji treba biti priložen poruci.
Na primer, želimo poslati prijatelju svoju sliku. Fotografisali bismo se našim telefonom, a onda to poslali koristeći sledeći poziv:
messaging.mms_send("15055554321", "Here is picture of me. Aren't I beautiful?", "E:\Images\12262008038.jpg") |
Metod mms_send() dozvoljava samo jedan prilog.
Modul inbox podržava rukovanje mailbox-om (skladištem poruka) u Symbian-u. U konfuznoj igri imena, inbox modul definiše Inbox klasu koja predstavlja direktorijum u Symbian-ovom skladištu poruka. Zbunjujuće je zato što Inbox objekat (instanca Inbox klase) može ispitati sadržaj Outbox-a, foldera Sent i Drafts, kao i sadržaj Inbox-a.
Objekti kreirani iz Inbox klase mogu ispitati sva četiri foldera skladišta poruka (Tabela 11 ):
Tabela 11 – Direktorijumi skladišta poruka
Python ime |
Opis |
EInbox |
Inbox folder na Symbian OS-u |
EOutbox |
Outbox folder na Symbian OS-u |
ESent |
Folder poslatih poruka |
EDraft |
Folder poruka u nacrtima |
Inbox objekat se kreira ovako:
inbox = Inbox([<foldertype>]) |
Ovde je <foldertype> jedno od imena iz gornje tabele. Ukoliko nije određen, koristi se Einbox.
Nakon što kreiramo Inbox objekat, možemo dobiti listu SMS poruka korištenjem sms_messages() metoda. Ovo vraća listu ID-ja poruka u obliku integer-a. Ovaj kod ispisuje broj SMS poruka u našem Inbox-u:
import inbox |
Većina preostalih metoda u inbox modulu rade na pojedinačnim porukama, koje se odabiru korištenjem <messageID>.
U narednom primeru biramo tekstualnu poruku za ispitivanje tako što uzimamo prvi ID poruke u listi poruka.
import inbox |
Ispitajmo sada atribute poruke. Primljena je preko Twitter-a i postavio ju je ljubitelj Nokia-e.
>>> box.content(id) |
*Napomene: Imam telefonski broj Twitter servisa unešen u moje kontakte pod “Twitter”, pa sistem prijavljuje izvor poruke kao „Twitter“, a ne broj telefona. Takođe, iako smo ispisali sadržaj poruke, poruka ostaje nepročitana. Nepročitani status poruke je svojstvo korisničkog interfejsa.
Klasa Inbox koristi callback funkciju za obradu primljenih poruka. Callback funkcija mora biti registrovana pomoću bind() funkcije, kao što je prikazano ispod.
inbox.bind(<function name>) |
Kada poruka stigne, sistem poziva callback funkciju sa ID-jem poruke kao parametrom. Pogledajmo sledeći primer:
import inbox |
Ovaj kod izgleda kao da bi trebao ispisati ID poruke i vreme kada je stigla, lepo formatirano. Međutim, pokreće izuzetak kao što je prikazano ispod:
Traceback (most recent call last): |
Da bismo shvatili gde je problem u ovom kodu, prvo moramo razumeti šta se događa u telefonu. Ugrađena Messaging aplikacija telefona, ona koja prikazuje poruke, se izvršava i takođe je registrovala callback sa operativnim sistemom. Kada izvršimo naš Python kod postoje dva programa koji se nadmeću za isti resurs (skladiše poruka). Messaging aplikacija je dizajnirana da se izvršava brzo i skoro će uvek pristupiti skladištu poruka pre našeg Python koda. Naš kod ne može pristupiti skladištu poruka u isto vreme i stoga nastaje izuzetak.
Da bismo rešili stanje trke, možemo dodati poziv time.sleep(). Ovo pauzira kod dovoljno dugo da messaging aplikacija oslobodi pristup skladištu poruka. Naš primer sada izgleda ovako:
import inbox |
Rezultat ovog koda izgleda ovako:
1051799 |
Navedeni kod radi u veći slučajeva, ali nije garantovano da radi uvek. Najbolje rešenje je pokušati pristupiti skladištu poruka određeni broj puta i uhvatiti izuzetak kao način za brojanje koliko puta pristup nije uspeo. Pogledajmo sledeći kod:
import inbox |
Pogledajmo veći primer. Evo aplikacije koja automatski odgovara dolaznoj SMS poruci sa odgovarajućom porukom, koja je formatirana sa imenom pošiljaoca koja naznačuje da ćemo poslati odgovor. Ovo radimo samo ako možemo pronaći ime pošiljaoca; ne želimo da naša poruka odgovora izgleda neuredno, a biće tako ako upotrebimo telefonski broj kao ime.
Pogledajmo navedeni kod kao verziju 1.:
import messaging, inbox, time |
Ovaj kod koristi funkciju isdigit() da odredi da li je bilo koji od karaktera od sender cifra. Istina, ovo je vrlo „krut“ način da utvrdimo da li je sender (telefonski) broj.
Kada pokrenemo ovaj kod i onda primimo tekstualnu poruku, izlaz izgleda ovako:
Sending "Joe Smith, I am not able to read your message at this time. I will respond soon." to Joe Smith |
Međutim, poruka nije poslata. Zapravo, kada pogledamo u Outbox, vidimo (Slika 23):
Slika 23 – Poruka u Outbox-u
I kada premestimo poruku u Drafts folder i pokušamo da je uredimo, dobijamo sledeću grešku (Slika 24):
Slika 24 – Greška pri premeštanju poruke
Dobijamo ovu grešku jer je:
Da bismo ispravili ovo, moramo pribaviti telefonski broj kontakta iz baze podataka kontakata. Pogledajmo sledeći kod:
import messaging, inbox, time, contacts |
Pogledajmo autoreply() funkciju. Prvi deo funkcije je isti: „spavamo“ 5 sekundi i onda izvlačimo pošiljaoca i tekst iz poruke. Ukoliko nema cifara u string-u pošiljaoca, počinjemo da obrađujemo poruku.
Ovaj deo koda otvara bazu podataka kontakata, pronalazi ime i prezime i pronalazi sve kontakte u bazi podataka sa imenom koje smo izvukli:
db = contacts.open() |
Zagarantovano je da će kontakt biti u bazi podataka jer je telefon već vratio ime ovoj funkciji umesto telefonskog broja. Već je pregledao bazu podataka kontakata i pronašao odgovarajući kontakt.
Međutim, moglo bi biti više kontakata sa istim imenom. Stoga ih moramo sve pregledati u potrazi za odgovarajućim prezimenom:
for contact in contactList: |
Ostaje još samo da se izvuče telefonski broj i da se pošalje poruka:
phoneNumber = contact.find('phone_number')[0].value |
Ovaj kod ispravno šalje poruku.
Svi S60 uređaju čuvaju zapise telefonskih poziva i aktivnosti sa porukama. Ovi zapisi su dostupni preko logs Python modula.
Stavke zapisa se gledaju kao dictionary-ji u Python-u. Na primer, zapis telefonskog poziva (kako izgleda u Python-u) je prikazan ispod. Zbog velike količine podataka koja se prikupi po zapisu, ovaj tip prezentacije može biti zbunjujuć, pa su podaci razdvojeni u key/value parove, po jedan u svakom redu. Primetimo da su vrednosti string predstavljene sa Unicode kodiranjem.
{ |
Evo objekta dictionary koji prikazuje zapis tekstualne poruke. Svaki key je opisan u tabeli ispod (Tabela 12 ).
{ |
Tabela 12 – Opis key-ova
Python ime |
Opis |
status |
Kratak opis događaja |
direction |
Smer: ili „Incoming“ – dolazni ili „Outgoing“ – odlazni |
description |
Duži opis događaja |
duration type |
Opis tipa događaja, poput događaja podataka ili događaja telefona |
number |
Broj telefona uređaja koji je povezan sa događajem |
name |
Ime koje je povezano sa događajem |
contact |
ID broj kontakta povezanim sa događajem |
flags |
Indikacija šta je izazvalo događaj: npr. korisnik je pogledao da li ima propušteni poziv |
time |
Vreme, u standardnoj notaciji, kada se događaj dogodio |
duration |
Broj sekundi koliko je događaj trajao |
link |
Validan ukoliko je događaj bio povezan sa drugom aplikacijom |
data |
Podaci povezani sa događajem (npr. tekst poruke) |
id |
ID događaja u bazi podataka zapisa (log-ova) |
subject |
Tema događaja, npr. tekst poruke |
Zapisi se čuvaju za nekoliko različitih tipova komunikacijskih događaja. Ovi tipovi su: call, sms, data, fax, email i scheduler. Ovi tipovi su jasni i ne zahtevaju posebna objašnjenja.
Metode u logs modulu su poprilični direktne. Možemo pribaviti sve podatke zapisa, podatke zapise određenog tipa ili podatke zapisa određenog tipa u određenom vremenskom okviru. Kada pribavljamo zapise, možemo filtrirati zapise pomoću mode-a, opisnika svake poruke. Mode specifikatori mogu biti in, out, fetched, missed, in_alt i out_alt. Ukoliko se ne naznači, mode zauzima default vrednost in.
Metode logs modula su sledeće. Podaci zapisa se vraćaju kao lista dictionary objekata.
Ovi pozivi pribavljaju određene tipove zapisa – koji su jasni sami po sebi – sa jednakim opcionim parametrima. Možemo odrediti početnu tačku pribavljanja, broj (količinski) zapisa za pribavljanje i mode zapisa koji treba pribaviti.
Evo nekoliko primera. Ako želimo da izbrojimo pozive koje smo propustili u poslednja 24 sata, možemo izvršiti sledeći kod:
import logs |
Ukoliko želimo prikazati pošiljaoce zadnjih 10 primljenih poruka, izvršavamo ovaj kod:
import logs |
Važno je napomenuti da ovaj kod ispisuje najnovijih 10 primljenih poruka, a ne prvih 10 primljenih poruka.