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

Source Code for Module phones.com_lgvx3200

  1  ### BITPIM 
  2  ### 
  3  ### Copyright (C) 2005      Bruce Schurmann <austinbp@schurmann.org> 
  4  ### Copyright (C) 2003-2005 Roger Binns <rogerb@rogerbinns.com> 
  5  ### 
  6  ### This program is free software; you can redistribute it and/or modify 
  7  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
  8  ### 
  9  ### $Id: com_lgvx3200.py 3918 2007-01-19 05:15:12Z djpham $ 
 10   
 11  """Communicate with the LG VX3200 cell phone 
 12   
 13  The VX3200 is somewhat similar to the VX4400 
 14   
 15  """ 
 16   
 17  # standard modules 
 18  import time 
 19  import cStringIO 
 20  import sha 
 21  import re 
 22   
 23  # my modules 
 24  import common 
 25  import copy 
 26  import helpids 
 27  import p_lgvx3200 
 28  import com_lgvx4400 
 29  import com_brew 
 30  import com_phone 
 31  import com_lg 
 32  import prototypes 
 33  import phone_media_codec 
 34  import conversions 
 35   
 36  media_codec=phone_media_codec.codec_name 
 37   
 38   
39 -class Phone(com_lgvx4400.Phone):
40 "Talk to the LG VX3200 cell phone" 41 42 desc="LG-VX3200" 43 helpid=helpids.ID_PHONE_LGVX3200 44 45 wallpaperindexfilename="download/dloadindex/brewImageIndex.map" 46 ringerindexfilename="download/dloadindex/brewRingerIndex.map" 47 48 protocolclass=p_lgvx3200 49 serialsname='lgvx3200' 50 51 # more VX3200 indices 52 imagelocations=( 53 # offset, index file, files location, type, maximumentries 54 ( 11, "download/dloadindex/brewImageIndex.map", "download", "images", 3) , 55 ) 56 57 ringtonelocations=( 58 # offset, index file, files location, type, maximumentries 59 ( 27, "download/dloadindex/brewRingerIndex.map", "user/sound/ringer", "ringers", 30), 60 ) 61 62 63 builtinimages= ('Sport 1', 'Sport 2', 'Nature 1', 'Nature 2', 64 'Animal', 'Martini', 'Goldfish', 'Umbrellas', 65 'Mountain climb', 'Country road') 66 67 # There are a few more builtins but they cannot be sent to the phone in phonebook 68 # entries so I am leaving them out. 69 builtinringtones= ('Ring 1', 'Ring 2', 'Ring 3', 'Ring 4', 'Ring 5', 'Ring 6', 70 'Ring 7', 'Ring 8', 'Annen Polka', 'Pachelbel Canon', 71 'Hallelujah', 'La Traviata', 'Leichte Kavallerie Overture', 72 'Mozart Symphony No.40', 'Bach Minuet', 'Farewell', 73 'Mozart Piano Sonata', 'Sting', 'O solemio', 74 'Pizzicata Polka', 'Stars and Stripes Forever', 75 'Pineapple Rag', 'When the Saints Go Marching In', 'Latin', 76 'Carol 1', 'Carol 2') 77
78 - def __init__(self, logtarget, commport):
79 com_lgvx4400.Phone.__init__(self,logtarget,commport) 80 self.mode=self.MODENONE 81 self.mediacache=self.DirCache(self)
82
83 - def makeentry(self, counter, entry, dict):
84 e=com_lgvx4400.Phone.makeentry(self, counter, entry, dict) 85 e.entrysize=0x202 86 return e
87
88 - def getindex(self, indexfile):
89 "Read an index file" 90 index={} 91 # Hack for LG-VX3200 - wallpaper index file is not real 92 if re.search("ImageIndex", indexfile) is not None: 93 # A little special treatment for wallpaper related media files 94 # Sneek a peek at the file because if it is zero len we do not index it 95 ind=0 96 for ifile in 'wallpaper', 'poweron', 'poweroff': 97 ifilefull="download/"+ifile+".bit" 98 try: 99 mediafiledata=self.mediacache.readfile(ifilefull) 100 if len(mediafiledata)!=0: 101 index[ind]=ifile 102 ind = ind + 1 103 self.log("Index file "+indexfile+" entry added: "+ifile) 104 except: 105 pass 106 else: 107 # A little special treatment for ringer related media files 108 try: 109 buf=prototypes.buffer(self.getfilecontents(indexfile)) 110 except com_brew.BrewNoSuchFileException: 111 # file may not exist 112 return index 113 g=self.protocolclass.indexfile() 114 g.readfrombuffer(buf, logtitle="Read index file "+indexfile) 115 for i in g.items: 116 if i.index!=0xffff: 117 ifile=re.sub("\.mid|\.MID", "", i.name) 118 self.log("Index file "+indexfile+" entry added: "+ifile) 119 index[i.index]=ifile 120 return index
121
122 - def getmedia(self, maps, result, key):
123 """Returns the contents of media as a dict where the key is a name as returned 124 by getindex, and the value is the contents of the media""" 125 media={} 126 # the maps 127 type=None 128 for offset,indexfile,location,type,maxentries in maps: 129 index=self.getindex(indexfile) 130 for i in index: 131 if type=="images": 132 # Wallpaper media files are all BIT type 133 mediafilename=index[i]+".bit" 134 else: 135 # Ringer media files are all MID suffixed 136 mediafilename=index[i]+".mid" 137 try: 138 media[index[i]]=self.mediacache.readfile(location+"/"+mediafilename) 139 except com_brew.BrewNoSuchFileException: 140 self.log("Missing index file: "+location+"/"+mediafilename) 141 result[key]=media 142 return result
143
144 - def savemedia(self, mediakey, mediaindexkey, maps, results, merge, reindexfunction):
145 """Actually saves out the media 146 147 @param mediakey: key of the media (eg 'wallpapers' or 'ringtone') 148 @param mediaindexkey: index key (eg 'wallpaper-index') 149 @param maps: list index files and locations 150 @param results: results dict 151 @param merge: are we merging or overwriting what is there? 152 @param reindexfunction: the media is re-indexed at the end. this function is called to do it 153 """ 154 print results.keys() 155 # I humbly submit this as the longest function in the bitpim code ... 156 # wp and wpi are used as variable names as this function was originally 157 # written to do wallpaper. it works just fine for ringtones as well 158 # 159 # LG-VX3200 special notes: 160 # The index entries for both the wallpaper and ringers are 161 # hacked just a bit AND are hacked in slightly different 162 # ways. In addition to that the media for the wallpapers is 163 # currently in BMP format inside the program and must be 164 # converted to BIT (LG proprietary) on the way out. 165 # 166 # In the following: 167 # wp has file references that nave no suffixes 168 # wpi has file references that are mixed, i.e. some have file suffixes (comes from UI that way) 169 wp=results[mediakey].copy() 170 wpi=results[mediaindexkey].copy() 171 172 # Walk through wp and remove any suffixes that make their way in via the UI 173 for k in wp.keys(): 174 wp[k]['name']=re.sub("\....$", "", wp[k]['name']) 175 176 # remove builtins 177 for k in wpi.keys(): 178 if wpi[k]['origin']=='builtin': 179 del wpi[k] 180 181 # build up list into init 182 init={} 183 for offset,indexfile,location,type,maxentries in maps: 184 init[type]={} 185 for k in wpi.keys(): 186 if wpi[k]['origin']==type: 187 index=k-offset 188 name=wpi[k]['name'] 189 data=None 190 del wpi[k] 191 for w in wp.keys(): 192 if wp[w]['name']==name and wp[w]['origin']==type: 193 data=wp[w]['data'] 194 del wp[w] 195 if not merge and data is None: 196 # delete the entry 197 continue 198 init[type][index]={'name': name, 'data': data} 199 200 # init now contains everything from wallpaper-index 201 print init.keys() 202 # now look through wallpapers and see if anything remaining was assigned a particular 203 # origin 204 for w in wp.keys(): 205 o=wp[w].get("origin", "") 206 if o is not None and len(o) and o in init: 207 idx=-1 208 while idx in init[o]: 209 idx-=1 210 init[o][idx]=wp[w] 211 del wp[w] 212 213 # we now have init[type] with the entries and index number as key (negative indices are 214 # unallocated). Proceed to deal with each one, taking in stuff from wp as we have space 215 for offset,indexfile,location,type,maxentries in maps: 216 if type=="camera": break 217 index=init[type] 218 try: 219 dirlisting=self.getfilesystem(location) 220 except com_brew.BrewNoSuchDirectoryException: 221 self.mkdirs(location) 222 dirlisting={} 223 # rename keys to basename 224 for i in dirlisting.keys(): 225 dirlisting[i[len(location)+1:]]=dirlisting[i] 226 del dirlisting[i] 227 # what we will be deleting 228 dellist=[] 229 if not merge: 230 # get existing wpi for this location 231 wpi=results[mediaindexkey] 232 for i in wpi: 233 entry=wpi[i] 234 if entry['origin']==type: 235 # it is in the original index, are we writing it back out? 236 delit=True 237 for idx in index: 238 if index[idx]['name']==entry['name']: 239 delit=False 240 break 241 if delit: 242 # Add the media file suffixes back to match real filenames on phone 243 if type=="ringers": 244 entryname=entry['name']+".mid" 245 else: 246 entryname=entry['name']+".bit" 247 if entryname in dirlisting: 248 dellist.append(entryname) 249 else: 250 self.log("%s in %s index but not filesystem" % (entryname, type)) 251 # go ahead and delete unwanted files 252 print "deleting",dellist 253 for f in dellist: 254 self.mediacache.rmfile(location+"/"+f) 255 # LG-VX3200 special case: 256 # We only keep (upload) wallpaper images if there was a legit 257 # one on the phone to begin with. This code will weed out any 258 # attempts to the contrary. 259 if type=="images": 260 losem=[] 261 # get existing wpi for this location 262 wpi=results[mediaindexkey] 263 for idx in index: 264 delit=True 265 for i in wpi: 266 entry=wpi[i] 267 if entry['origin']==type: 268 if index[idx]['name']==entry['name']: 269 delit=False 270 break 271 if delit: 272 self.log("Inhibited upload of illegit image (not originally on phone): "+index[idx]['name']) 273 losem.append(idx) 274 # Now actually remove any illegit images from upload attempt 275 for idx in losem: 276 del index[idx] 277 # slurp up any from wp we can take 278 while len(index)<maxentries and len(wp): 279 idx=-1 280 while idx in index: 281 idx-=1 282 k=wp.keys()[0] 283 index[idx]=wp[k] 284 del wp[k] 285 # normalise indices 286 index=self._normaliseindices(index) # hey look, I called a function! 287 # move any overflow back into wp 288 if len(index)>maxentries: 289 keys=index.keys() 290 keys.sort() 291 for k in keys[maxentries:]: 292 idx=-1 293 while idx in wp: 294 idx-=1 295 wp[idx]=index[k] 296 del index[k] 297 298 # Now add the proper media file suffixes back to the internal index 299 # prior to the finish up steps coming 300 for k in index.keys(): 301 if type=="ringers": 302 index[k]['name']=index[k]['name']+".mid" 303 else: 304 index[k]['name']=index[k]['name']+".bit" 305 306 # write out the new index 307 keys=index.keys() 308 keys.sort() 309 ifile=self.protocolclass.indexfile() 310 ifile.numactiveitems=len(keys) 311 for k in keys: 312 entry=self.protocolclass.indexentry() 313 entry.index=k 314 entry.name=index[k]['name'] 315 ifile.items.append(entry) 316 while len(ifile.items)<maxentries: 317 ifile.items.append(self.protocolclass.indexentry()) 318 buffer=prototypes.buffer() 319 ifile.writetobuffer(buffer, autolog=False) 320 if type!="images": 321 # The images index file on the LG-VX3200 is a noop - don't write 322 self.logdata("Updated index file "+indexfile, buffer.getvalue(), ifile) 323 self.writefile(indexfile, buffer.getvalue()) 324 325 # Write out files - we compare against existing dir listing and don't rewrite if they 326 # are the same size 327 for k in keys: 328 entry=index[k] 329 entryname=entry['name'] 330 data=entry.get("data", None) 331 # Special test for wallpaper files - LG-VX3200 will ONLY accept these files 332 if type=="images": 333 if entryname!="wallpaper.bit" and entryname!="poweron.bit" and entryname!="poweroff.bit": 334 self.log("The wallpaper files can only be wallpaper.bmp, poweron.bmp or poweroff.bmp. "+entry['name']+" does not conform - skipping upload.") 335 continue 336 if data is None: 337 if entryname not in dirlisting: 338 self.log("Index error. I have no data for "+entryname+" and it isn't already in the filesystem - skipping upload.") 339 continue 340 # Some of the wallpaper media files came here from the UI as BMP files. 341 # These need to be converted to LGBIT files on the way out. 342 if type=="images" and data[0:2]=="BM": 343 data=conversions.convertbmptolgbit(data) 344 if data is None: 345 self.log("The wallpaper BMP images must be 8BPP or 24BPP, "+entry['name']+", does not comply - skipping upload.") 346 continue 347 # Can only upload 128x128 images 348 if type=="images" and (common.LSBUint16(data[0:2])!=128 or common.LSBUint16(data[2:4])!=128): 349 self.log("The wallpaper must be 128x128, "+entry['name']+", does not comply - skipping upload.") 350 continue 351 # This following test does not apply to wallpaper - it is always the same size on the LG-VX3200! 352 if type!="images": 353 if entryname in dirlisting and len(data)==dirlisting[entryname]['size']: 354 self.log("Skipping writing %s/%s as there is already a file of the same length" % (location,entryname)) 355 continue 356 self.mediacache.writefile(location+"/"+entryname, data) 357 self.log("Wrote media file: "+location+"/"+entryname) 358 # did we have too many 359 if len(wp): 360 for k in wp: 361 self.log("Unable to put %s on the phone as there weren't any spare index entries" % (wp[k]['name'],)) 362 363 # Note that we don't write to the camera area 364 365 # tidy up - reread indices 366 del results[mediakey] # done with it 367 # This excessive and expensive so do not do 368 # self.mediacache.flush() 369 # self.log("Flushed ringer cache") 370 reindexfunction(results) 371 return results
372 373 my_model='AX3200'
374 375 parentprofile=com_lgvx4400.Profile
376 -class Profile(parentprofile):
377 protocolclass=Phone.protocolclass 378 serialsname=Phone.serialsname 379 phone_manufacturer='LG Electronics Inc' 380 phone_model='VX3200' 381 382 # use for auto-detection 383 phone_manufacturer='LG Electronics Inc.' 384 phone_model='VX3200 107' 385 386 # no direct usb interface 387 usbids=com_lgvx4400.Profile.usbids_usbtoserial 388
389 - def convertphonebooktophone(self, helper, data):
390 """Converts the data to what will be used by the phone 391 392 @param data: contains the dict returned by getfundamentals 393 as well as where the results go""" 394 results={} 395 396 speeds={} 397 398 self.normalisegroups(helper, data) 399 400 for pbentry in data['phonebook']: 401 if len(results)==self.protocolclass.NUMPHONEBOOKENTRIES: 402 break 403 e={} # entry out 404 entry=data['phonebook'][pbentry] # entry in 405 try: 406 # serials 407 serial1=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'serial1', 0) 408 serial2=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'serial2', serial1) 409 410 e['serial1']=serial1 411 e['serial2']=serial2 412 for ss in entry["serials"]: 413 if ss["sourcetype"]=="bitpim": 414 e['bitpimserial']=ss 415 assert e['bitpimserial'] 416 417 # name 418 e['name']=helper.getfullname(entry.get('names', []),1,1,22)[0] 419 420 # categories/groups 421 cat=helper.makeone(helper.getcategory(entry.get('categories', []),0,1,22), None) 422 if cat is None: 423 e['group']=0 424 else: 425 key,value=self._getgroup(cat, data['groups']) 426 if key is not None: 427 # lgvx3200 fix 428 if key>5: 429 e['group']=0 430 #self.log("Custom Groups in PB not supported - setting to No Group for "+e['name']) 431 print "Custom Groups in PB not supported - setting to No Group for "+e['name'] 432 else: 433 e['group']=key 434 else: 435 # sorry no space for this category 436 e['group']=0 437 438 # email addresses 439 emails=helper.getemails(entry.get('emails', []) ,0,self.protocolclass.NUMEMAILS,48) 440 e['emails']=helper.filllist(emails, self.protocolclass.NUMEMAILS, "") 441 442 # url 443 e['url']=helper.makeone(helper.geturls(entry.get('urls', []), 0,1,48), "") 444 445 # memo (-1 is to leave space for null terminator - not all software puts it in, but we do) 446 e['memo']=helper.makeone(helper.getmemos(entry.get('memos', []), 0, 1, self.protocolclass.MEMOLENGTH-1), "") 447 448 # phone numbers 449 # there must be at least one email address or phonenumber 450 minnumbers=1 451 if len(emails): minnumbers=0 452 numbers=helper.getnumbers(entry.get('numbers', []),minnumbers,self.protocolclass.NUMPHONENUMBERS) 453 e['numbertypes']=[] 454 e['numbers']=[] 455 for numindex in range(len(numbers)): 456 num=numbers[numindex] 457 # deal with type 458 b4=len(e['numbertypes']) 459 type=num['type'] 460 for i,t in enumerate(self.protocolclass.numbertypetab): 461 if type==t: 462 # some voodoo to ensure the second home becomes home2 463 if i in e['numbertypes'] and t[-1]!='2': 464 type+='2' 465 continue 466 e['numbertypes'].append(i) 467 break 468 if t=='none': # conveniently last entry 469 e['numbertypes'].append(i) 470 break 471 if len(e['numbertypes'])==b4: 472 # we couldn't find a type for the number 473 continue 474 # deal with number 475 number=self.phonize(num['number']) 476 if len(number)==0: 477 # no actual digits in the number 478 continue 479 if len(number)>48: # get this number from somewhere sensible 480 # ::TODO:: number is too long and we have to either truncate it or ignore it? 481 number=number[:48] # truncate for moment 482 e['numbers'].append(number) 483 # deal with speed dial 484 sd=num.get("speeddial", -1) 485 if self.protocolclass.NUMSPEEDDIALS: 486 if sd>=self.protocolclass.FIRSTSPEEDDIAL and sd<=self.protocolclass.LASTSPEEDDIAL: 487 speeds[sd]=(e['bitpimserial'], numindex) 488 489 e['numbertypes']=helper.filllist(e['numbertypes'], 5, 0) 490 e['numbers']=helper.filllist(e['numbers'], 5, "") 491 492 # ringtones, wallpaper 493 # LG VX3200 only supports writing the first 26 builtin ringers in pb 494 ecring=helper.getringtone(entry.get('ringtones', []), 'call', None) 495 if ecring is not None: 496 if ecring not in Phone.builtinringtones: 497 #self.log("Ringers past Carol 2 in PB not supported - setting to Default Ringer for "+e['name']) 498 print "Ringers past Carol 2 in PB not supported - setting to Default Ringer for "+e['name']+" id was: "+ecring 499 ecring=None 500 e['ringtone']=ecring 501 emring=helper.getringtone(entry.get('ringtones', []), 'message', None) 502 if emring is not None: 503 if emring not in Phone.builtinringtones: 504 #self.log("Ringers past Carol 2 in PB not supported - setting to Default MsgRinger for "+e['name']) 505 print "Ringers past Carol 2 in PB not supported - setting to Default MsgRinger for "+e['name']+" id was: "+emring 506 emring=None 507 e['msgringtone']=emring 508 # LG VX3200 does not support writing wallpaper in pb 509 ewall=helper.getwallpaper(entry.get('wallpapers', []), 'call', None) 510 if ewall is not None: 511 #self.log("Custom Wallpapers in PB not supported - setting to Default Wallpaper for "+e['name']) 512 print "Custom Wallpapers in PB not supported - setting to Default Wallpaper for "+e['name'] 513 e['wallpaper']=None 514 515 # flags 516 e['secret']=helper.getflag(entry.get('flags',[]), 'secret', False) 517 518 results[pbentry]=e 519 520 except helper.ConversionFailed: 521 continue 522 523 if self.protocolclass.NUMSPEEDDIALS: 524 data['speeddials']=speeds 525 data['phonebook']=results 526 return data
527 528 _supportedsyncs=( 529 ('phonebook', 'read', None), # all phonebook reading 530 ('calendar', 'read', None), # all calendar reading 531 ('wallpaper', 'read', None), # all wallpaper reading 532 ('ringtone', 'read', None), # all ringtone reading 533 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 534 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 535 # ('wallpaper', 'write', 'MERGE'), # merge and overwrite wallpaper 536 ('wallpaper', 'write', 'OVERWRITE'), # merge and overwrite wallpaper 537 ('ringtone', 'write', 'MERGE'), # merge and overwrite ringtone 538 ('ringtone', 'write', 'OVERWRITE'), 539 ('call_history', 'read', None), 540 ('memo', 'read', None), # all memo list reading DJP 541 ('memo', 'write', 'OVERWRITE'), # all memo list writing DJP 542 ('sms', 'read', None), 543 ('sms', 'write', 'OVERWRITE'), 544 ) 545 546 WALLPAPER_WIDTH=128 547 WALLPAPER_HEIGHT=128 548 MAX_WALLPAPER_BASENAME_LENGTH=19 549 WALLPAPER_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789 ." 550 WALLPAPER_CONVERT_FORMAT="bmp" 551 552 MAX_RINGTONE_BASENAME_LENGTH=19 553 RINGTONE_FILENAME_CHARS="abcdefghijklmnopqrstuvxwyz0123456789 ." 554 555 imageorigins={} 556 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images")) 557
558 - def GetImageOrigins(self):
559 return self.imageorigins
560 561 # our targets are the same for all origins 562 imagetargets={} 563 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", 564 {'width': 128, 'height': 128, 'format': "BMP"})) 565
566 - def GetTargetsForImageOrigin(self, origin):
567 return self.imagetargets
568
569 - def __init__(self):
570 parentprofile.__init__(self)
571