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

Source Code for Module phones.com_lgpm225

   1  ### BITPIM 
   2  ### 
   3  ### Copyright (C) 2006 Simon Capper <skyjunky@sbcglobal.net> 
   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   
   9  """Communicate with the LG PM225 (Sprint) cell phone""" 
  10   
  11  # standard modules 
  12  import re 
  13  import time 
  14  import cStringIO 
  15  import sha 
  16   
  17  # my modules 
  18  import p_lgpm225 
  19  import p_brew 
  20  import common 
  21  import commport 
  22  import com_brew 
  23  import com_phone 
  24  import com_lg 
  25  import com_lgvx4400 
  26  import helpids 
  27  import prototypes 
  28  import call_history 
  29  import sms 
  30  import fileinfo 
  31  import memo 
  32   
  33   
34 -class Phone(com_lgvx4400.Phone):
35 "Talk to the LG PM225 cell phone" 36 37 desc="LG PM225" 38 helpid=helpids.ID_PHONE_LGPM225 39 protocolclass=p_lgpm225 40 serialsname='lgpm225' 41 42 # read only locations, for regular ringers/wallpaper this phone stores 43 # the information in a different location 44 imagelocations=( 45 # offset, index file, files location, type, maximumentries 46 (0x600, "setas/dcamIndex.map", "Dcam/Wallet", "camera", 50, 6), 47 ) 48 49 ringtonelocations=( 50 # offset, index file, files location, type, maximumentries 51 (0x1100, "setas/voicememoRingerIndex.map", "VoiceDB/All/Memos", "voice_memo", 50, 11), 52 ) 53 54 builtinimages=('Spectrum', 'Speed', 'Drops', 'Country Road', 55 'Houses','Tulip', 'Flower', 'Lines', 'Network', 'Abstract') 56 57 builtinringtones=( 'Tone 1', 'Tone 2', 'Tone 3', 'Tone 4', 'Tone 5', 'Tone 6', 58 'Alert 1', 'Alert 2', 'Alert 3', 'Alert 4', 'Alert 5', 'Alert 6', 59 'Moonlight', 'Bumble Bee', 'Latin', 'Baroque', 60 'Lovable Baby', 'LG Sound', 'Love Song', 'Badinerie', 61 'Follow Me', 'Head, Shoulders, Knees & Toes', 'Lake Trance', 'Beethoven', 62 'Let''s Play', 'Piano Concerto No.1', 'Pleasure', 63 'Leichte Kavallerie', 'Up & Down Melody', 'Vivaldi - Winter') 64
65 - def __init__(self, logtarget, commport):
66 "Calls all the constructors and sets initial modes" 67 com_phone.Phone.__init__(self, logtarget, commport) 68 com_brew.BrewProtocol.__init__(self) 69 com_lg.LGPhonebook.__init__(self) 70 self.log("Attempting to contact phone") 71 self._cal_has_voice_id=hasattr(self.protocolclass, 'cal_has_voice_id') \ 72 and self.protocolclass.cal_has_voice_id 73 self.mode=self.MODENONE
74 75 #----- PhoneBook ----------------------------------------------------------------------- 76
77 - def getfundamentals(self, results):
78 """Gets information fundamental to interoperating with the phone and UI. 79 80 Currently this is: 81 82 - 'uniqueserial' a unique serial number representing the phone 83 - 'groups' the phonebook groups 84 85 This method is called before we read the phonebook data or before we 86 write phonebook data. 87 """ 88 89 # use a hash of ESN and other stuff (being paranoid) 90 self.log("Retrieving fundamental phone information") 91 self.log("Phone serial number") 92 results['uniqueserial']=sha.new(self.getfilecontents("nvm/$SYS.ESN")).hexdigest() 93 self.log(results) 94 95 # now read groups 96 self.log("Reading group information") 97 buf=prototypes.buffer(self.getfilecontents("pim/pbookgroup.dat")) 98 g=self.protocolclass.pbgroups() 99 g.readfrombuffer(buf, logtitle="Groups read") 100 groups={} 101 for i in range(len(g.groups)): 102 if len(g.groups[i].name): # sometimes have zero length names 103 groups[g.groups[i].group_id]={'name': g.groups[i].name } 104 results['groups']=groups 105 self.getwallpaperindices(results) 106 self.getringtoneindices(results) 107 self.log("Fundamentals retrieved") 108 return results
109
110 - def syncbegin(self):
111 self.mode = self.MODEPHONEBOOK 112 self.sendpbcommand(self.protocolclass.pbstartsyncrequest(), self.protocolclass.pbstartsyncresponse)
113
114 - def syncend(self):
115 req=self.protocolclass.pbendsyncrequest() 116 self.sendpbcommand(req, self.protocolclass.pbendsyncresponse)
117
118 - def getphonebook(self,result):
119 """Reads the phonebook data. The L{getfundamentals} information will 120 already be in result.""" 121 122 pbook={} 123 # Bug in the phone. if you repeatedly read the phone book it starts 124 # returning a random number as the number of entries. We get around 125 # this by switching into brew mode which clears that. 126 self.mode=self.MODENONE 127 self.setmode(self.MODEBREW) 128 129 self.log("Reading number of phonebook entries") 130 self.mode = self.MODEBREW 131 res=self.sendpbcommand(self.protocolclass.pbinforequest(), self.protocolclass.pbinforesponse) 132 numentries = res.numentries 133 self.log("There are %d entries" % (numentries,)) 134 for i in range(0, numentries): 135 ### Read current entry 136 req=self.protocolclass.pbreadentryrequest() 137 res=self.sendpbcommand(req, self.protocolclass.pbreadentryresponse) 138 self.log("Read entry "+`i`+" - "+res.entry.name) 139 entry=self.extractphonebookentry(res.entry, result) 140 pbook[res.entry.entrynumber]=entry 141 self.progress(i, numentries, res.entry.name) 142 #### Advance to next entry 143 req=self.protocolclass.pbnextentryrequest() 144 self.sendpbcommand(req, self.protocolclass.pbnextentryresponse) 145 146 pbook=self.get_phonebook_media(pbook, result) 147 148 self.progress(numentries, numentries, "Phone book read completed") 149 self.log("Phone book read completed") 150 151 result['phonebook']=pbook 152 153 cats=[] 154 for i in result['groups']: 155 if result['groups'][i]['name']!='No Group': 156 cats.append(result['groups'][i]['name']) 157 result['categories']=cats 158 return pbook
159
160 - def get_phonebook_media(self, pbook, fundamentals):
161 """This phone does not provide ringtone and image info for contacts in 162 the regular packets so we have to read the filesystem directly """ 163 buf=prototypes.buffer(self.getfilecontents(self.protocolclass.phonebook_media)) 164 g=self.protocolclass.pb_contact_media_file() 165 g.readfrombuffer(buf, logtitle="PB Media read") 166 for i in range(len(g.contacts)): 167 # adjust wallpaper for stock 168 if (g.contacts[i].wallpaper & 0xFF00)==0x100: 169 g.contacts[i].wallpaper-=0x100 170 if __debug__: 171 tone="None" 172 paper="None" 173 try: 174 tone=fundamentals['ringtone-index'][g.contacts[i].ringer]['name'] 175 except: 176 pass 177 try: 178 paper=fundamentals['wallpaper-index'][g.contacts[i].wallpaper]['name'] 179 except: 180 pass 181 self.log("media entry "+g.contacts[i].name+" ringer "+`tone`+" ("+`g.contacts[i].ringer`+")") 182 self.log("media entry "+g.contacts[i].name+" wallpaper "+`paper`+" ("+`g.contacts[i].wallpaper`+")") 183 if g.contacts[i].index in pbook: 184 self.log("Index "+`g.contacts[i].index`+" found") 185 if g.contacts[i].ringer: 186 try: 187 tone=fundamentals['ringtone-index'][g.contacts[i].ringer]['name'] 188 pbook[g.contacts[i].index]['ringtones']=[{'ringtone': tone, 'use': 'call'}] 189 except: 190 self.log("Exception in ringtone assignment") 191 if g.contacts[i].wallpaper: 192 try: 193 paper=fundamentals['wallpaper-index'][g.contacts[i].wallpaper]['name'] 194 pbook[g.contacts[i].index]['wallpapers']=[{'wallpaper': paper, 'use': 'call'}] 195 except: 196 self.log("Exception in wallpaper assignment") 197 else: 198 self.log("Index "+`g.contacts[i].index`+" not found") 199 return pbook
200
201 - def extractphonebookentry(self, entry, fundamentals):
202 """Return a phonebook entry in BitPim format. This is called from getphonebook.""" 203 res={} 204 # serials 205 res['serials']=[ {'sourcetype': self.serialsname, 'serial1': entry.serial1, 206 'sourceuniqueid': fundamentals['uniqueserial']} ] 207 # only one name 208 res['names']=[ {'full': entry.name} ] 209 # only one category 210 cat=fundamentals['groups'].get(entry.group, {'name': "No Group"})['name'] 211 if cat!="No Group": 212 res['categories']=[ {'category': cat} ] 213 # emails 214 res['emails']=[] 215 for i in entry.emails: 216 if len(i.email): 217 res['emails'].append( {'email': i.email} ) 218 if not len(res['emails']): del res['emails'] # it was empty 219 # urls 220 if 'url' in entry.getfields() and len(entry.url): 221 res['urls']=[ {'url': entry.url} ] 222 # private 223 if 'secret' in entry.getfields() and entry.secret: 224 # we only supply secret if it is true 225 res['flags']=[ {'secret': entry.secret } ] 226 # memos 227 if 'memo' in entry.getfields() and len(entry.memo): 228 res['memos']=[ {'memo': entry.memo } ] 229 230 # numbers 231 res['numbers']=[] 232 for i in range(self.protocolclass.NUMPHONENUMBERS): 233 num=entry.numbers[i].number 234 type=entry.numbertypes[i].numbertype 235 speeddial=entry.numberspeeds[i].numberspeed 236 if len(num): 237 t=self.protocolclass.numbertypetab[type] 238 if speeddial != 0xFF: # invalid entry 239 res['numbers'].append({'number': num, 'type': t, 'speeddial': speeddial}) 240 else: 241 res['numbers'].append({'number': num, 'type': t}) 242 return res
243
244 - def makeentry(self, counter, entry, data):
245 """Creates pbentry object 246 247 @param counter: The new entry number 248 @param entry: The phonebook object (as returned from convertphonebooktophone) that we 249 are using as the source 250 @param data: The main dictionary, which we use to get access to media indices amongst 251 other things 252 """ 253 e=self.protocolclass.pbentry() 254 255 e.entrynumber=counter 256 257 for k in entry: 258 # special treatment for lists 259 if k in ('emails', 'numbers', 'numbertypes', 'numberspeeds'): 260 l=getattr(e,k) 261 for item in entry[k]: 262 l.append(item) 263 elif k=='ringtone': 264 try: 265 e.ringtone=self._findmediainindex(data['ringtone-index'], entry['ringtone'], entry['name'], 'ringtone') 266 except: 267 pass 268 elif k=='msgringtone': 269 pass # not supported by phone 270 #e.msgringtone=self._findmediainindex(data['ringtone-index'], entry['msgringtone'], entry['name'], 'message ringtone') 271 elif k=='wallpaper': 272 try: 273 e.wallpaper=self._findmediainindex(data['wallpaper-index'], entry['wallpaper'], entry['name'], 'wallpaper') 274 # adjust for stock wallpaper 275 if e.wallpaper < 0x100: 276 e.wallpaper+=0x100 277 except: 278 pass 279 elif k in e.getfields(): 280 # everything else we just set 281 setattr(e,k,entry[k]) 282 283 return e
284
285 - def save_phonebook_media(self, pb_entries):
286 """This phone does not provide ringtone and image info for contacts in 287 the regular packets so we have to write the filesystem directly """ 288 # we read the file, modify it and write it back 289 buf=prototypes.buffer(self.getfilecontents(self.protocolclass.phonebook_media)) 290 g=self.protocolclass.pb_contact_media_file() 291 g.readfrombuffer(buf, logtitle="PB Media read") 292 rc=False 293 for i in range(len(g.contacts)): 294 if g.contacts[i].index in pb_entries: 295 if g.contacts[i].ringer!=pb_entries[g.contacts[i].index].ringtone: 296 g.contacts[i].ringer=pb_entries[g.contacts[i].index].ringtone 297 rc=True 298 if g.contacts[i].wallpaper!=pb_entries[g.contacts[i].index].wallpaper: 299 g.contacts[i].wallpaper=pb_entries[g.contacts[i].index].wallpaper 300 rc=True 301 if rc: 302 buf=prototypes.buffer() 303 g.writetobuffer(buf, logtitle="Writing PB media file") 304 self.writefile(self.protocolclass.phonebook_media, buf.getvalue()) 305 else: 306 self.log("PB media file up to date, no write required") 307 return rc
308
309 - def savegroups(self, data):
310 groups=data['groups'] 311 keys=groups.keys() 312 keys.sort() 313 g=self.protocolclass.pbgroups() 314 for k in keys: 315 e=self.protocolclass.pbgroup() 316 e.group_id=k 317 e.rectype = 0x30 318 e.name=groups[k]['name'] 319 g.groups.append(e) 320 buffer=prototypes.buffer() 321 g.writetobuffer(buffer, logtitle="New group file") 322 self.writefile("pim/pbookgroup.dat", buffer.getvalue())
323
324 - def savephonebook(self, data):
325 "Saves out the phonebook" 326 self.savegroups(data) 327 328 # if __debug__: 329 # # for testing without a real phone, the packets are stored in a 330 # # file and can be examined 331 # counter=0 332 # pb_entries={} 333 # for i in data['phonebook'].keys(): 334 # ii=data['phonebook'][i] 335 # entry=self.makeentry(counter, ii, data) 336 # counter+=1 337 # req=self.protocolclass.pbupdateentryrequest() 338 # req.entry=entry 339 # req.header.sequence=counter 340 # buffer=prototypes.buffer() 341 # req.writetobuffer(buffer, logtitle="New contents for pim/pb"+`counter`+".dat") 342 # self.writefile("pim/pb"+`counter`+".dat", buffer.getvalue()) 343 # pb_entries[counter]=entry 344 # self.save_phonebook_media(pb_entries) 345 346 # set up progress bar on main window 347 progressmax=len(data['phonebook'].keys()) 348 349 # To write the phone book, we scan through all existing entries 350 # and record their record number and serials. 351 # We then delete any entries that aren't in data 352 # We then write out our records, using overwrite or append 353 # commands as necessary 354 serialupdates=[] 355 pb_entries={} 356 existingpbook={} # keep track of the phonebook that is on the phone 357 self.mode=self.MODENONE 358 self.setmode(self.MODEBREW) # see note in getphonebook() for why this is necessary 359 self.setmode(self.MODEPHONEBOOK) 360 # similar loop to reading 361 req=self.protocolclass.pbinforequest() 362 res=self.sendpbcommand(req, self.protocolclass.pbinforesponse) 363 numexistingentries=res.numentries 364 self.log("There are %d existing entries" % (numexistingentries,)) 365 progressmax+=numexistingentries 366 loop=xrange(0, numexistingentries) 367 progresscur=0 368 369 # reset cursor 370 self.sendpbcommand(self.protocolclass.pbinitrequest(), self.protocolclass.pbinitresponse) 371 for i in loop: 372 ### Read current entry 373 req=self.protocolclass.pbreadentryrequest() 374 res=self.sendpbcommand(req, self.protocolclass.pbreadentryresponse) 375 376 entry={ 'number': res.entry.entrynumber, 'serial1': res.entry.serial1, 'name': res.entry.name} 377 378 self.log("Reading entry "+`i`+" - "+str(entry['serial1'])+" - "+entry['name']) 379 existingpbook[i]=entry 380 self.progress(progresscur, progressmax, "existing "+entry['name']) 381 #### Advance to next entry 382 req=self.protocolclass.pbnextentryrequest() 383 res=self.sendpbcommand(req, self.protocolclass.pbnextentryresponse) 384 progresscur+=1 385 # we have now looped around back to begining 386 387 # Find entries that have been deleted 388 pbook=data['phonebook'] 389 dellist=[] 390 for i in loop: 391 ii=existingpbook[i] 392 serial=ii['serial1'] 393 item=self._findserial(serial, pbook) 394 if item is None: 395 dellist.append(i) 396 397 progressmax+=len(dellist) # more work to do 398 399 # Delete those entries 400 for i in dellist: 401 progresscur+=1 402 numexistingentries-=1 # keep count right 403 ii=existingpbook[i] 404 self.log("Deleting entry "+`i`+" - "+str(ii['serial1'])+" - "+ii['name']) 405 req=self.protocolclass.pbdeleteentryrequest() 406 req.serial1=ii['serial1'] 407 req.serial2=ii['serial1'] 408 req.entrynumber=ii['number'] 409 self.sendpbcommand(req, self.protocolclass.pbdeleteentryresponse) 410 self.progress(progresscur, progressmax, "Deleting "+ii['name']) 411 # also remove them from existingpbook 412 del existingpbook[i] 413 414 # Now rewrite out existing entries 415 self.log("Rewrite existing entries") 416 keys=existingpbook.keys() 417 existingserials=[] 418 keys.sort() # do in same order as existingpbook 419 for i in keys: 420 progresscur+=1 421 ii=pbook[self._findserial(existingpbook[i]['serial1'], pbook)] 422 self.log("Rewriting entry "+`i`+" - "+ii['name']) 423 self.progress(progresscur, progressmax, "Rewriting "+ii['name']) 424 entry=self.makeentry(existingpbook[i]['serial1'], ii, data) 425 existingserials.append(existingpbook[i]['serial1']) 426 req=self.protocolclass.pbupdateentryrequest() 427 req.entry=entry 428 res=self.sendpbcommand(req, self.protocolclass.pbupdateentryresponse) 429 serialupdates.append( ( ii["bitpimserial"], 430 {'sourcetype': self.serialsname, 431 'serial1': res.serial1, 432 'sourceuniqueid': data['uniqueserial']})) 433 assert ii['serial1']==res.serial1 # serial should stay the same 434 pb_entries[res.serial1]=entry 435 436 # Finally write out new entries 437 counter=0 438 keys=pbook.keys() 439 self.log("Write new entries") 440 keys.sort() 441 for i in keys: 442 ii=pbook[i] 443 if ii['serial1'] in existingserials: 444 continue # already wrote this one out 445 # find an unused serial number 446 while True: 447 if counter in existingserials: 448 counter+=1 449 else: 450 break 451 progresscur+=1 452 entry=self.makeentry(counter, ii, data) 453 self.log("Appending entry "+ii['name']) 454 self.progress(progresscur, progressmax, "Writing "+ii['name']) 455 req=self.protocolclass.pbappendentryrequest() 456 req.entry=entry 457 res=self.sendpbcommand(req, self.protocolclass.pbappendentryresponse) 458 serialupdates.append( ( ii["bitpimserial"], 459 {'sourcetype': self.serialsname, 460 'serial1': res.newserial, 461 'sourceuniqueid': data['uniqueserial']})) 462 pb_entries[res.newserial]=entry 463 counter+=1 464 # update the media file 465 data['serialupdates']=serialupdates 466 467 changed=self.save_phonebook_media(pb_entries) 468 if changed: 469 data["rebootphone"]=True 470 return data
471 472 #----- SMS --------------------------------------------------------------------------- 473
474 - def _readsms(self):
475 res={} 476 # go through the sms directory looking for messages 477 for item in self.listfiles("sms").values(): 478 folder=None 479 for f,pat in self.protocolclass.SMS_PATTERNS.items(): 480 if pat.match(item['name']): 481 folder=f 482 break 483 if folder: 484 buf=prototypes.buffer(self.getfilecontents(item['name'], True)) 485 self.logdata("SMS message file " +item['name'], buf.getdata()) 486 if folder=='Inbox': 487 sf=self.protocolclass.sms_in() 488 sf.readfrombuffer(buf, logtitle="SMS inbox item") 489 entry=self._getinboxmessage(sf) 490 res[entry.id]=entry 491 elif folder=='Sent': 492 sf=self.protocolclass.sms_out() 493 sf.readfrombuffer(buf, logtitle="SMS sent item") 494 entry=self._getoutboxmessage(sf) 495 res[entry.id]=entry 496 return res
497 498
499 - def _setquicktext(self, result):
500 sf=self.protocolclass.sms_canned_file() 501 quicktext=result.get('canned_msg', []) 502 count=0 503 for entry in quicktext: 504 if count < self.protocolclass.SMS_CANNED_MAX_ITEMS: 505 msg_entry=self.protocolclass.sms_quick_text() 506 msg_entry.msg=entry['text'][:self.protocolclass.SMS_CANNED_MAX_LENGTH-1] 507 sf.msgs.append(msg_entry) 508 count+=1 509 else: 510 break 511 if count!=0: 512 # don't create the file if there are no entries 513 sf.num_active=count 514 buf=prototypes.buffer() 515 sf.writetobuffer(buf, logtitle="Writing quicktext") 516 self.writefile(self.protocolclass.SMS_CANNED_FILENAME, buf.getvalue()) 517 return
518
519 - def _getquicktext(self):
520 quicks=[] 521 try: 522 buf=prototypes.buffer(self.getfilecontents(self.protocolclass.SMS_CANNED_FILENAME)) 523 sf=self.protocolclass.sms_canned_file() 524 sf.readfrombuffer(buf, logtitle="SMS quicktext file sms/canned_msg.dat") 525 for rec in sf.msgs: 526 if rec.msg!="": 527 quicks.append({ 'text': rec.msg, 'type': sms.CannedMsgEntry.user_type }) 528 except com_brew.BrewNoSuchFileException: 529 pass # do nothing if file doesn't exist 530 return quicks
531
532 - def _getinboxmessage(self, sf):
533 entry=sms.SMSEntry() 534 entry.folder=entry.Folder_Inbox 535 entry.datetime="%d%02d%02dT%02d%02d%02d" % (sf.GPStime) 536 entry._from=self._getsender(sf.sender, sf.sender_length) 537 entry.subject=sf.subject 538 entry.locked=sf.locked 539 # if sf.priority==0: 540 # entry.priority=sms.SMSEntry.Priority_Normal 541 # else: 542 # entry.priority=sms.SMSEntry.Priority_High 543 entry.read=sf.read 544 entry.text=sf.msg 545 entry.callback=sf.callback 546 return entry
547
548 - def _getoutboxmessage(self, sf):
549 entry=sms.SMSEntry() 550 if not sf.saved: 551 entry.folder=entry.Folder_Sent 552 else: 553 entry.folder=entry.Folder_Saved 554 entry.datetime="%d%02d%02dT%02d%02d00" % ((sf.timesent)) 555 # add all the recipients 556 for r in sf.recipients: 557 if r.number: 558 confirmed=(r.status==2) 559 confirmed_date=None 560 if confirmed: 561 confirmed_date="%d%02d%02dT%02d%02d00" % r.time 562 entry.add_recipient(r.number, confirmed, confirmed_date) 563 entry.subject=sf.msg[:28] 564 entry.text=sf.msg 565 # if sf.priority==0: 566 # entry.priority=sms.SMSEntry.Priority_Normal 567 # else: 568 # entry.priority=sms.SMSEntry.Priority_High 569 entry.locked=sf.locked 570 entry.callback=sf.callback 571 return entry
572 573 574 #----- Media ----------------------------------------------------------------------- 575 576 # this phone can take MP3 files, but you have to use the vnd.qcelp MIME type 577 # this makes renaming the files (from the ##.dat format) back to a real 578 # name difficult, we assume all vnd.qcelp files are mp3 files as this is the 579 # most common format 580 581 __mimetype={ 'mid': 'audio/midi', 'qcp': 'audio/vnd.qcelp', 'jar': 'application/java-archive', 582 'jpg': 'image/jpg', 'jpeg': 'image/jpeg', 'gif': 'image/gif', 583 'bmp': 'image/bmp', 'png': 'image/png', 'mp3': 'audio/vnd.qcelp'} 584 585 __reverse_mimetype={ 'audio/midi': 'mid', 'audio/vnd.qcelp': 'mp3', 'application/java-archive': 'jar', 586 'image/jpg': 'jpg', 'image/jpeg': 'jpeg', 'image/gif': 'gif', 587 'image/bmp': 'bmp', 'image/png': 'png', 'audio/mp3': 'mp3'} 588 589 __app_extensions={ 'jar':'' } 590 591
592 - def getwallpaperindices(self, results):
593 return self.getmediaindex(self.builtinimages, self.imagelocations, results, 'wallpaper-index')
594
595 - def getringtoneindices(self, results):
596 return self.getmediaindex(self.builtinringtones, self.ringtonelocations, results, 'ringtone-index')
597
598 - def getwallpapers(self, result):
599 return self.getmedia(self.imagelocations, result, 'wallpapers')
600
601 - def getringtones(self, result):
602 return self.getmedia(self.ringtonelocations, result, 'ringtone')
603
604 - def savewallpapers(self, results, merge):
605 return self.savemedia('wallpapers', 'wallpaper-index', results, merge, self.getwallpaperindices)
606
607 - def saveringtones(self, results, merge):
608 return self.savemedia('ringtone', 'ringtone-index', results, merge, self.getringtoneindices)
609
610 - def getindex(self, indexfile):
611 "Read an index file" 612 index={} 613 try: 614 buf=prototypes.buffer(self.getfilecontents(indexfile)) 615 except com_brew.BrewNoSuchFileException: 616 # file may not exist 617 return index 618 g=self.protocolclass.indexfile() 619 g.readfrombuffer(buf, logtitle="Read indexfile "+indexfile) 620 for i in g.items: 621 if i.index!=0xffff and len(i.name): 622 index[i.index]=i.name 623 return index
624
625 - def get_content_file(self, key):
626 index={} 627 if key=='ringtone' or key=='ringtone-index': 628 type='Ringers' 629 index_const=self.protocolclass.ringerconst*0x100 630 indexfile=self.getindex(self.protocolclass.ringerindex) 631 else: 632 type='Screen Savers' 633 index_const=self.protocolclass.imageconst*0x100 634 indexfile=self.getindex(self.protocolclass.imageindex) 635 try: 636 buf=prototypes.buffer(self.getfilecontents(self.protocolclass.content_file_name)) 637 g=self.protocolclass.content_file() 638 g.readfrombuffer(buf, logtitle="Content file "+self.protocolclass.content_file_name) 639 for i in g.items: 640 if i.type=='!C' and i.content_type==type: 641 try: 642 # construct a user friendly filename 643 ext=self.__reverse_mimetype[i.mime_type] 644 # find the "file" in the index file and get it's index 645 # if the index was created by bitpim it will be the same 646 # as the unfriendly filename, but this is not guarenteed 647 found=False 648 for j in indexfile.keys(): 649 # convert to int to strip leading zero 650 try: 651 if int(common.stripext(indexfile[j]))==int(i.index1): 652 index[j + index_const]=i.name1+'.'+ext 653 found=True 654 break; 655 except: 656 pass 657 if not found: 658 self.log("Unable to find index entry for "+i.name1+". Index : "+`i.index1`) 659 except: 660 pass 661 except com_brew.BrewNoSuchFileException: 662 pass 663 return index, indexfile, index_const
664
665 - def getmedia(self, maps, result, key):
666 """Returns the contents of media as a dict where the key is a name as returned 667 by getindex, and the value is the contents of the media""" 668 media={} 669 # first read the maps 670 type=None 671 for offset,indexfile,location,type,maxentries,const in maps: 672 index=self.getindex(indexfile) 673 for i in index: 674 try: 675 media[index[i]]=self.getfilecontents(location+"/"+index[i], True) 676 except (com_brew.BrewNoSuchFileException,com_brew.BrewBadPathnameException,com_brew.BrewNameTooLongException): 677 self.log("It was in the index, but not on the filesystem") 678 679 # secondly read the content file 680 index, indexfile, index_const=self.get_content_file(key) 681 for i in index: 682 try: 683 buf=prototypes.buffer(self.getfilecontents(self.protocolclass.media_directory+'/'+indexfile[i-index_const], True)) 684 _fname=index[i] 685 # This would be nice but this will read the entire file which will slow the media retrieval down 686 # ext=common.getext(_fname) 687 # if ext=='mp3': 688 # # see if it is really an mp3, we will need to rename it 689 # try: 690 # qcp_header=self.protocolclass.qcp_media_header() 691 # qcp_header.readfrombuffer(buf, logtitle="qcp header") 692 # except: # exception will be thrown if header does not match 693 # _fname=common.stripext(index[i])+'.'+'qcp' 694 media[_fname]=buf.getdata() 695 except (com_brew.BrewNoSuchFileException,com_brew.BrewBadPathnameException,com_brew.BrewNameTooLongException): 696 self.log("It was in the index, but not on the filesystem") 697 self.log("Contents not in the filesystem") 698 699 result[key]=media 700 return result
701
702 - def getmediaindex(self, builtins, maps, results, key):
703 """Gets the media (wallpaper/ringtone) index 704 705 @param builtins: the builtin list on the phone 706 @param results: places results in this dict 707 @param maps: the list of index files and locations 708 @param key: key to place results in 709 """ 710 711 self.log("Reading "+key) 712 media={} 713 714 # builtins 715 c=1 716 for name in builtins: 717 media[c]={'name': name, 'origin': 'builtin' } 718 c+=1 719 720 # the maps 721 type=None 722 for offset,indexfile,location,type,maxentries,const in maps: 723 index=self.getindex(indexfile) 724 for i in index: 725 media[i+offset]={'name': index[i], 'origin': type} 726 727 # secondly read the content file 728 if key=='ringtone-index': 729 type='ringers' 730 else: 731 type='images' 732 index,_,_=self.get_content_file(key) 733 for i in index: 734 media[i]={'name': index[i], 'origin': type} 735 results[key]=media 736 return media
737
738 - def savemedia(self, mediakey, mediaindexkey, results, merge, reindexfunction):
739 """Actually saves out the media 740 741 @param mediakey: key of the media (eg 'wallpapers' or 'ringtones') 742 @param mediaindexkey: index key (eg 'wallpaper-index') 743 @param maps: list index files and locations 744 @param results: results dict 745 @param merge: are we merging or overwriting what is there? 746 @param reindexfunction: the media is re-indexed at the end. this function is called to do it 747 """ 748 content_changed=False 749 media=results[mediakey].copy() 750 751 752 #figure out if the origin we are interested in 753 if mediaindexkey=='ringtone-index': 754 type='ringers' 755 content_type="Ringers" 756 indexfile=self.protocolclass.ringerindex 757 index_const=self.protocolclass.ringerconst 758 max_media_entries=self.protocolclass.max_ringers 759 # remove voice_memos 760 for i in media.keys(): 761 try: 762 if media[i]['origin']=='voice_memo': 763 del media[i] 764 except: 765 pass 766 else: 767 type='images' 768 content_type="Screen Savers" 769 indexfile=self.protocolclass.imageindex 770 index_const=self.protocolclass.imageconst 771 max_media_entries=self.protocolclass.max_images 772 # remove camera images 773 for i in media.keys(): 774 try: 775 if media[i]['origin']=='camera': 776 del media[i] 777 except: 778 pass 779 780 #read content file off the phone 781 content={} 782 try: 783 buf=prototypes.buffer(self.getfilecontents(self.protocolclass.content_file_name)) 784 g=self.protocolclass.content_file() 785 g.readfrombuffer(buf, logtitle="Read content file") 786 for i in g.items: 787 # type !C always appears first 788 if i.type=='!C': 789 content[int(i.index1)]= {'C': i} 790 elif i.type=='!E': 791 content[int(i.index2)]['E']=i 792 except (com_brew.BrewNoSuchFileException,com_brew.BrewBadPathnameException,com_brew.BrewNameTooLongException): 793 pass 794 # get a list of files in the media directory so we can figure out what to delete 795 # and what needs to be copied onto the phone 796 dirlisting=self.getfilesystem(self.protocolclass.media_directory) 797 # strip path from directory listing 798 for i in dirlisting.keys(): 799 dirlisting[i[len(self.protocolclass.media_directory)+1:]]=dirlisting[i] 800 del dirlisting[i] 801 802 # build up list of existing media items into init, remove missing items from content list 803 init={} 804 for k in content.keys(): 805 if content[k]['C'].content_type==content_type: 806 index=k 807 name=content[k]['C'].name1 808 size=int(content[k]['C'].size) 809 data=None 810 # find the media content file, check that the size matches 811 for w in media: 812 if common.stripext(media[w]['name'])==name and media[w]['data']!=None: 813 size_fix=((3024+len(media[w]['data']))/1008)*1008 814 if size_fix==size: 815 data=media[w]['data'] 816 del media[w] 817 break 818 # remove unused entries from index and filesystem 819 if not merge and data is None: 820 # delete the entry 821 del content[k] 822 content_changed=True 823 # we have to remove the data file, the gcd file and the url file 824 _fname='%02d.dat' % k 825 if _fname in dirlisting: 826 self.rmfile(self.protocolclass.media_directory+'/'+_fname) 827 del dirlisting[_fname] 828 gcdname='%02d.gcd' % k 829 if gcdname in dirlisting: 830 self.rmfile(self.protocolclass.media_directory+'/'+gcdname) 831 del dirlisting[gcdname] 832 urlname='%02d.url' % k 833 if urlname in dirlisting: 834 self.rmfile(self.protocolclass.media_directory+'/'+urlname) 835 del dirlisting[urlname] 836 continue 837 init[index]={'name': name, 'data': data} 838 839 # go through adding new media not currently on the phone 840 # assign these item keys in the content media 841 applications={} 842 for sp in range(100): 843 if len(media.keys()) == 0: 844 break 845 if sp not in content: 846 #found a hole 847 for w in media.keys(): 848 C,E,add_to_index=self.make_new_media_entry(media[w], sp, type) 849 if add_to_index=='index': 850 content[sp]= {'C': C, 'E': E} 851 init[sp]=media[w] 852 content_changed=True 853 elif add_to_index=='content': 854 content[sp]= {'C': C, 'E': E} 855 applications[sp]=media[w] 856 content_changed=True 857 else: 858 self.log("Unknown media type for "+`media[w]['name']`+". Not written to phone.") 859 sp-=1 860 del media[w] 861 break 862 863 if len(media.keys()): 864 self.log("Phone index full, some media not written to phone") 865 866 # write out the new index, there are two files on this phone 867 # the content file and the media index, kinda dumb having two copies 868 # of the same thing 869 # content_file 870 content_count=0 871 keys=content.keys() 872 keys.sort() 873 cfile=self.protocolclass.content_file() 874 # add in the !C types first 875 for k in keys: 876 content_count+=1 877 cfile.items.append(content[k]['C']) 878 for k in keys: 879 cfile.items.append(content[k]['E']) 880 #terminator 881 entry=self.protocolclass.content_entry() 882 entry.type='!F' 883 cfile.items.append(entry) 884 buffer=prototypes.buffer() 885 cfile.writetobuffer(buffer, logtitle="Updated content file "+self.protocolclass.content_file_name) 886 self.writefile(self.protocolclass.content_file_name, buffer.getvalue()) 887 888 countfile=self.protocolclass.content_count() 889 countfile.count=`content_count` 890 buffer=prototypes.buffer() 891 countfile.writetobuffer(buffer, logtitle="Updated content count file "+self.protocolclass.content_count_file_name) 892 self.writefile(self.protocolclass.content_count_file_name, buffer.getvalue()) 893 894 # now write out the index file (this is like the verizon LG phones) 895 keys=init.keys() 896 keys.sort() 897 ifile=self.protocolclass.indexfile() 898 ifile.numactiveitems=len(keys) 899 for k in keys: 900 entry=self.protocolclass.indexentry() 901 entry.index=k 902 entry.const=index_const 903 entry.name='%02d.dat' % k 904 ifile.items.append(entry) 905 # fill the remainder of the entries with unused keys 906 for k in range(max_media_entries): 907 if k in keys: 908 continue 909 entry=self.protocolclass.indexentry() 910 entry.index=k 911 entry.const=index_const 912 ifile.items.append(entry) 913 buffer=prototypes.buffer() 914 ifile.writetobuffer(buffer, logtitle="Updated index file "+indexfile) 915 self.writefile(indexfile, buffer.getvalue()) 916 917 # Write out files - we compare against existing dir listing and don't rewrite if they 918 # are the same size 919 for k in keys: 920 entry=init[k] 921 data=entry.get("data", None) 922 _fname='%02d.dat' % k 923 gcdname='%02d.gcd' % k 924 urlname='%02d.url' % k 925 if data is None: 926 if _fname not in dirlisting: 927 self.log("Index error. I have no data for "+entry['name']+" and it isn't already in the filesystem") 928 continue 929 contentsize=len(data) 930 urlcontents='file://'+entry['name'] 931 gcdcontents=self.makegcd(entry['name'],contentsize) 932 if _fname in dirlisting and len(data)==dirlisting[_fname]['size']: 933 self.log("Skipping writing %s/%s as there is already a file of the same length" % (self.protocolclass.media_directory,entry['name'])) 934 # repair gcd and url incase they are missing 935 if gcdname not in dirlisting: 936 self.writefile(self.protocolclass.media_directory+"/"+gcdname, gcdcontents) 937 if urlname not in dirlisting: 938 self.writefile(self.protocolclass.media_directory+"/"+urlname, urlcontents) 939 continue 940 self.writefile(self.protocolclass.media_directory+"/"+_fname, data) 941 self.writefile(self.protocolclass.media_directory+"/"+gcdname, gcdcontents) 942 self.writefile(self.protocolclass.media_directory+"/"+urlname, urlcontents) 943 944 # write out applications 945 for k in applications.keys(): 946 entry=applications[k] 947 data=entry.get("data", None) 948 _fname='%02d.jar' % k 949 jadname='%02d.jad' % k 950 urlname='%02d.url' % k 951 if data is None: 952 if _fname not in dirlisting: 953 self.log("Index error. I have no data for "+entry['name']+" and it isn't already in the filesystem") 954 print "here2" 955 continue 956 contentsize=len(data) 957 urlcontents='file://'+entry['name'] 958 jadcontents=self.makejad(entry['name'],contentsize) 959 print "here3" 960 if _fname in dirlisting and len(data)==dirlisting[_fname]['size']: 961 self.log("Skipping writing %s/%s as there is already a file of the same length" % (self.protocolclass.media_directory,entry['name'])) 962 # repair jad and url incase they are missing 963 if jadname not in dirlisting: 964 self.writefile(self.protocolclass.media_directory+"/"+jadname, jadcontents) 965 if urlname not in dirlisting: 966 self.writefile(self.protocolclass.media_directory+"/"+urlname, urlcontents) 967 print "here4" 968 continue 969 print "here5" 970 self.writefile(self.protocolclass.media_directory+"/"+_fname, data) 971 self.writefile(self.protocolclass.media_directory+"/"+jadname, jadcontents) 972 self.writefile(self.protocolclass.media_directory+"/"+urlname, urlcontents) 973 974 del results[mediakey] # done with it 975 reindexfunction(results) 976 if content_changed: 977 results["rebootphone"]=True 978 return results
979
980 - def make_new_media_entry(self, entry, index, type):
981 c=self.protocolclass.content_entry() 982 e=self.protocolclass.content_entry() 983 name=common.stripext(entry['name']) 984 ext=common.getext(entry['name']) 985 data=entry.get("data", None) 986 add_to_index='index' 987 c.type='!C' 988 c.index1=index 989 c.name1=name 990 try: 991 c.mime_type=self.__mimetype[ext] 992 except: 993 # bad file type 994 add_to_index='none' 995 return c, e, add_to_index 996 if c.mime_type=='application/java-archive': 997 c.content_type='Games' 998 e.location_maybe='midlet:'+name 999 add_to_index='content' 1000 elif type=='ringers': 1001 c.content_type='Ringers' 1002 else: 1003 c.content_type='Screen Savers' 1004 c.size=`((3024+len(data))/1008)*1008` 1005 e.type='!E' 1006 e.index2=index 1007 e.name2=name 1008 return c, e, add_to_index
1009
1010 - def makegcd(self,filename,size):
1011 "Build a GCD file for filename" 1012 ext=common.getext(filename.lower()) 1013 noextname=common.stripext(filename) 1014 try: 1015 mimetype=self.__mimetype[ext] 1016 gcdcontent="Content-Type: "+mimetype+"\nContent-Name: "+noextname+"\nContent-Version: 1.0\nContent-Vendor: BitPim\nContent-URL: file://"+filename+"\nContent-Size: "+`size`+"\nContent-Description: Content for V10044 LG PM225"+"\n\n\n" 1017 except: 1018 gcdcontent="Content-Name: "+noextname+"\nContent-Version: 1.0\nContent-Vendor: BitPim\nContent-URL: file://"+filename+"\nContent-Size: "+`size`+"\n\n\n" 1019 return gcdcontent
1020
1021 - def makejad(self,filename,size):
1022 "Build a JAD file for filename" 1023 ext=common.getext(filename.lower()) 1024 noextname=common.stripext(filename) 1025 jadcontent="MIDlet-1: "+noextname+", "+noextname+".png, BitPim\nMIDlet-Jar-Size: "+`size`+"\nMIDlet-Jar-URL: "+filename+"\nMIDlet-Name: "+noextname+"\nMIDlet-Vendor: Unknown\nMIDlet-Version: 1.0\nMicroEdition-Configuration: CLDC-1.0\nMicroEdition-Profile: MIDP-1.0\nContent-Folder: Games\n\n\n" 1026 return jadcontent
1027 1028 #----- Phone Detection ----------------------------------------------------------- 1029 1030 brew_version_file='ams/version.txt' 1031 brew_version_txt_key='ams_version.txt' 1032 my_model='PM225' 1033
1034 - def getphoneinfo(self, phone_info):
1035 self.log('Getting Phone Info') 1036 try: 1037 s=self.getfilecontents('ams/version.txt') 1038 if s[:5]==self.my_model: 1039 phone_info.append('Model:', self.my_model) 1040 phone_info.append('ESN:', self.get_brew_esn()) 1041 req=p_brew.firmwarerequest() 1042 #res=self.sendbrewcommand(req, self.protocolclass.firmwareresponse) 1043 #phone_info.append('Firmware Version:', res.firmware) 1044 txt=self.getfilecontents("nvm/nvm/nvm_0000")[207:217] 1045 phone_info.append('Phone Number:', txt) 1046 except: 1047 pass 1048 return
1049 1050 1051 parentprofile=com_lgvx4400.Profile
1052 -class Profile(parentprofile):
1053 protocolclass=Phone.protocolclass 1054 serialsname=Phone.serialsname 1055 BP_Calendar_Version=3 1056 phone_manufacturer='LG Electronics Inc' 1057 phone_model='PM225' # from Sprint 1058 brew_required=True 1059 RINGTONE_LIMITS= { 1060 'MAXSIZE': 250000 1061 } 1062 1063 WALLPAPER_WIDTH=160 1064 WALLPAPER_HEIGHT=120 1065 MAX_WALLPAPER_BASENAME_LENGTH=30 1066 WALLPAPER_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .-_" 1067 WALLPAPER_CONVERT_FORMAT="jpg" 1068 1069 MAX_RINGTONE_BASENAME_LENGTH=30 1070 RINGTONE_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .-_" 1071 DIALSTRING_CHARS="[^0-9PT#*]" 1072 1073 # our targets are the same for all origins 1074 imagetargets={} 1075 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", 1076 {'width': 160, 'height': 120, 'format': "JPEG"})) 1077 # delay auto-detection when the phone is plugged in (in seconds) 1078 autodetect_delay=3 1079
1080 - def convertphonebooktophone(self, helper, data):
1081 """Converts the data to what will be used by the phone 1082 1083 @param data: contains the dict returned by getfundamentals 1084 as well as where the results go""" 1085 results={} 1086 1087 speeds={} 1088 1089 for pbentry in data['phonebook']: 1090 if len(results)==self.protocolclass.NUMPHONEBOOKENTRIES: 1091 break 1092 e={} # entry out 1093 entry=data['phonebook'][pbentry] # entry in 1094 try: 1095 # serials, can't use 0 as default as the phone uses this for the first entry 1096 serial1=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'serial1', 0xFFFFFFFF) 1097 1098 e['serial1']=serial1 1099 for ss in entry["serials"]: 1100 if ss["sourcetype"]=="bitpim": 1101 e['bitpimserial']=ss 1102 assert e['bitpimserial'] 1103 1104 # name 1105 e['name']=helper.getfullname(entry.get('names', []),1,1,32)[0] 1106 1107 # categories/groups 1108 cat=helper.makeone(helper.getcategory(entry.get('categories', []),0,1,32), None) 1109 if cat is None: 1110 e['group']=0 1111 else: 1112 key,value=self._getgroup(cat, data['groups']) 1113 if key is not None: 1114 e['group']=key 1115 else: 1116 # sorry no space for this category 1117 e['group']=0 1118 1119 # email addresses 1120 emails=helper.getemails(entry.get('emails', []) ,0,self.protocolclass.NUMEMAILS,72) 1121 e['emails']=helper.filllist(emails, self.protocolclass.NUMEMAILS, "") 1122 1123 # url 1124 e['url']=helper.makeone(helper.geturls(entry.get('urls', []), 0,1,74), "") 1125 1126 # memo (-1 is to leave space for null terminator - not all software puts it in, but we do) 1127 e['memo']=helper.makeone(helper.getmemos(entry.get('memos', []), 0, 1, self.protocolclass.MEMOLENGTH-1), "") 1128 1129 # phone numbers 1130 # there must be at least one email address or phonenumber 1131 minnumbers=1 1132 if len(emails): minnumbers=0 1133 numbers=helper.getnumbers(entry.get('numbers', []),minnumbers,self.protocolclass.NUMPHONENUMBERS) 1134 e['numberspeeds']=[] 1135 e['numbertypes']=[] 1136 e['numbers']=[] 1137 for numindex in range(len(numbers)): 1138 num=numbers[numindex] 1139 # deal with type 1140 b4=len(e['numbertypes']) 1141 type=num['type'] 1142 for i,t in enumerate(self.protocolclass.numbertypetab): 1143 if type==t: 1144 e['numbertypes'].append(i) 1145 break 1146 if t=='none': # conveniently last entry 1147 e['numbertypes'].append(i) 1148 break 1149 if len(e['numbertypes'])==b4: 1150 # we couldn't find a type for the number 1151 helper.add_error_message("%s has number %s of type %s, the phone does not support this type" % 1152 (e['name'], num['number'], num['type'])) 1153 continue 1154 # deal with number 1155 number=self.phonize(num['number']) 1156 if len(number)==0: 1157 # no actual digits in the number 1158 continue 1159 if len(number)>48: # get this number from somewhere sensible 1160 # ::TODO:: number is too long and we have to either truncate it or ignore it? 1161 number=number[:48] # truncate for moment 1162 e['numbers'].append(number) 1163 # deal with speed dial 1164 sd=num.get("speeddial", 0xFF) 1165 if sd>=self.protocolclass.FIRSTSPEEDDIAL and sd<=self.protocolclass.LASTSPEEDDIAL: 1166 e['numberspeeds'].append(sd) 1167 else: 1168 e['numberspeeds'].append(0xFF) 1169 1170 e['numberspeeds']=helper.filllist(e['numberspeeds'], 5, 0xFF) 1171 e['numbertypes']=helper.filllist(e['numbertypes'], 5, 0) 1172 e['numbers']=helper.filllist(e['numbers'], 5, "") 1173 1174 # ringtones, wallpaper 1175 e['ringtone']=helper.getringtone(entry.get('ringtones', []), 'call', None) 1176 # e['msgringtone']=helper.getringtone(entry.get('ringtones', []), 'message', None) 1177 e['wallpaper']=helper.getwallpaper(entry.get('wallpapers', []), 'call', None) 1178 1179 # flags 1180 e['secret']=helper.getflag(entry.get('flags',[]), 'secret', False) 1181 results[pbentry]=e 1182 1183 except helper.ConversionFailed: 1184 continue 1185 1186 data['phonebook']=results 1187 return data
1188 1189 _supportedsyncs=( 1190 ('phonebook', 'read', None), # all phonebook reading 1191 ('calendar', 'read', None), # all calendar reading 1192 ('wallpaper', 'read', None), # all wallpaper reading 1193 ('ringtone', 'read', None), # all ringtone reading 1194 ('call_history', 'read', None),# all call history list reading 1195 ('memo', 'read', None), # all memo list reading 1196 ('sms', 'read', None), # all SMS list reading 1197 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 1198 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 1199 ('wallpaper', 'write', 'MERGE'), # merge and overwrite wallpaper 1200 ('wallpaper', 'write', 'OVERWRITE'), 1201 ('ringtone', 'write', 'MERGE'), # merge and overwrite ringtone 1202 ('ringtone', 'write', 'OVERWRITE'), 1203 ('memo', 'write', 'OVERWRITE'), # all memo list writing 1204 ('sms', 'write', 'OVERWRITE'), # all SMS list writing 1205 ) 1206
1207 - def QueryAudio(self, origin, currentextension, afi):
1208 # we don't modify any of these 1209 if afi.format in ("MIDI", "QCP"): 1210 return currentextension, afi 1211 # examine mp3 1212 if afi.format=="MP3": 1213 if afi.channels==1 and 8<=afi.bitrate<=128 and 16000<=afi.samplerate<=44100: 1214 return currentextension, afi 1215 # convert it 1216 return ("mp3", fileinfo.AudioFileInfo(afi, **{'format': 'MP3', 'channels': 1, 'bitrate': 32, 'samplerate': 22050}))
1217 1218 1219 field_color_data={ 1220 'phonebook': { 1221 'name': { 1222 'first': 0, 'middle': 0, 'last': 0, 'full': 1, 1223 'nickname': 0, 'details': 1 }, 1224 'number': { 1225 'type': 5, 'speeddial': 5, 'number': 5, 'details': 5 }, 1226 'email': 3, 1227 'address': { 1228 'type': 0, 'company': 0, 'street': 0, 'street2': 0, 1229 'city': 0, 'state': 0, 'postalcode': 0, 'country': 0, 1230 'details': 0 }, 1231 'url': 1, 1232 'memo': 1, 1233 'category': 1, 1234 'wallpaper': 1, 1235 'ringtone': 1, 1236 'storage': 0, 1237 }, 1238 'calendar': { 1239 'description': True, 'location': False, 'allday': True, 1240 'start': True, 'end': True, 'priority': False, 1241 'alarm': True, 'vibrate': False, 1242 'repeat': True, 1243 'memo': False, 1244 'category': False, 1245 'wallpaper': False, 1246 'ringtone': True, 1247 }, 1248 'memo': { 1249 'subject': True, 1250 'date': False, 1251 'secret': False, 1252 'category': False, 1253 'memo': True, 1254 }, 1255 'todo': { 1256 'summary': False, 1257 'status': False, 1258 'due_date': False, 1259 'percent_complete': False, 1260 'completion_date': False, 1261 'private': False, 1262 'priority': False, 1263 'category': False, 1264 'memo': False, 1265 }, 1266 }
1267