Package phones ::
Module com_lgvx4400
|
|
1
2
3
4
5
6
7
8
9
10 """Communicate with the LG VX4400 cell phone"""
11
12
13 import datetime
14 import re
15 import time
16 import cStringIO
17 import sha
18
19
20 import bpcalendar
21 import common
22 import commport
23 import copy
24 import p_lgvx4400
25 import p_brew
26 import com_brew
27 import com_phone
28 import com_lg
29 import helpids
30 import prototypes
31 import fileinfo
32 import call_history
33 import sms
34 import memo
35
36 -class Phone(com_phone.Phone,com_brew.BrewProtocol,com_lg.LGPhonebook,com_lg.LGIndexedMedia):
37 "Talk to the LG VX4400 cell phone"
38
39 desc="LG-VX4400"
40 helpid=helpids.ID_PHONE_LGVX4400
41 wallpaperindexfilename="dloadindex/brewImageIndex.map"
42 ringerindexfilename="dloadindex/brewRingerIndex.map"
43 protocolclass=p_lgvx4400
44 serialsname='lgvx4400'
45
46 imagelocations=(
47
48 ( 10, "dloadindex/brewImageIndex.map", "brew/shared", "images", 30),
49 )
50
51 ringtonelocations=(
52
53 ( 50, "dloadindex/brewRingerIndex.map", "user/sound/ringer", "ringers", 30),
54 )
55
56 builtinimages=('Balloons', 'Soccer', 'Basketball', 'Bird',
57 'Sunflower', 'Puppy', 'Mountain House', 'Beach')
58
59 builtinringtones=( 'Ring 1', 'Ring 2', 'Ring 3', 'Ring 4', 'Ring 5',
60 'Ring 6', 'Voices of Spring', 'Twinkle Twinkle',
61 'The Toreadors', 'Badinerie', 'The Spring', 'Liberty Bell',
62 'Trumpet Concerto', 'Eine Kleine', 'Silken Ladder', 'Nocturne',
63 'Csikos Post', 'Turkish March', 'Mozart Aria', 'La Traviata',
64 'Rag Time', 'Radetzky March', 'Can-Can', 'Sabre Dance', 'Magic Flute',
65 'Carmen' )
66
67
68 - def __init__(self, logtarget, commport):
78
80 """Gets information fundamental to interopating with the phone and UI.
81
82 Currently this is:
83
84 - 'uniqueserial' a unique serial number representing the phone
85 - 'groups' the phonebook groups
86 - 'wallpaper-index' map index numbers to names
87 - 'ringtone-index' map index numbers to ringtone names
88
89 This method is called before we read the phonebook data or before we
90 write phonebook data.
91 """
92
93
94 self.log("Retrieving fundamental phone information")
95 self.log("Phone serial number")
96 results['uniqueserial']=sha.new(self.get_esn()).hexdigest()
97
98 self.getgroups(results)
99 self.getwallpaperindices(results)
100 self.getringtoneindices(results)
101
102 self.log("Fundamentals retrieved")
103
104 return results
105
107 self._setquicktext(result)
108 result['rebootphone']=True
109 return result
110
111 - def _setquicktext(self, result):
112 sf=self.protocolclass.sms_quick_text()
113 quicktext=result.get('canned_msg', [])
114 count=0
115 for entry in quicktext:
116 if count < self.protocolclass.SMS_CANNED_MAX_ITEMS:
117 sf.msgs.append(entry['text'][:self.protocolclass.SMS_CANNED_MAX_LENGTH-1])
118 count+=1
119 else:
120 break
121 if count!=0:
122
123 buf=prototypes.buffer()
124 sf.writetobuffer(buf, logtitle="Writing calendar")
125 self.writefile(self.protocolclass.SMS_CANNED_FILENAME, buf.getvalue())
126 return
127
133
166
167 - def _getquicktext(self):
168 quicks=[]
169 try:
170 buf=prototypes.buffer(self.getfilecontents("sms/mediacan000.dat"))
171 sf=self.protocolclass.sms_quick_text()
172 sf.readfrombuffer(buf, logtitle="SMS quicktext file sms/mediacan000.dat")
173 for rec in sf.msgs:
174 if rec.msg!="":
175 quicks.append({ 'text': rec.msg, 'type': sms.CannedMsgEntry.user_type })
176 except com_brew.BrewNoSuchFileException:
177 pass
178 return quicks
179
201
229
231 out=""
232 for i in range(num_septets):
233 tmp = (msg[(i*7)/8].byte<<8) | msg[((i*7)/8) + 1].byte
234 bit_index = 9 - ((i*7) % 8)
235 out += chr((tmp >> bit_index) & 0x7f)
236 return out
237
239 data_len = ((msg[0].byte+1)*8+6)/7
240 seven_bits={}
241 raw={}
242 out={}
243
244
245 for i in range(0, (num_septets*7)/8+8, 7):
246 for k in range(7):
247 raw[i+6-k]=msg[i+k].byte
248
249 for i in range(num_septets+7):
250 tmp = (raw[(i*7)/8]<<8) | raw[((i*7)/8) + 1]
251 bit_index = 9 - ((i*7) % 8)
252 seven_bits[i] = (tmp >> bit_index) & 0x7f
253
254 i=0
255 for i in range(0, num_septets+7, 8):
256 for k in range(8):
257 if(i+7-k-data_len>=0):
258 if i+k<num_septets+7:
259 out[i+7-k-data_len]=seven_bits[i+k]
260 res=""
261 for i in range(num_septets-data_len):
262 res+=chr(out[i])
263 return res
264
266 result=""
267 for i in range(len):
268 if(raw[i].byte==10):
269 result+="0"
270 else:
271 result+="%d" % raw[i].byte
272 return result
273
274 - def getcallhistory(self, result):
275 res={}
276
277
278 if hasattr(self.protocolclass, 'this_takes_the_prize_for_the_most_brain_dead_call_history_file_naming_ive_seen'):
279 self._readhistoryfile("pim/missed_log.dat", 'Incoming', res)
280 self._readhistoryfile("pim/outgoing_log.dat", 'Missed', res)
281 self._readhistoryfile("pim/incoming_log.dat", 'Outgoing', res)
282 else:
283 self._readhistoryfile("pim/missed_log.dat", 'Missed', res)
284 self._readhistoryfile("pim/outgoing_log.dat", 'Outgoing', res)
285 self._readhistoryfile("pim/incoming_log.dat", 'Incoming', res)
286 result['call_history']=res
287 return result
288
289 - def _readhistoryfile(self, fname, folder, res):
290 try:
291 buf=prototypes.buffer(self.getfilecontents(fname))
292 ch=self.protocolclass.callhistory()
293 ch.readfrombuffer(buf, logtitle="Call History")
294 for call_idx in range(ch.numcalls):
295 call=ch.calls[call_idx]
296 if call.number=='' and call.name=='':
297 continue
298 entry=call_history.CallHistoryEntry()
299 entry.folder=folder
300 if call.duration:
301 entry.duration=call.duration
302 entry.datetime=((call.GPStime))
303 if call.number=='':
304 entry.number=call.name
305 else:
306 entry.number=call.number
307 if call.name:
308 entry.name=call.name
309 res[entry.id]=entry
310 except (com_brew.BrewNoSuchFileException,
311 IndexError):
312 pass
313 return
314
317
320
322 """Reads the phonebook data. The L{getfundamentals} information will
323 already be in result."""
324
325 speeds={}
326 try:
327 if self.protocolclass.NUMSPEEDDIALS:
328 self.log("Reading speed dials")
329 buf=prototypes.buffer(self.getfilecontents("pim/pbspeed.dat"))
330 sd=self.protocolclass.speeddials()
331 sd.readfrombuffer(buf, logtitle="Read speed dials")
332 for i in range(self.protocolclass.FIRSTSPEEDDIAL, self.protocolclass.LASTSPEEDDIAL+1):
333 if sd.speeddials[i].entry<0 or sd.speeddials[i].entry>self.protocolclass.NUMPHONEBOOKENTRIES:
334 continue
335 l=speeds.get(sd.speeddials[i].entry, [])
336 l.append((i, sd.speeddials[i].number))
337 speeds[sd.speeddials[i].entry]=l
338 except com_brew.BrewNoSuchFileException:
339 pass
340
341 pbook={}
342
343
344
345 self.mode=self.MODENONE
346 self.setmode(self.MODEBREW)
347 self.log("Reading number of phonebook entries")
348 req=self.protocolclass.pbinforequest()
349 res=self.sendpbcommand(req, self.protocolclass.pbinforesponse)
350 numentries=res.numentries
351 if numentries<0 or numentries>1000:
352 self.log("The phone is lying about how many entries are in the phonebook so we are doing it the hard way")
353 numentries=0
354 firstserial=None
355 loop=xrange(0,1000)
356 hardway=True
357 else:
358 self.log("There are %d entries" % (numentries,))
359 loop=xrange(0, numentries)
360 hardway=False
361
362 self.sendpbcommand(self.protocolclass.pbinitrequest(), self.protocolclass.pbinitresponse)
363 problemsdetected=False
364 dupecheck={}
365 for i in loop:
366 try:
367 if hardway:
368 numentries+=1
369 req=self.protocolclass.pbreadentryrequest()
370 res=self.sendpbcommand(req, self.protocolclass.pbreadentryresponse)
371 self.log("Read entry "+`i`+" - "+res.entry.name)
372 entry=self.extractphonebookentry(res.entry, speeds, result)
373 if hardway and firstserial is None:
374 firstserial=res.entry.serial1
375 pbook[i]=entry
376 if res.entry.serial1 in dupecheck:
377 self.log("Entry %s has same serial as entry %s. This will cause problems." % (`entry`, dupecheck[res.entry.serial1]))
378 problemsdetected=True
379 else:
380 dupecheck[res.entry.serial1]=entry
381 self.progress(i, numentries, res.entry.name)
382 except common.PhoneBookBusyException:
383 raise
384 except Exception, e:
385
386 self.log('Failed to read entry %d'%i)
387 self.log('Exception %s raised'%`e`)
388 if __debug__:
389 raise
390
391 req=self.protocolclass.pbnextentryrequest()
392 res=self.sendpbcommand(req, self.protocolclass.pbnextentryresponse)
393 if hardway:
394
395 if res.serial==firstserial or res.serial==0:
396 break
397
398 self.progress(numentries, numentries, "Phone book read completed")
399
400 if problemsdetected:
401 self.log("There are duplicate serial numbers. See above for details.")
402 raise common.IntegrityCheckFailed(self.desc, "Data in phonebook is inconsistent. There are multiple entries with the same serial number. See the log.")
403
404 result['phonebook']=pbook
405 cats=[]
406 for i in result['groups']:
407 if result['groups'][i]['name']!='No Group':
408 cats.append(result['groups'][i]['name'])
409 result['categories']=cats
410 print "returning keys",result.keys()
411 return pbook
412
424
439
441 "Saves out the phonebook"
442 self.savegroups(data)
443
444 progressmax=len(data['phonebook'].keys())
445
446
447 if data.get('speeddials',None) is not None:
448 progressmax+=len(data['phonebook'].keys())
449
450
451
452
453
454
455 serialupdates=[]
456 existingpbook={}
457 self.mode=self.MODENONE
458 self.setmode(self.MODEBREW)
459 self.setmode(self.MODEPHONEBOOK)
460
461 req=self.protocolclass.pbinforequest()
462 res=self.sendpbcommand(req, self.protocolclass.pbinforesponse)
463 numexistingentries=res.numentries
464 if numexistingentries<0 or numexistingentries>1000:
465 self.log("The phone is lying about how many entries are in the phonebook so we are doing it the hard way")
466 numexistingentries=0
467 firstserial=None
468 loop=xrange(0,1000)
469 hardway=True
470 else:
471 self.log("There are %d existing entries" % (numexistingentries,))
472 progressmax+=numexistingentries
473 loop=xrange(0, numexistingentries)
474 hardway=False
475 progresscur=0
476
477 self.sendpbcommand(self.protocolclass.pbinitrequest(), self.protocolclass.pbinitresponse)
478 for i in loop:
479
480 if hardway:
481 numexistingentries+=1
482 progressmax+=1
483 req=self.protocolclass.pbreadentryrequest()
484 res=self.sendpbcommand(req, self.protocolclass.pbreadentryresponse)
485
486 entry={ 'number': res.entry.entrynumber, 'serial1': res.entry.serial1,
487 'serial2': res.entry.serial2, 'name': res.entry.name}
488 assert entry['serial1']==entry['serial2']
489 self.log("Reading entry "+`i`+" - "+entry['name'])
490 if hardway and firstserial is None:
491 firstserial=res.entry.serial1
492 existingpbook[i]=entry
493 self.progress(progresscur, progressmax, "existing "+entry['name'])
494
495 req=self.protocolclass.pbnextentryrequest()
496 res=self.sendpbcommand(req, self.protocolclass.pbnextentryresponse)
497 progresscur+=1
498 if hardway:
499
500 if res.serial==firstserial or res.serial==0:
501 break
502
503
504
505 pbook=data['phonebook']
506 dellist=[]
507 for i in range(0, numexistingentries):
508 ii=existingpbook[i]
509 serial=ii['serial1']
510 item=self._findserial(serial, pbook)
511 if item is None:
512 dellist.append(i)
513
514 progressmax+=len(dellist)
515
516
517 for i in dellist:
518 progresscur+=1
519 numexistingentries-=1
520 ii=existingpbook[i]
521 self.log("Deleting entry "+`i`+" - "+ii['name'])
522 req=self.protocolclass.pbdeleteentryrequest()
523 req.serial1=ii['serial1']
524 req.serial2=ii['serial2']
525 req.entrynumber=ii['number']
526 self.sendpbcommand(req, self.protocolclass.pbdeleteentryresponse)
527 self.progress(progresscur, progressmax, "Deleting "+ii['name'])
528
529 del existingpbook[i]
530
531
532 counter=0
533
534 keys=existingpbook.keys()
535 existingserials=[]
536 keys.sort()
537 for i in keys:
538 progresscur+=1
539 ii=pbook[self._findserial(existingpbook[i]['serial1'], pbook)]
540 self.log("Rewriting entry "+`i`+" - "+ii['name'])
541 self.progress(progresscur, progressmax, "Rewriting "+ii['name'])
542 entry=self.makeentry(counter, ii, data)
543 counter+=1
544 existingserials.append(existingpbook[i]['serial1'])
545 req=self.protocolclass.pbupdateentryrequest()
546 req.entry=entry
547 res=self.sendpbcommand(req, self.protocolclass.pbupdateentryresponse)
548 serialupdates.append( ( ii["bitpimserial"],
549 {'sourcetype': self.serialsname, 'serial1': res.serial1, 'serial2': res.serial1,
550 'sourceuniqueid': data['uniqueserial']})
551 )
552 assert ii['serial1']==res.serial1
553
554
555 keys=pbook.keys()
556 keys.sort()
557 for i in keys:
558 try:
559 ii=pbook[i]
560 if ii['serial1'] in existingserials:
561 continue
562 progresscur+=1
563 entry=self.makeentry(counter, ii, data)
564 counter+=1
565 self.log("Appending entry "+ii['name'])
566 self.progress(progresscur, progressmax, "Writing "+ii['name'])
567 req=self.protocolclass.pbappendentryrequest()
568 req.entry=entry
569 res=self.sendpbcommand(req, self.protocolclass.pbappendentryresponse)
570 serialupdates.append( ( ii["bitpimserial"],
571 {'sourcetype': self.serialsname, 'serial1': res.newserial, 'serial2': res.newserial,
572 'sourceuniqueid': data['uniqueserial']})
573 )
574 except:
575 self.log('Failed to write entry: '+ii['name'])
576 if __debug__:
577 raise
578 data["serialupdates"]=serialupdates
579
580 if data.get("speeddials",None) is not None:
581
582
583
584
585 newspeeds={}
586 if len(data['speeddials']):
587
588 self.mode=self.MODENONE
589 self.setmode(self.MODEBREW)
590 self.setmode(self.MODEPHONEBOOK)
591 self.log("Searching for speed dials")
592 self.sendpbcommand(self.protocolclass.pbinitrequest(), self.protocolclass.pbinitresponse)
593 for i in range(len(pbook)):
594
595 req=self.protocolclass.pbreadentryrequest()
596 res=self.sendpbcommand(req, self.protocolclass.pbreadentryresponse)
597 self.log("Scanning "+res.entry.name)
598 progresscur+=1
599
600 serial=res.entry.serial1
601 found=False
602 for bps, serials in serialupdates:
603 if serials['serial1']==serial:
604
605 for sd in data['speeddials']:
606 xx=data['speeddials'][sd]
607 if xx[0]==bps:
608 found=True
609 if(getattr(self.protocolclass, 'SPEEDDIALINDEX', 0)==0):
610 newspeeds[sd]=(res.entry.entrynumber, xx[1])
611 else:
612 newspeeds[sd]=(res.entry.entrynumber, res.entry.numbertypes[xx[1]].numbertype)
613 nt=self.protocolclass.numbertypetab[res.entry.numbertypes[xx[1]].numbertype]
614 self.log("Speed dial #%d = %s (%s/%d)" % (sd, res.entry.name, nt, xx[1]))
615 self.progress(progresscur, progressmax, "Speed dial #%d = %s (%s/%d)" % (sd, res.entry.name, nt, xx[1]))
616 if not found:
617 self.progress(progresscur, progressmax, "Scanning "+res.entry.name)
618
619 self.sendpbcommand(self.protocolclass.pbnextentryrequest(), self.protocolclass.pbnextentryresponse)
620
621 self.progress(progressmax, progressmax, "Finished scanning")
622 print "new speed dials is",newspeeds
623 req=self.protocolclass.speeddials()
624 for i in range(self.protocolclass.NUMSPEEDDIALS):
625 sd=self.protocolclass.speeddial()
626 if i in newspeeds:
627 sd.entry=newspeeds[i][0]
628 sd.number=newspeeds[i][1]
629 req.speeddials.append(sd)
630 buffer=prototypes.buffer()
631 req.writetobuffer(buffer, logtitle="New speed dials")
632
633
634 self.log("Checking existing speed dials")
635 if buffer.getvalue()!=self.getfilecontents("pim/pbspeed.dat"):
636 self.writefile("pim/pbspeed.dat", buffer.getvalue())
637 self.log("Your phone has to be rebooted due to the speed dials changing")
638 self.progress(progressmax, progressmax, "Rebooting phone")
639 data["rebootphone"]=True
640 else:
641 self.log("No changes to speed dials")
642
643 return data
644
645
647 """Searches dict to find entry with matching serial. If not found,
648 returns None"""
649 for i in dict:
650 if dict[i]['serial1']==serial:
651 return i
652 return None
653
655 "turn all negative keys into positive ones for index"
656 res={}
657 keys=d.keys()
658 keys.sort()
659 keys.reverse()
660 for k in keys:
661 if k<0:
662 for c in range(999999):
663 if c not in keys and c not in res:
664 break
665 res[c]=d[k]
666 else:
667 res[k]=d[k]
668 return res
669
671 """Return a phonebook entry in BitPim format. This is called from getphonebook."""
672 res={}
673
674 res['serials']=[ {'sourcetype': self.serialsname, 'serial1': entry.serial1, 'serial2': entry.serial2,
675 'sourceuniqueid': fundamentals['uniqueserial']} ]
676
677 res['names']=[ {'full': entry.name} ]
678
679 cat=fundamentals['groups'].get(entry.group, {'name': "No Group"})['name']
680 if cat!="No Group":
681 res['categories']=[ {'category': cat} ]
682
683 res['emails']=[]
684 for i in entry.emails:
685 if len(i.email):
686 res['emails'].append( {'email': i.email} )
687 if not len(res['emails']): del res['emails']
688
689 if 'url' in entry.getfields() and len(entry.url):
690 res['urls']=[ {'url': entry.url} ]
691
692 if 'secret' in entry.getfields() and entry.secret:
693
694 res['flags']=[ {'secret': entry.secret } ]
695
696 if 'memo' in entry.getfields() and len(entry.memo):
697 res['memos']=[ {'memo': entry.memo } ]
698
699 if entry.wallpaper!=self.protocolclass.NOWALLPAPER:
700 try:
701 paper=fundamentals['wallpaper-index'][entry.wallpaper]['name']
702 res['wallpapers']=[ {'wallpaper': paper, 'use': 'call'} ]
703 except:
704 print "can't find wallpaper for index",entry.wallpaper
705 pass
706
707
708 res['ringtones']=[]
709 if 'ringtone' in entry.getfields() and entry.ringtone!=self.protocolclass.NORINGTONE:
710 try:
711 tone=fundamentals['ringtone-index'][entry.ringtone]['name']
712 res['ringtones'].append({'ringtone': tone, 'use': 'call'})
713 except:
714 print "can't find ringtone for index",entry.ringtone
715 if 'msgringtone' in entry.getfields() and entry.msgringtone!=self.protocolclass.NOMSGRINGTONE:
716 try:
717 tone=fundamentals['ringtone-index'][entry.msgringtone]['name']
718 res['ringtones'].append({'ringtone': tone, 'use': 'message'})
719 except:
720 print "can't find ringtone for index",entry.msgringtone
721 if len(res['ringtones'])==0:
722 del res['ringtones']
723 if(getattr(self.protocolclass, 'SPEEDDIALINDEX', 0)==0):
724 res=self._assignpbtypeandspeeddialsbyposition(entry, speeds, res)
725 else:
726 res=self._assignpbtypeandspeeddialsbytype(entry, speeds, res)
727 return res
728
730
731 res['numbers']=[]
732 for i in range(self.protocolclass.NUMPHONENUMBERS):
733 num=entry.numbers[i].number
734 type=entry.numbertypes[i].numbertype
735 if len(num):
736 t=self.protocolclass.numbertypetab[type]
737 if t[-1]=='2':
738 t=t[:-1]
739 res['numbers'].append({'number': num, 'type': t})
740
741 if entry.entrynumber in speeds:
742 for speeddial,numberindex in speeds[entry.entrynumber]:
743 try:
744 res['numbers'][numberindex]['speeddial']=speeddial
745 except IndexError:
746 print "speed dial refers to non-existent number\n",res['numbers'],"\n",numberindex,speeddial
747 return res
748
750
751 _sd_list=speeds.get(entry.entrynumber, [])
752 _sd_dict={}
753 for _sd in _sd_list:
754 _sd_dict[_sd[1]]=_sd[0]
755 res['numbers']=[]
756 for i in range(self.protocolclass.NUMPHONENUMBERS):
757 num=entry.numbers[i].number
758 type=entry.numbertypes[i].numbertype
759 if len(num):
760 t=self.protocolclass.numbertypetab[type]
761 if t[-1]=='2':
762 t=t[:-1]
763 res['numbers'].append({'number': num, 'type': t})
764
765 if _sd_dict.get(type, None):
766 res['numbers'][i]['speeddial']=_sd_dict[type]
767 return res
768
783
784 - def makeentry(self, counter, entry, data):
785 """Creates pbentry object
786
787 @param counter: The new entry number
788 @param entry: The phonebook object (as returned from convertphonebooktophone) that we
789 are using as the source
790 @param data: The main dictionary, which we use to get access to media indices amongst
791 other things
792 """
793 e=self.protocolclass.pbentry()
794 e.entrynumber=counter
795
796 for k in entry:
797
798 if k in ('emails', 'numbers', 'numbertypes'):
799 l=getattr(e,k)
800 for item in entry[k]:
801 l.append(item)
802 elif k=='ringtone':
803 e.ringtone=self._findmediainindex(data['ringtone-index'], entry['ringtone'], entry['name'], 'ringtone')
804 elif k=='msgringtone':
805 e.msgringtone=self._findmediainindex(data['ringtone-index'], entry['msgringtone'], entry['name'], 'message ringtone')
806 elif k=='wallpaper':
807 e.wallpaper=self._findmediainindex(data['wallpaper-index'], entry['wallpaper'], entry['name'], 'wallpaper')
808 elif k in e.getfields():
809
810 setattr(e,k,entry[k])
811
812 return e
813
815 req=p_brew.memoryconfigrequest()
816 respc=p_brew.memoryconfigresponse
817
818 for baud in 0, 38400, 115200:
819 if baud:
820 if not self.comm.setbaudrate(baud):
821 continue
822 try:
823 self.sendbrewcommand(req, respc, callsetmode=False)
824 return True
825 except com_phone.modeignoreerrortypes:
826 pass
827 return False
828
829 brew_version_txt_key='brew_version.txt'
830 brew_version_file='brew/version.txt'
831 lgpbinfo_key='lgpbinfo'
832 esn_file_key='esn_file'
833 esn_file='nvm/$SYS.ESN'
834 my_model='VX4400'
873
875
876 try:
877 if data is None:
878 s=self.getfilecontents(self.esn_file)
879 else:
880 s=data
881 if s:
882 s=s[85:89]
883 return '%02X%02X%02X%02X'%(ord(s[3]), ord(s[2]),
884 ord(s[1]), ord(s[0]))
885 except:
886 if __debug__:
887 raise
888
901 @classmethod
902 - def detectphone(_, coms, likely_ports, res, _module, _log):
903 if not likely_ports:
904
905 return None
906 for port in likely_ports:
907 if not res.has_key(port):
908 res[port]={ 'mode_modem': None, 'mode_brew': None,
909 'manufacturer': None, 'model': None,
910 'firmware_version': None, 'esn': None,
911 'firmwareresponse': None }
912 try:
913 if res[port]['mode_brew']==False or \
914 res[port]['model']:
915
916
917 continue
918 p=_module.Phone(_log, commport.CommConnection(_log, port, timeout=1))
919 if res[port]['mode_brew'] is None:
920 res[port]['mode_brew']=p.is_mode_brew()
921 if res[port]['mode_brew']:
922 p.get_detect_data(res[port])
923 p.eval_detect_data(res[port])
924 p.comm.close()
925 except:
926 if __debug__:
927 raise
928
929
957
959
960
961 voice_files={}
962 if self._cal_has_voice_id:
963 try:
964 file_list=self.listfiles(self.protocolclass.cal_dir)
965 for k in file_list.keys():
966 if k.endswith(self.protocolclass.cal_voice_ext):
967 voice_files[int(k[8:11])]=k
968 except:
969 self.log('Failed to list Calendar Voice Files')
970
971 sc=self.protocolclass.schedulefile()
972 sc_ex=self.set_cal(sc, dict.get('calendar', {}),
973 dict.get('ringtone-index', {}),
974 voice_files)
975 buf=prototypes.buffer()
976 sc.writetobuffer(buf, logtitle="Calendar data")
977 self.writefile(self.protocolclass.cal_data_file_name,
978 buf.getvalue())
979
980 exceptions_file=self.protocolclass.scheduleexceptionfile()
981 for k,l in sc_ex.items():
982 for x in l:
983 _ex=self.protocolclass.scheduleexception()
984 _ex.pos=k
985 _ex.year, _ex.month, _ex.day=x
986 exceptions_file.items.append(_ex)
987 buf=prototypes.buffer()
988 exceptions_file.writetobuffer(buf, logtitle="calendar exceptions")
989 self.writefile(self.protocolclass.cal_exception_file_name,
990 buf.getvalue())
991
992 if self._cal_has_voice_id:
993 for k,e in voice_files.items():
994 try:
995 self.rmfile(e)
996 except:
997 self.log('Failed to delete file '+e)
998 return dict
999
1000 _repeat_values={
1001 protocolclass.CAL_REP_DAILY: bpcalendar.RepeatEntry.daily,
1002 protocolclass.CAL_REP_MONFRI: bpcalendar.RepeatEntry.daily,
1003 protocolclass.CAL_REP_WEEKLY: bpcalendar.RepeatEntry.weekly,
1004 protocolclass.CAL_REP_MONTHLY: bpcalendar.RepeatEntry.monthly,
1005 protocolclass.CAL_REP_YEARLY: bpcalendar.RepeatEntry.yearly
1006 }
1007
1030
1032 if event.hasvoice:
1033 entry.voice=event.voiceid
1034
1035 - def _build_cal_entry(self, event, exceptions, ringtone_index):
1036 try:
1037
1038
1039 entry=bpcalendar.CalendarEntry()
1040 entry.start=event.start
1041 try:
1042 entry.end=event.end
1043
1044 except ValueError:
1045 entry.end=self.protocolclass.CAL_REPEAT_DATE
1046 entry.desc_loc=event.description
1047
1048 if entry.start[3:]==(0, 0) and entry.end[3:]==(23, 59):
1049 entry.allday=True
1050
1051 if event.alarmtype:
1052 entry.alarm=event.alarmhours*60+event.alarmminutes
1053
1054 rt_idx=event.ringtone
1055
1056 if rt_idx<50:
1057
1058 rt_idx+=1
1059 entry.ringtone=ringtone_index.get(rt_idx, {'name': None} )['name']
1060
1061 if self._cal_has_voice_id:
1062 self._get_voice_id(event, entry)
1063
1064 entry.repeat=self._build_cal_repeat(event, exceptions)
1065 return entry
1066 except ValueError:
1067
1068 pass
1069 except:
1070
1071 if __debug__:
1072 raise
1073
1074 - def get_cal(self, sch_file, exceptions, ringtone_index):
1075 res={}
1076 for event in sch_file.events:
1077 if event.pos==-1:
1078 continue
1079 cal_event=self._build_cal_entry(event, exceptions, ringtone_index)
1080 if cal_event:
1081 res[cal_event.id]=cal_event
1082 return res
1083
1084 _alarm_info={
1085 -1: (protocolclass.CAL_REMINDER_NONE, 100, 100),
1086 0: (protocolclass.CAL_REMINDER_ONTIME, 0, 0),
1087 5: (protocolclass.CAL_REMINDER_5MIN, 5, 0),
1088 10: (protocolclass.CAL_REMINDER_10MIN, 10, 0),
1089 60: (protocolclass.CAL_REMINDER_1HOUR, 0, 1),
1090 1440: (protocolclass.CAL_REMINDER_1DAY, 0, 24),
1091 2880: (protocolclass.CAL_REMINDER_2DAYS, 0, 48) }
1092 _default_alarm=(protocolclass.CAL_REMINDER_NONE, 100, 100)
1093 _phone_dow={
1094 1: protocolclass.CAL_DOW_SUN,
1095 2: protocolclass.CAL_DOW_MON,
1096 4: protocolclass.CAL_DOW_TUE,
1097 8: protocolclass.CAL_DOW_WED,
1098 16: protocolclass.CAL_DOW_THU,
1099 32: protocolclass.CAL_DOW_FRI,
1100 64: protocolclass.CAL_DOW_SAT
1101 }
1102
1140
1154
1164
1165 - def _set_cal_event(self, event, entry, exceptions, ringtone_index,
1166 voice_files):
1196
1197 - def set_cal(self, sch_file, cal_dict, ringtone_index, voice_files):
1198 exceptions={}
1199 _pos=2
1200 _packet_size=None
1201
1202 _entry_cnt=0
1203 for k, e in cal_dict.items():
1204
1205
1206 event=self.protocolclass.scheduleevent()
1207 event.pos=_pos
1208 self._set_cal_event(event, e, exceptions, ringtone_index,
1209 voice_files)
1210 sch_file.events.append(event)
1211 if not _packet_size:
1212 _packet_size=event.packetsize()
1213 _pos+=_packet_size
1214 _entry_cnt+=1
1215 if _entry_cnt>=self.protocolclass.NUMCALENDARENTRIES:
1216 break
1217 sch_file.numactiveitems=_entry_cnt
1218 return exceptions
1219
1220
1237
1252
1253
1254 parentprofile=com_phone.Profile
1256 protocolclass=Phone.protocolclass
1257 serialsname=Phone.serialsname
1258 BP_Calendar_Version=3
1259 phone_manufacturer='LG Electronics Inc'
1260 phone_model='VX4400'
1261 brew_required=True
1262
1263 WALLPAPER_WIDTH=120
1264 WALLPAPER_HEIGHT=98
1265 MAX_WALLPAPER_BASENAME_LENGTH=19
1266 WALLPAPER_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789 ."
1267 WALLPAPER_CONVERT_FORMAT="bmp"
1268
1269 MAX_RINGTONE_BASENAME_LENGTH=19
1270 RINGTONE_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789 ."
1271 DIALSTRING_CHARS="[^0-9PT#*]"
1272
1273
1274 usbids_straight=( ( 0x1004, 0x6000, 2), )
1275 usbids_usbtoserial=(
1276 ( 0x067b, 0x2303, None),
1277 ( 0x0403, 0x6001, None),
1278 ( 0x0731, 0x2003, None),
1279 )
1280 usbids=usbids_straight+usbids_usbtoserial
1281
1282
1283 deviceclasses=("serial",)
1284
1285
1286 imageorigins={}
1287 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images"))
1290
1291
1292 imagetargets={}
1293 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper",
1294 {'width': 120, 'height': 98, 'format': "BMP"}))
1295 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "pictureid",
1296 {'width': 120, 'height': 98, 'format': "BMP"}))
1297 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "fullscreen",
1298 {'width': 120, 'height': 133, 'format': "BMP"}))
1299
1302
1305
1306
1312
1313
1315 "Assigns groups based on category data"
1316
1317 pad=[]
1318 keys=data['groups'].keys()
1319 keys.sort()
1320 for k in keys:
1321 if k:
1322 name=data['groups'][k]['name']
1323 pad.append(name)
1324
1325 groups=helper.getmostpopularcategories(self.protocolclass.MAX_PHONEBOOK_GROUPS, data['phonebook'], ["No Group"], 22, pad)
1326
1327
1328 groups.sort()
1329
1330
1331 newgroups={}
1332
1333
1334 newgroups[0]={'name': 'No Group', 'icon': 0}
1335
1336
1337 for name in groups:
1338
1339 if name=="No Group": continue
1340 key,value=self._getgroup(name, data['groups'])
1341 if key is not None and key!=0:
1342 newgroups[key]=value
1343
1344 for name in groups:
1345 key,value=self._getgroup(name, newgroups)
1346 if key is None:
1347 for key in range(1,100000):
1348 if key not in newgroups:
1349 newgroups[key]={'name': name, 'icon': 1}
1350 break
1351
1352
1353 if data['groups']!=newgroups:
1354 data['groups']=newgroups
1355 data['rebootphone']=True
1356
1358 """Converts the data to what will be used by the phone
1359
1360 @param data: contains the dict returned by getfundamentals
1361 as well as where the results go"""
1362 results={}
1363
1364 speeds={}
1365
1366 self.normalisegroups(helper, data)
1367
1368 for pbentry in data['phonebook']:
1369 if len(results)==self.protocolclass.NUMPHONEBOOKENTRIES:
1370 break
1371 e={}
1372 entry=data['phonebook'][pbentry]
1373 try:
1374
1375 serial1=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'serial1', 0)
1376 serial2=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'serial2', serial1)
1377
1378 e['serial1']=serial1
1379 e['serial2']=serial2
1380 for ss in entry["serials"]:
1381 if ss["sourcetype"]=="bitpim":
1382 e['bitpimserial']=ss
1383 assert e['bitpimserial']
1384
1385
1386 e['name']=helper.getfullname(entry.get('names', []),1,1,22)[0]
1387
1388
1389 cat=helper.makeone(helper.getcategory(entry.get('categories', []),0,1,22), None)
1390 if cat is None:
1391 e['group']=0
1392 else:
1393 key,value=self._getgroup(cat, data['groups'])
1394 if key is not None:
1395 e['group']=key
1396 else:
1397
1398 e['group']=0
1399
1400
1401 emails=helper.getemails(entry.get('emails', []) ,0,self.protocolclass.NUMEMAILS,48)
1402 e['emails']=helper.filllist(emails, self.protocolclass.NUMEMAILS, "")
1403
1404
1405 e['url']=helper.makeone(helper.geturls(entry.get('urls', []), 0,1,48), "")
1406
1407
1408 e['memo']=helper.makeone(helper.getmemos(entry.get('memos', []), 0, 1, self.protocolclass.MEMOLENGTH-1), "")
1409
1410
1411
1412 minnumbers=1
1413 if len(emails): minnumbers=0
1414 numbers=helper.getnumbers(entry.get('numbers', []),minnumbers,self.protocolclass.NUMPHONENUMBERS)
1415 e['numbertypes']=[]
1416 e['numbers']=[]
1417 for numindex in range(len(numbers)):
1418 num=numbers[numindex]
1419
1420 b4=len(e['numbertypes'])
1421 type=num['type']
1422 for i,t in enumerate(self.protocolclass.numbertypetab):
1423 if type==t:
1424
1425 if i in e['numbertypes'] and t[-1]!='2':
1426 type+='2'
1427 continue
1428 e['numbertypes'].append(i)
1429 break
1430 if t=='none':
1431 e['numbertypes'].append(i)
1432 break
1433 if len(e['numbertypes'])==b4:
1434
1435 helper.add_error_message('Number %s (%s/%s) not supported and ignored.'%
1436 (num['number'], e['name'], num['type']))
1437 continue
1438
1439 number=self.phonize(num['number'])
1440 if len(number)==0:
1441
1442 continue
1443 if len(number)>48:
1444
1445 number=number[:48]
1446 e['numbers'].append(number)
1447
1448 sd=num.get("speeddial", -1)
1449 if self.protocolclass.NUMSPEEDDIALS:
1450 if sd>=self.protocolclass.FIRSTSPEEDDIAL and sd<=self.protocolclass.LASTSPEEDDIAL:
1451 speeds[sd]=(e['bitpimserial'], numindex)
1452
1453 if len(e['numbers'])<minnumbers:
1454
1455
1456 helper.add_error_message("Name: %s. No suitable numbers or emails found" % e['name'])
1457 continue
1458 e['numbertypes']=helper.filllist(e['numbertypes'], 5, 0)
1459 e['numbers']=helper.filllist(e['numbers'], 5, "")
1460
1461
1462 e['ringtone']=helper.getringtone(entry.get('ringtones', []), 'call', None)
1463 e['msgringtone']=helper.getringtone(entry.get('ringtones', []), 'message', None)
1464 e['wallpaper']=helper.getwallpaper(entry.get('wallpapers', []), 'call', None)
1465
1466
1467 e['secret']=helper.getflag(entry.get('flags',[]), 'secret', False)
1468
1469 results[pbentry]=e
1470
1471 except helper.ConversionFailed:
1472 continue
1473
1474 if self.protocolclass.NUMSPEEDDIALS:
1475 data['speeddials']=speeds
1476 data['phonebook']=results
1477 return data
1478
1479 _supportedsyncs=(
1480 ('phonebook', 'read', None),
1481 ('calendar', 'read', None),
1482 ('wallpaper', 'read', None),
1483 ('ringtone', 'read', None),
1484 ('call_history', 'read', None),
1485 ('sms', 'read', None),
1486 ('memo', 'read', None),
1487 ('phonebook', 'write', 'OVERWRITE'),
1488 ('calendar', 'write', 'OVERWRITE'),
1489 ('wallpaper', 'write', 'MERGE'),
1490 ('wallpaper', 'write', 'OVERWRITE'),
1491 ('ringtone', 'write', 'MERGE'),
1492 ('ringtone', 'write', 'OVERWRITE'),
1493 ('sms', 'write', 'OVERWRITE'),
1494 ('memo', 'write', 'OVERWRITE'),
1495 )
1496
1497 - def QueryAudio(self, origin, currentextension, afi):
1498
1499 if afi.format in ("MIDI", "QCP", "PMD"):
1500 return currentextension, afi
1501
1502 if afi.format=="MP3":
1503 if afi.channels==1 and 8<=afi.bitrate<=64 and 16000<=afi.samplerate<=22050:
1504 return currentextension, afi
1505
1506 return ("mp3", fileinfo.AudioFileInfo(afi, **{'format': 'MP3', 'channels': 1, 'bitrate': 32, 'samplerate': 22050}))
1507