Package phones :: Module com_lgvx4400
[hide private]
[frames] | no frames]

Source Code for Module phones.com_lgvx4400

   1  ### BITPIM 
   2  ### 
   3  ### Copyright (C) 2003-2004 Roger Binns <rogerb@rogerbinns.com> 
   4  ### 
   5  ### This program is free software; you can redistribute it and/or modify 
   6  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
   7  ### 
   8  ### $Id: com_lgvx4400.py 4636 2008-07-21 03:25:38Z djpham $ 
   9   
  10  """Communicate with the LG VX4400 cell phone""" 
  11   
  12  # standard modules 
  13  import datetime 
  14  import re 
  15  import time 
  16  import cStringIO 
  17  import sha 
  18   
  19  # my modules 
  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 # offset, index file, files location, type, maximumentries 48 ( 10, "dloadindex/brewImageIndex.map", "brew/shared", "images", 30), 49 ) 50 51 ringtonelocations=( 52 # offset, index file, files location, type, maximumentries 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):
69 "Calls all the constructors and sets initial modes" 70 com_phone.Phone.__init__(self, logtarget, commport) 71 com_brew.BrewProtocol.__init__(self) 72 com_lg.LGPhonebook.__init__(self) 73 com_lg.LGIndexedMedia.__init__(self) 74 self.log("Attempting to contact phone") 75 self.mode=self.MODENONE 76 self._cal_has_voice_id=hasattr(self.protocolclass, 'cal_has_voice_id') \ 77 and self.protocolclass.cal_has_voice_id
78
79 - def getfundamentals(self, results):
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 # use a hash of ESN and other stuff (being paranoid) 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
106 - def savesms(self, result, merge):
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 # don't create the file if there are no entries 123 buf=prototypes.buffer() 124 sf.writetobuffer(buf, logtitle="Writing calendar") 125 self.writefile(self.protocolclass.SMS_CANNED_FILENAME, buf.getvalue()) 126 return
127
128 - def getsms(self, result):
129 # get the quicktext (LG name for canned messages) 130 result['canned_msg']=self._getquicktext() 131 result['sms']=self._readsms() 132 return result
133
134 - def _readsms(self):
135 res={} 136 # go through the sms directory looking for messages 137 for item in self.listfiles("sms").values(): 138 folder=None 139 for f,pat in self.protocolclass.SMS_PATTERNS.items(): 140 if pat.match(item['name']): 141 folder=f 142 break 143 if folder: 144 buf=prototypes.buffer(self.getfilecontents(item['name'], True)) 145 self.logdata("SMS message file " +item['name'], buf.getdata()) 146 if folder=='Inbox': 147 sf=self.protocolclass.sms_in() 148 sf.readfrombuffer(buf, logtitle="SMS inbox item") 149 entry=self._getinboxmessage(sf) 150 res[entry.id]=entry 151 elif folder=='Sent': 152 sf=self.protocolclass.sms_out() 153 sf.readfrombuffer(buf, logtitle="SMS sent item") 154 entry=self._getoutboxmessage(sf) 155 res[entry.id]=entry 156 elif folder=='Saved': 157 sf=self.protocolclass.sms_saved() 158 sf.readfrombuffer(buf, logtitle="SMS saved item") 159 if sf.outboxmsg: 160 entry=self._getoutboxmessage(sf.outbox) 161 else: 162 entry=self._getinboxmessage(sf.inbox) 163 entry.folder=entry.Folder_Saved 164 res[entry.id]=entry 165 return res
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 # do nothing if file doesn't exist 178 return quicks
179
180 - def _getinboxmessage(self, sf):
181 entry=sms.SMSEntry() 182 entry.folder=entry.Folder_Inbox 183 entry.datetime="%d%02d%02dT%02d%02d%02d" % (sf.GPStime) 184 entry._from=self._getsender(sf.sender, sf.sender_length) 185 entry.subject=sf.subject 186 entry.locked=sf.locked 187 if sf.priority==0: 188 entry.priority=sms.SMSEntry.Priority_Normal 189 else: 190 entry.priority=sms.SMSEntry.Priority_High 191 entry.read=sf.read 192 txt="" 193 if sf.num_msg_elements==1 and sf.bin_header1==0: 194 txt=self._get_text_from_sms_msg_without_header(sf.msgs[0].msg, sf.msglengths[0].msglength) 195 else: 196 for i in range(sf.num_msg_elements): 197 txt+=self._get_text_from_sms_msg_with_header(sf.msgs[i].msg, sf.msglengths[i].msglength) 198 entry.text=unicode(txt, errors='ignore') 199 entry.callback=sf.callback 200 return entry
201
202 - def _getoutboxmessage(self, sf):
203 entry=sms.SMSEntry() 204 entry.folder=entry.Folder_Sent 205 entry.datetime="%d%02d%02dT%02d%02d00" % sf.timesent[:5] 206 # add all the recipients 207 for r in sf.recipients: 208 if r.number: 209 confirmed=(r.status==5) 210 confirmed_date=None 211 if confirmed: 212 confirmed_date="%d%02d%02dT%02d%02d00" % r.timereceived 213 entry.add_recipient(r.number, confirmed, confirmed_date) 214 entry.subject=sf.subject 215 txt="" 216 if sf.num_msg_elements==1 and not sf.messages[0].binary: 217 txt=self._get_text_from_sms_msg_without_header(sf.messages[0].msg, sf.messages[0].length) 218 else: 219 for i in range(sf.num_msg_elements): 220 txt+=self._get_text_from_sms_msg_with_header(sf.messages[i].msg, sf.messages[i].length) 221 entry.text=unicode(txt, errors='ignore') 222 if sf.priority==0: 223 entry.priority=sms.SMSEntry.Priority_Normal 224 else: 225 entry.priority=sms.SMSEntry.Priority_High 226 entry.locked=sf.locked 227 entry.callback=sf.callback 228 return entry
229
230 - def _get_text_from_sms_msg_without_header(self, msg, num_septets):
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
238 - def _get_text_from_sms_msg_with_header(self, msg, num_septets):
239 data_len = ((msg[0].byte+1)*8+6)/7 240 seven_bits={} 241 raw={} 242 out={} 243 # re-order the text into the correct order for separating into 244 # 7-bit characters 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 # extract the 7-bit chars 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 # correct the byte order and remove the data portion of the message 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
265 - def _getsender(self, raw, len):
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 # read the incoming call history file 277 # the telus lg8100 programmers were on something when they wrote their code. 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=='': # restricted calls have no 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 # do nothing if file doesn't exist or is corrupted 313 return
314
315 - def getwallpaperindices(self, results):
316 return self.getmediaindex(self.builtinimages, self.imagelocations, results, 'wallpaper-index')
317
318 - def getringtoneindices(self, results):
319 return self.getmediaindex(self.builtinringtones, self.ringtonelocations, results, 'ringtone-index')
320
321 - def getphonebook(self,result):
322 """Reads the phonebook data. The L{getfundamentals} information will 323 already be in result.""" 324 # Read speed dials first 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 # Bug in the phone. if you repeatedly read the phone book it starts 343 # returning a random number as the number of entries. We get around 344 # this by switching into brew mode which clears that. 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 # reset cursor 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 # Something's wrong with this entry, log it and skip 386 self.log('Failed to read entry %d'%i) 387 self.log('Exception %s raised'%`e`) 388 if __debug__: 389 raise 390 #### Advance to next entry 391 req=self.protocolclass.pbnextentryrequest() 392 res=self.sendpbcommand(req, self.protocolclass.pbnextentryresponse) 393 if hardway: 394 # look to see if we have looped 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
413 - def getgroups(self, results):
414 self.log("Reading group information") 415 buf=prototypes.buffer(self.getfilecontents("pim/pbgroup.dat")) 416 g=self.protocolclass.pbgroups() 417 g.readfrombuffer(buf, logtitle="Groups read") 418 groups={} 419 for i in range(len(g.groups)): 420 if len(g.groups[i].name): # sometimes have zero length names 421 groups[i]={ 'icon': g.groups[i].icon, 'name': g.groups[i].name } 422 results['groups']=groups 423 return groups
424
425 - def savegroups(self, data):
426 groups=data['groups'] 427 keys=groups.keys() 428 keys.sort() 429 430 g=self.protocolclass.pbgroups() 431 for k in keys: 432 e=self.protocolclass.pbgroup() 433 e.icon=groups[k]['icon'] 434 e.name=groups[k]['name'] 435 g.groups.append(e) 436 buffer=prototypes.buffer() 437 g.writetobuffer(buffer, logtitle="New group file") 438 self.writefile("pim/pbgroup.dat", buffer.getvalue())
439
440 - def savephonebook(self, data):
441 "Saves out the phonebook" 442 self.savegroups(data) 443 444 progressmax=len(data['phonebook'].keys()) 445 # if we are going to write out speeddials, we have to re-read the entire 446 # phonebook again 447 if data.get('speeddials',None) is not None: 448 progressmax+=len(data['phonebook'].keys()) 449 450 # To write the phone book, we scan through all existing entries 451 # and record their record number and serials. 452 # We then delete any entries that aren't in data 453 # We then write out our records, using overwrite or append 454 # commands as necessary 455 serialupdates=[] 456 existingpbook={} # keep track of the phonebook that is on the phone 457 self.mode=self.MODENONE 458 self.setmode(self.MODEBREW) # see note in getphonebook() for why this is necessary 459 self.setmode(self.MODEPHONEBOOK) 460 # similar loop to reading 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 # reset cursor 477 self.sendpbcommand(self.protocolclass.pbinitrequest(), self.protocolclass.pbinitresponse) 478 for i in loop: 479 ### Read current entry 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'] # always the same 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 #### Advance to next entry 495 req=self.protocolclass.pbnextentryrequest() 496 res=self.sendpbcommand(req, self.protocolclass.pbnextentryresponse) 497 progresscur+=1 498 if hardway: 499 # look to see if we have looped 500 if res.serial==firstserial or res.serial==0: 501 break 502 # we have now looped around back to begining 503 504 # Find entries that have been deleted 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) # more work to do 515 516 # Delete those entries 517 for i in dellist: 518 progresscur+=1 519 numexistingentries-=1 # keep count right 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 # also remove them from existingpbook 529 del existingpbook[i] 530 531 # counter to keep track of record number (otherwise appends don't work) 532 counter=0 533 # Now rewrite out existing entries 534 keys=existingpbook.keys() 535 existingserials=[] 536 keys.sort() # do in same order as existingpbook 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 # serial should stay the same 553 554 # Finally write out new entries 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 # already wrote this one out 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 # deal with the speeddials 580 if data.get("speeddials",None) is not None: 581 # Yes, we have to read the ENTIRE phonebook again. This 582 # is because we don't know which entry numbers actually 583 # got assigned to the various entries, and we need the 584 # actual numbers to assign to the speed dials 585 newspeeds={} 586 if len(data['speeddials']): 587 # Move cursor to begining of phonebook 588 self.mode=self.MODENONE 589 self.setmode(self.MODEBREW) # see note in getphonebook() for why this is necessary 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 ### Read current entry 595 req=self.protocolclass.pbreadentryrequest() 596 res=self.sendpbcommand(req, self.protocolclass.pbreadentryresponse) 597 self.log("Scanning "+res.entry.name) 598 progresscur+=1 599 # we have to turn the entry serial number into a bitpim serial 600 serial=res.entry.serial1 601 found=False 602 for bps, serials in serialupdates: 603 if serials['serial1']==serial: 604 # found the entry 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 # move to next entry 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 # We check the existing speed dial file as changes require a reboot 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
646 - def _findserial(self, serial, dict):
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
654 - def _normaliseindices(self, d):
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
670 - def extractphonebookentry(self, entry, speeds, fundamentals):
671 """Return a phonebook entry in BitPim format. This is called from getphonebook.""" 672 res={} 673 # serials 674 res['serials']=[ {'sourcetype': self.serialsname, 'serial1': entry.serial1, 'serial2': entry.serial2, 675 'sourceuniqueid': fundamentals['uniqueserial']} ] 676 # only one name 677 res['names']=[ {'full': entry.name} ] 678 # only one category 679 cat=fundamentals['groups'].get(entry.group, {'name': "No Group"})['name'] 680 if cat!="No Group": 681 res['categories']=[ {'category': cat} ] 682 # emails 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'] # it was empty 688 # urls 689 if 'url' in entry.getfields() and len(entry.url): 690 res['urls']=[ {'url': entry.url} ] 691 # private 692 if 'secret' in entry.getfields() and entry.secret: 693 # we only supply secret if it is true 694 res['flags']=[ {'secret': entry.secret } ] 695 # memos 696 if 'memo' in entry.getfields() and len(entry.memo): 697 res['memos']=[ {'memo': entry.memo } ] 698 # wallpapers 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 # ringtones 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
729 - def _assignpbtypeandspeeddialsbyposition(self, entry, speeds, res):
730 # numbers 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 # speed dials 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
749 - def _assignpbtypeandspeeddialsbytype(self, entry, speeds, res):
750 # for some phones (e.g. vx8100) the speeddial numberindex is really the numbertype (now why would LG want to change this!) 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 # if this is a speeddial number set it 765 if _sd_dict.get(type, None): 766 res['numbers'][i]['speeddial']=_sd_dict[type] 767 return res
768
769 - def _findmediainindex(self, index, name, pbentryname, type):
770 if type=="ringtone": default=self.protocolclass.NORINGTONE 771 elif type=="message ringtone": default=self.protocolclass.NOMSGRINGTONE 772 elif type=="wallpaper": default=self.protocolclass.NOWALLPAPER 773 else: 774 assert False, "unknown type "+type 775 776 if name is None: 777 return default 778 for i in index: 779 if index[i]['name']==name: 780 return i 781 self.log("%s: Unable to find %s %s in the index. Setting to default." % (pbentryname, type, name)) 782 return default
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 # special treatment for lists 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 # everything else we just set 810 setattr(e,k,entry[k]) 811 812 return e
813
814 - def is_mode_brew(self):
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'
835 - def get_detect_data(self, res):
836 # get the data needed for detection 837 if not res.has_key(self.brew_version_txt_key): 838 # read the BREW version.txt file, which may contain phone model info 839 print 'reading BREW version.txt' 840 try: 841 # read this file 842 s=self.getfilecontents(self.brew_version_file) 843 res[self.brew_version_txt_key]=s 844 except (com_brew.BrewNoSuchFileException, 845 com_brew.BrewBadBrewCommandException, 846 com_brew.BrewAccessDeniedException): 847 res[self.brew_version_txt_key]=None 848 except: 849 if __debug__: 850 raise 851 res[self.brew_version_txt_key]=None 852 # get pbinfo data, which also may include phone model 853 if not res.has_key(self.lgpbinfo_key): 854 print 'getting pbinfo' 855 try: 856 req=self.protocolclass.pbinforequest() 857 resp=self.sendpbcommand(req, self.protocolclass.pbstartsyncresponse) 858 res[self.lgpbinfo_key]=resp.unknown 859 except com_brew.BrewBadBrewCommandException: 860 res[self.lgpbinfo_key]=None 861 except: 862 if __debug__: 863 raise 864 res[self.lgpbinfo_key]=None 865 # attempt the get the ESN 866 if not res.has_key(self.esn_file_key): 867 print 'reading ESN file' 868 try: 869 s=self.getfilecontents(self.esn_file) 870 res[self.esn_file_key]=s 871 except: 872 res[self.esn_file_key]=None
873
874 - def get_esn(self, data=None):
875 # return the ESN for this phone 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
889 - def eval_detect_data(self, res):
890 found=False 891 if res.get(self.brew_version_txt_key, None) is not None: 892 found=res[self.brew_version_txt_key][:len(self.my_model)]==self.my_model 893 if not found and res.get(self.lgpbinfo_key, None): 894 found=res[self.lgpbinfo_key].find(self.my_model)!=-1 895 if found: 896 res['model']=self.my_model 897 res['manufacturer']='LG Electronics Inc' 898 s=res.get(self.esn_file_key, None) 899 if s: 900 res['esn']=self.get_esn(s)
901 @classmethod
902 - def detectphone(_, coms, likely_ports, res, _module, _log):
903 if not likely_ports: 904 # cannot detect any likely ports 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 # either phone is not in BREW, or a model has already 916 # been found, not much we can do now 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 # Calendar stuff------------------------------------------------------------
930 - def getcalendar(self,result):
931 # Read exceptions file first 932 try: 933 buf=prototypes.buffer(self.getfilecontents( 934 self.protocolclass.cal_exception_file_name)) 935 ex=self.protocolclass.scheduleexceptionfile() 936 ex.readfrombuffer(buf, logtitle="Calendar exceptions") 937 exceptions={} 938 for i in ex.items: 939 exceptions.setdefault(i.pos, []).append( (i.year,i.month,i.day) ) 940 except com_brew.BrewNoSuchFileException: 941 exceptions={} 942 943 # Now read schedule 944 try: 945 buf=prototypes.buffer(self.getfilecontents( 946 self.protocolclass.cal_data_file_name)) 947 if len(buf.getdata())<2: 948 # file is empty, and hence same as non-existent 949 raise com_brew.BrewNoSuchFileException() 950 sc=self.protocolclass.schedulefile() 951 sc.readfrombuffer(buf, logtitle="Calendar") 952 res=self.get_cal(sc, exceptions, result.get('ringtone-index', {})) 953 except com_brew.BrewNoSuchFileException: 954 res={} 955 result['calendar']=res 956 return result
957
958 - def savecalendar(self, dict, merge):
959 # ::TODO:: obey merge param 960 # get the list of available voice alarm files 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 # build the schedule file 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 # build the exceptions 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 # clear out any alarm voice files that may have been deleted 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
1008 - def _build_cal_repeat(self, event, exceptions):
1009 rep_val=Phone._repeat_values.get(event.repeat, None) 1010 if not rep_val: 1011 return None 1012 rep=bpcalendar.RepeatEntry(rep_val) 1013 if event.repeat==self.protocolclass.CAL_REP_MONFRI: 1014 rep.interval=rep.dow=0 1015 elif event.repeat!=self.protocolclass.CAL_REP_YEARLY: 1016 rep.interval=1 1017 rep.dow=0 1018 # do exceptions 1019 cal_ex=exceptions.get(event.pos, []) 1020 for e in cal_ex: 1021 try: 1022 rep.add_suppressed(*e) 1023 except ValueError: 1024 # illegal exception date 1025 pass 1026 except: 1027 if __debug__: 1028 raise 1029 return rep
1030
1031 - def _get_voice_id(self, event, entry):
1032 if event.hasvoice: 1033 entry.voice=event.voiceid
1034
1035 - def _build_cal_entry(self, event, exceptions, ringtone_index):
1036 try: 1037 # return a copy of bpcalendar object based on my values 1038 # general fields 1039 entry=bpcalendar.CalendarEntry() 1040 entry.start=event.start 1041 try: 1042 entry.end=event.end 1043 # some phones (e.g. lx5550) have unreliable end dates for "forever" events 1044 except ValueError: 1045 entry.end=self.protocolclass.CAL_REPEAT_DATE 1046 entry.desc_loc=event.description 1047 # check for allday event 1048 if entry.start[3:]==(0, 0) and entry.end[3:]==(23, 59): 1049 entry.allday=True 1050 # alarm 1051 if event.alarmtype: 1052 entry.alarm=event.alarmhours*60+event.alarmminutes 1053 # ringtone 1054 rt_idx=event.ringtone 1055 # hack to account for the VX4650 weird ringtone setup 1056 if rt_idx<50: 1057 # 1st part of builtin ringtones, need offset by 1 1058 rt_idx+=1 1059 entry.ringtone=ringtone_index.get(rt_idx, {'name': None} )['name'] 1060 # voice ID if applicable 1061 if self._cal_has_voice_id: 1062 self._get_voice_id(event, entry) 1063 # repeat info 1064 entry.repeat=self._build_cal_repeat(event, exceptions) 1065 return entry 1066 except ValueError: 1067 # illegal date/time 1068 pass 1069 except: 1070 # this entry is hosed! 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: # blank entry 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) # default alarm is off 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
1103 - def _set_repeat_event(self, event, entry, exceptions):
1104 rep_val=self.protocolclass.CAL_REP_NONE 1105 day_bitmap=0 1106 rep=entry.repeat 1107 if rep: 1108 rep_type=rep.repeat_type 1109 if rep_type==bpcalendar.RepeatEntry.yearly: 1110 rep_val=self.protocolclass.CAL_REP_YEARLY 1111 else: 1112 rep_interval=rep.interval 1113 rep_dow=rep.dow 1114 if rep_type==bpcalendar.RepeatEntry.daily: 1115 if rep_interval==0: 1116 rep_val=self.protocolclass.CAL_REP_MONFRI 1117 elif rep_interval==1: 1118 rep_val=self.protocolclass.CAL_REP_DAILY 1119 elif rep_type==bpcalendar.RepeatEntry.weekly: 1120 start_dow=1<<datetime.date(*event.start[:3]).isoweekday()%7 1121 if (rep_dow==0 or rep_dow==start_dow) and rep_interval==1: 1122 rep_val=self.protocolclass.CAL_REP_WEEKLY 1123 day_bitmap=self._phone_dow.get(start_dow, 0) 1124 elif rep_type==bpcalendar.RepeatEntry.monthly: 1125 if rep_dow==0: 1126 rep_val=self.protocolclass.CAL_REP_MONTHLY 1127 if rep_val!=self.protocolclass.CAL_REP_NONE: 1128 # build exception list 1129 if rep.suppressed: 1130 day_bitmap|=self.protocolclass.CAL_DOW_EXCEPTIONS 1131 for x in rep.suppressed: 1132 exceptions.setdefault(event.pos, []).append(x.get()[:3]) 1133 # this is a repeat event, set the end date appropriately 1134 if event.end[:3]==entry.no_end_date: 1135 event.end=self.protocolclass.CAL_REPEAT_DATE+event.end[3:] 1136 else: 1137 event.end=entry.end 1138 event.repeat=rep_val 1139 event.daybitmap=day_bitmap
1140
1141 - def _set_alarm(self, event, entry):
1142 # set alarm value based on entry's value, or its approximation 1143 keys=Phone._alarm_info.keys() 1144 keys.sort() 1145 keys.reverse() 1146 _alarm_val=entry.alarm 1147 _alarm_key=None 1148 for k in keys: 1149 if _alarm_val>=k: 1150 _alarm_key=k 1151 break 1152 event.alarmtype, event.alarmminutes, event.alarmhours=Phone._alarm_info.get( 1153 _alarm_key, self._default_alarm)
1154
1155 - def _set_voice_id(self, event, entry, voice_files):
1156 if entry.voice and \ 1157 voice_files.has_key(entry.voice-self.protocolclass.cal_voice_id_ofs): 1158 event.hasvoice=1 1159 event.voiceid=entry.voice 1160 del voice_files[entry.voice-self.protocolclass.cal_voice_id_ofs] 1161 else: 1162 event.hasvoice=0 1163 event.voiceid=self.protocolclass.CAL_NO_VOICE
1164
1165 - def _set_cal_event(self, event, entry, exceptions, ringtone_index, 1166 voice_files):
1167 # desc 1168 event.description=entry.desc_loc 1169 # start & end times 1170 if entry.allday: 1171 event.start=entry.start[:3]+(0,0) 1172 event.end=entry.start[:3]+(23,59) 1173 else: 1174 event.start=entry.start 1175 event.end=entry.start[:3]+entry.end[3:] 1176 # make sure the event lasts in 1 calendar day 1177 if event.end<event.start: 1178 event.end=event.start[:3]+(23,59) 1179 # alarm 1180 self._set_alarm(event, entry) 1181 # ringtone 1182 rt=0 # always default to the first bultin ringtone 1183 if entry.ringtone: 1184 for k,e in ringtone_index.items(): 1185 if e['name']==entry.ringtone: 1186 rt=k 1187 break 1188 if rt and rt<50: 1189 rt-=1 1190 event.ringtone=rt 1191 # voice ID 1192 if self._cal_has_voice_id: 1193 self._set_voice_id(event, entry, voice_files) 1194 # repeat 1195 self._set_repeat_event(event, entry, exceptions)
1196
1197 - def set_cal(self, sch_file, cal_dict, ringtone_index, voice_files):
1198 exceptions={} 1199 _pos=2 1200 _packet_size=None 1201 ## _today=datetime.date.today().timetuple()[:5] 1202 _entry_cnt=0 1203 for k, e in cal_dict.items(): 1204 ## # only send either repeat events or present&future single events 1205 ## if e.repeat or (e.start>=_today): 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 # Text Memo stuff-----------------------------------------------------------
1221 - def getmemo(self, result):
1222 # read the memo file 1223 try: 1224 buf=prototypes.buffer(self.getfilecontents( 1225 self.protocolclass.text_memo_file)) 1226 text_memo=self.protocolclass.textmemofile() 1227 text_memo.readfrombuffer(buf, logtitle="Read text memo") 1228 res={} 1229 for i in range(text_memo.itemcount): 1230 entry=memo.MemoEntry() 1231 entry.text=text_memo.items[i].text 1232 res[entry.id]=entry 1233 except com_brew.BrewNoSuchFileException: 1234 res={} 1235 result['memo']=res 1236 return result
1237
1238 - def savememo(self, result, merge):
1239 text_memo=self.protocolclass.textmemofile() 1240 memo_dict=result.get('memo', {}) 1241 keys=memo_dict.keys() 1242 keys.sort() 1243 text_memo.itemcount=len(keys) 1244 for k in keys: 1245 entry=self.protocolclass.textmemo() 1246 entry.text=memo_dict[k].text 1247 text_memo.items.append(entry) 1248 buf=prototypes.buffer() 1249 text_memo.writetobuffer(buf, logtitle="text memo "+self.protocolclass.text_memo_file) 1250 self.writefile(self.protocolclass.text_memo_file, buf.getvalue()) 1251 return result
1252 1253 1254 parentprofile=com_phone.Profile
1255 -class Profile(parentprofile):
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 # which usb ids correspond to us 1274 usbids_straight=( ( 0x1004, 0x6000, 2), )# VID=LG Electronics, PID=LG VX4400/VX6000 -internal USB diagnostics interface 1275 usbids_usbtoserial=( 1276 ( 0x067b, 0x2303, None), # VID=Prolific, PID=USB to serial 1277 ( 0x0403, 0x6001, None), # VID=FTDI, PID=USB to serial 1278 ( 0x0731, 0x2003, None), # VID=Susteen, PID=Universal USB to serial 1279 ) 1280 usbids=usbids_straight+usbids_usbtoserial 1281 1282 # which device classes we are. not we are not modem! 1283 deviceclasses=("serial",) 1284 1285 # nb we don't allow save to camera so it isn't listed here 1286 imageorigins={} 1287 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images"))
1288 - def GetImageOrigins(self):
1289 return self.imageorigins
1290 1291 # our targets are the same for all origins 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
1300 - def GetTargetsForImageOrigin(self, origin):
1301 return self.imagetargets
1302
1303 - def __init__(self):
1304 parentprofile.__init__(self)
1305 1306
1307 - def _getgroup(self, name, groups):
1308 for key in groups: 1309 if groups[key]['name']==name: 1310 return key,groups[key] 1311 return None,None
1312 1313
1314 - def normalisegroups(self, helper, data):
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: # ignore key 0 which is 'No Group' 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 # alpha sort 1328 groups.sort() 1329 1330 # newgroups 1331 newgroups={} 1332 1333 # put in No group 1334 newgroups[0]={'name': 'No Group', 'icon': 0} 1335 1336 # populate 1337 for name in groups: 1338 # existing entries remain unchanged 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 # new entries get whatever numbers are free 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 # yay, done 1353 if data['groups']!=newgroups: 1354 data['groups']=newgroups 1355 data['rebootphone']=True
1356
1357 - def convertphonebooktophone(self, helper, data):
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={} # entry out 1372 entry=data['phonebook'][pbentry] # entry in 1373 try: 1374 # serials 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 # name 1386 e['name']=helper.getfullname(entry.get('names', []),1,1,22)[0] 1387 1388 # categories/groups 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 # sorry no space for this category 1398 e['group']=0 1399 1400 # email addresses 1401 emails=helper.getemails(entry.get('emails', []) ,0,self.protocolclass.NUMEMAILS,48) 1402 e['emails']=helper.filllist(emails, self.protocolclass.NUMEMAILS, "") 1403 1404 # url 1405 e['url']=helper.makeone(helper.geturls(entry.get('urls', []), 0,1,48), "") 1406 1407 # memo (-1 is to leave space for null terminator - not all software puts it in, but we do) 1408 e['memo']=helper.makeone(helper.getmemos(entry.get('memos', []), 0, 1, self.protocolclass.MEMOLENGTH-1), "") 1409 1410 # phone numbers 1411 # there must be at least one email address or phonenumber 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 # deal with type 1420 b4=len(e['numbertypes']) 1421 type=num['type'] 1422 for i,t in enumerate(self.protocolclass.numbertypetab): 1423 if type==t: 1424 # some voodoo to ensure the second home becomes home2 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': # conveniently last entry 1431 e['numbertypes'].append(i) 1432 break 1433 if len(e['numbertypes'])==b4: 1434 # we couldn't find a type for the number 1435 helper.add_error_message('Number %s (%s/%s) not supported and ignored.'% 1436 (num['number'], e['name'], num['type'])) 1437 continue 1438 # deal with number 1439 number=self.phonize(num['number']) 1440 if len(number)==0: 1441 # no actual digits in the number 1442 continue 1443 if len(number)>48: # get this number from somewhere sensible 1444 # ::TODO:: number is too long and we have to either truncate it or ignore it? 1445 number=number[:48] # truncate for moment 1446 e['numbers'].append(number) 1447 # deal with speed dial 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 # we couldn't find any numbers 1455 # for this entry, so skip it, entries with no numbers cause error 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 # ringtones, wallpaper 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 # flags 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), # all phonebook reading 1481 ('calendar', 'read', None), # all calendar reading 1482 ('wallpaper', 'read', None), # all wallpaper reading 1483 ('ringtone', 'read', None), # all ringtone reading 1484 ('call_history', 'read', None),# all call history list reading 1485 ('sms', 'read', None), # all SMS list reading 1486 ('memo', 'read', None), # all memo list reading 1487 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 1488 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 1489 ('wallpaper', 'write', 'MERGE'), # merge and overwrite wallpaper 1490 ('wallpaper', 'write', 'OVERWRITE'), 1491 ('ringtone', 'write', 'MERGE'), # merge and overwrite ringtone 1492 ('ringtone', 'write', 'OVERWRITE'), 1493 ('sms', 'write', 'OVERWRITE'), # all SMS list writing 1494 ('memo', 'write', 'OVERWRITE'), # all memo list writing 1495 ) 1496
1497 - def QueryAudio(self, origin, currentextension, afi):
1498 # we don't modify any of these 1499 if afi.format in ("MIDI", "QCP", "PMD"): 1500 return currentextension, afi 1501 # examine mp3 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 # convert it 1506 return ("mp3", fileinfo.AudioFileInfo(afi, **{'format': 'MP3', 'channels': 1, 'bitrate': 32, 'samplerate': 22050}))
1507