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

Source Code for Module phones.com_lgvx8500

  1  #!/usr/bin/env python 
  2   
  3  ### BITPIM 
  4  ### 
  5  ### Copyright (C) 2006 Joe Pham <djpham@bitpim.org> 
  6  ### 
  7  ### This program is free software; you can redistribute it and/or modify 
  8  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
  9  ### 
 10  ### $Id: com_lgvx8500.py 4305 2007-07-16 04:05:25Z djpham $ 
 11   
 12  """ 
 13  Communicate with the LG VX8500 cell phone 
 14  """ 
 15  # standard modules 
 16  import datetime 
 17   
 18  # wx modules 
 19   
 20  # BitPim modules 
 21  import common 
 22  import com_brew 
 23  import com_lg 
 24  import com_lgvx8300 
 25  import helpids 
 26  import fileinfo 
 27  import guihelper 
 28  import os 
 29  import p_brew 
 30  import p_lgvx8500 
 31  import playlist 
 32  import prototypes 
 33  import prototypeslg 
 34  import t9editor 
 35   
 36  parentphone=com_lgvx8300.Phone 
37 -class Phone(parentphone):
38 desc="LG-VX8500" 39 helpid=helpids.ID_PHONE_LGVX8500 40 41 protocolclass=p_lgvx8500 42 serialsname='lgvx8500' 43 44 my_model='VX8500' 45 builtinringtones= ('Low Beep Once', 'Low Beeps', 'Loud Beep Once', 'Loud Beeps', 'VZW Default Ringtone') + \ 46 tuple(['Ringtone '+`n` for n in range(1,13)]) + \ 47 ('No Ring',) 48 49 ringtonelocations= ( 50 # type index file default dir external dir max type Index 51 ( 'ringers', 'dload/myringtone.dat','brew/16452/lk/mr','mmc1/ringers', 100, 0x01, 100), 52 ( 'sounds', 'dload/mysound.dat', 'brew/16452/ms', '', 100, 0x02, None), 53 ( 'sounds(sd)', 'dload/sd_sound.dat', 'mmc1/my_sounds', '', 100, 0x02, None), 54 ( 'music', 'dload/efs_music.dat', 'my_music', '', 100, 0x104, None), 55 ( 'music(sd)', 'dload/sd_music.dat', 'mmc1/my_music', '', 100, 0x14, None), 56 ) 57 58 wallpaperlocations= ( 59 # type index file default dir external dir max type Index 60 ( 'images', 'dload/image.dat', 'brew/16452/mp', '', 100, 0x00, 100), 61 ( 'images(sd)', 'dload/sd_image.dat', 'mmc1/my_pix', '', 100, 0x10, None), 62 ( 'video', 'dload/video.dat', 'brew/16452/mf', '', 100, 0x03, None), 63 ( 'video(sd)', 'dload/sd_video.dat', 'mmc1/my_flix', '', 100, 0x13, None), 64 ) 65
66 - def setDMversion(self):
67 """Define the DM version required for this phone, default to DMv5""" 68 _fw_version=self.get_firmware_version()[-1] 69 self._DMv5=self.my_model=='VX8500' and _fw_version>'4'
70 71 # Fundamentals: 72 # - get_esn - same as LG VX-8300 73 # - getgroups - same as LG VX-8100 74 # - getwallpaperindices - LGUncountedIndexedMedia 75 # - getrintoneindices - LGUncountedIndexedMedia 76 # - DM Version - T85VZV01 - T85VZV04: 4, T85VZV05 - : 5 77 78 # Phonebook stuff-----------------------------------------------------------
79 - def _build_pb_info(self, fundamentals):
80 # build a dict of info to update pbentry 81 pbook=fundamentals.get('phonebook', {}) 82 wallpaper_index=fundamentals.get('wallpaper-index', {}) 83 ringtone_index=fundamentals.get('ringtone-index', {}) 84 r1={} 85 for k,e in pbook.items(): 86 r1[e['bitpimserial']['id']]={ 'wallpaper': \ 87 self._findmediainindex(wallpaper_index, 88 e['wallpaper'], 89 e['name'], 90 'wallpaper'), 91 'msgringtone': \ 92 self._findmediainindex(ringtone_index, 93 e['msgringtone'], 94 e['name'], 95 'message ringtone')} 96 serialupdates=fundamentals.get("serialupdates", []) 97 r2={} 98 for bps, serials in serialupdates: 99 r2[serials['serial1']]=r1[bps['id']] 100 return r2
101 - def _update_pb_file(self, pb, fundamentals, pbinfo):
102 # update the pbentry file 103 update_flg=False 104 for e in pb.items: 105 _info=pbinfo.get(e.serial1, None) 106 if _info: 107 wp=_info.get('wallpaper', None) 108 if wp is not None and wp!=e.wallpaper: 109 update_flg=True 110 e.wallpaper=wp 111 msgrt=_info.get('msgringtone', None) 112 if msgrt is not None and msgrt!=e.msgringtone: 113 update_flg=True 114 e.msgringtone=msgrt 115 if update_flg: 116 self.log('Updating wallpaper index') 117 buf=prototypes.buffer() 118 pb.writetobuffer(buf, logtitle="Updated index "+self.protocolclass.pb_file_name) 119 self.writefile(self.protocolclass.pb_file_name, buf.getvalue())
120 - def _update_pb_info(self, pbentries, fundamentals):
121 # Manually update phonebook data that the normal protocol should have 122 _pbinfo=self._build_pb_info(fundamentals) 123 self._update_pb_file(pbentries, fundamentals, _pbinfo)
124 - def _write_path_index(self, pbentries, pbmediakey, media_index, 125 index_file, invalid_values):
126 _path_entry=self.protocolclass.PathIndexEntry() 127 _path_file=self.protocolclass.PathIndexFile() 128 for _ in range(self.protocolclass.NUMPHONEBOOKENTRIES): 129 _path_file.items.append(_path_entry) 130 for _entry in pbentries.items: 131 _idx=getattr(_entry, pbmediakey) 132 if _idx in invalid_values or not media_index.has_key(_idx): 133 continue 134 if media_index[_idx].get('origin', None)=='builtin': 135 _filename=media_index[_idx]['name'] 136 elif media_index[_idx].get('filename', None): 137 _filename=media_index[_idx]['filename'] 138 else: 139 continue 140 _path_file.items[_entry.entrynumber]=self.protocolclass.PathIndexEntry( 141 pathname=_filename) 142 _buf=prototypes.buffer() 143 _path_file.writetobuffer(_buf, logtitle='Writing Path ID') 144 self.writefile(index_file, _buf.getvalue())
145
146 - def savephonebook(self, data):
147 "Saves out the phonebook" 148 res=parentphone.savephonebook(self, data) 149 # retrieve the phonebook entries 150 _buf=prototypes.buffer(self.getfilecontents( 151 self.protocolclass.pb_file_name)) 152 _pb_entries=self.protocolclass.pbfile() 153 _pb_entries.readfrombuffer(_buf, logtitle="Read phonebook file "+self.protocolclass.pb_file_name) 154 _rt_index=data.get('ringtone-index', {}) 155 _wp_index=data.get('wallpaper-index', {}) 156 # update info that the phone software failed to do!! 157 self._update_pb_info(_pb_entries, data) 158 # fix up ringtone index 159 self._write_path_index(_pb_entries, 'ringtone', 160 _rt_index, 161 self.protocolclass.RTPathIndexFile, 162 (0xffff,)) 163 # fix up msg ringtone index 164 self._write_path_index(_pb_entries, 'msgringtone', 165 _rt_index, 166 self.protocolclass.MsgRTIndexFile, 167 (0xffff,)) 168 # fix up wallpaer index 169 self._write_path_index(_pb_entries, 'wallpaper', 170 _wp_index, 171 self.protocolclass.WPPathIndexFile, 172 (0, 0xffff,)) 173 return res
174 175 # Playlist stuff------------------------------------------------------------
176 - def _get_all_songs(self, fundamentals):
177 # return a list of all available songs 178 _rt_index=fundamentals.get('ringtone-index', {}) 179 return [x['name'] for _,x in _rt_index.items() \ 180 if x.get('origin', None) in ('music', 'music(sd)') ]
181 - def _read_pl_list(self, filename):
182 # read and return the contents of the specified playlist 183 _req=self.protocolclass.PLPlayListFile() 184 _buf=prototypes.buffer(self.getfilecontents(filename)) 185 _req.readfrombuffer(_buf, logtitle="Read playlist "+filename) 186 _songs=[] 187 for _item in _req.items: 188 _songs.append(common.basename(_item.pathname)) 189 _entry=playlist.PlaylistEntry() 190 _entry.name=common.stripext(common.basename(filename)) 191 _entry.songs=_songs 192 return _entry
193 - def getplaylist(self, result):
194 # return a list of all available songs and playlists 195 # first, retrieve the list of all songs 196 result[playlist.masterlist_key]=self._get_all_songs(result) 197 # get a list of playlists and their contents 198 _pl_list=[] 199 try: 200 _req=self.protocolclass.PLIndexFile() 201 _buf=prototypes.buffer( 202 self.getfilecontents(self.protocolclass.PLIndexFileName)) 203 _req.readfrombuffer(_buf, logtitle='Reading Playlist Index') 204 for _item in _req.items: 205 try: 206 _pl_list.append(self._read_pl_list(_item.pathname)) 207 except com_brew.BrewNoSuchFileException: 208 pass 209 except: 210 if __debug__: 211 raise 212 except com_brew.BrewNoSuchFileException: 213 pass 214 except: 215 if __debug__: 216 raise 217 result[playlist.playlist_key]=_pl_list 218 return result
219
220 - def _get_info_from_index(self, filename):
221 _res={} 222 try: 223 _buf=prototypes.buffer( 224 self.getfilecontents(filename)) 225 _req=self.protocolclass.indexfile() 226 _req.readfrombuffer(_buf, logtitle='Reading Index File') 227 for _item in _req.items: 228 _res[common.basename(_item.filename)]={ 'size': _item.size, 229 'filename': _item.filename } 230 except com_brew.BrewNoSuchFileException: 231 pass 232 except: 233 if __debug__: 234 raise 235 return _res
236 - def _get_song_info(self):
237 # return a dict of all songs & their info 238 _res=self._get_info_from_index('dload/efs_music.dat') 239 _res.update(self._get_info_from_index('dload/sd_music.dat')) 240 return _res
241 - def _write_a_playlist(self, filename, pl, song_info):
242 # write a single playlist 243 _pl_file=self.protocolclass.PLPlayListFile() 244 _dt=datetime.datetime.now() 245 _onesec=datetime.timedelta(seconds=1) 246 _plsize=0 247 for _song in pl.songs: 248 if song_info.has_key(_song): 249 _dt-=_onesec 250 _entry=self.protocolclass.PLSongEntry( 251 pathname=song_info[_song]['filename'], 252 date=_dt.timetuple()[:6], 253 size=song_info[_song]['size']) 254 _pl_file.items.append(_entry) 255 _plsize+=1 256 if _plsize>=self.protocolclass.PLMaxSize: 257 # reached max size 258 break 259 _buf=prototypes.buffer() 260 _pl_file.writetobuffer(_buf, 261 logtitle='Writing Playlist '+pl.name) 262 self.writefile(filename, _buf.getvalue())
263 - def _write_playlists(self, playlists, song_info):
264 _pl_index=self.protocolclass.PLIndexFile() 265 for _pl in playlists: 266 try: 267 _filename=self.protocolclass.PLFilePath+'/'+_pl.name+\ 268 self.protocolclass.PLExt 269 self._write_a_playlist(_filename, _pl, song_info) 270 _pl_index.items.append( 271 self.protocolclass.PLIndexEntry(pathname=_filename)) 272 except: 273 self.log('Failed to write playlist '+_pl.name) 274 if __debug__: 275 raise 276 try: 277 _buf=prototypes.buffer() 278 _pl_index.writetobuffer(_buf, 279 logtitle='Writing Playlist Index') 280 self.writefile(self.protocolclass.PLIndexFileName, 281 _buf.getvalue()) 282 except: 283 self.log('Failed to write Playlist Index file') 284 if __debug__: 285 raise
286 - def saveplaylist(self, result, merge):
287 # delete all existing playlists 288 _buf=prototypes.buffer(self.getfilecontents( 289 self.protocolclass.PLIndexFileName)) 290 _req=self.protocolclass.PLIndexFile() 291 _req.readfrombuffer(_buf, logtitle='Reading Playlist Index') 292 for _item in _req.items: 293 try: 294 self.rmfile(_item.pathname) 295 except com_brew. BrewNoSuchFileException: 296 pass 297 except: 298 if __debug__: 299 raise 300 # get info on all songs 301 _song_info=self._get_song_info() 302 # update the new playlists 303 self._write_playlists(result.get(playlist.playlist_key, []), 304 _song_info) 305 return result
306 307 # T9 DB stuff
308 - def _get_current_db(self):
309 _current_db=None 310 try: 311 _data=self.getfilecontents( 312 self.protocolclass.T9USERDBFILENAME) 313 if _data: 314 _current_db=self.protocolclass.t9udbfile() 315 _current_db.readfrombuffer(prototypes.buffer(_data), 316 logtitle='Reading T9 User DB File') 317 except com_brew.BrewNoSuchFileException: 318 pass 319 return _current_db
320 - def _copy_fields(self, old, new):
321 for _field in ('unknown1', 'unknown2', 'unknown3', 'unknown4'): 322 setattr(new, _field, getattr(old, _field))
323 - def gett9db(self, result):
324 _req=self._get_current_db() 325 _t9list=t9editor.T9WordsList() 326 if _req: 327 for _item in _req.blocks: 328 _blk=_item.block 329 if _blk['type']==prototypeslg.T9USERDBBLOCK.WordsList_Type: 330 for _word in _blk['value']: 331 _t9list.append_word(_word['word']) 332 result[t9editor.dict_key]=_t9list 333 return result
334 _buffer_data={ 335 '1': 0x7FA, '2': 0x7FA, '3': 0x7FA, '4': 0x7FA, 336 '5': 0x7FA, '6': 0x7FA, '7': 0x7FA, '8': 0x7FA, 337 '9': 0x7FA, '0': 0x800 } 338 _t9keys=('1', '2', '3', '4', '5', '6', '7', '8', '9', '0')
339 - def savet9db(self, result, _):
340 _new_db=self.protocolclass.t9udbfile() 341 try: 342 _current_db=self._get_current_db() 343 if _current_db: 344 self._copy_fields(_current_db, _new_db) 345 except: 346 _current_db=None 347 _t9words={} 348 _t9list=result.get(t9editor.dict_key, t9editor.T9WordsList()) 349 _wordcount=0 350 for _key in _t9list.keys: 351 for _word in _t9list.get_words(_key): 352 _t9words.setdefault(_key[0], []).append(_word) 353 _wordcount+=1 354 _new_db.word_count=_wordcount 355 _total_free_size=0 356 for _key in self._t9keys: 357 _lst=_t9words.get(_key, []) 358 if _lst: 359 _val={ 'type': prototypeslg.T9USERDBBLOCK.WordsList_Type, 360 'value': [ { 'word': x } for x in _lst ] } 361 _blk=prototypeslg.T9USERDBBLOCK(_val) 362 _free_len=self._buffer_data[_key]-_blk.packetsize() 363 # ugly hack! 364 if _key!='1': 365 _free_len+=1 366 _total_free_size+=_free_len 367 _new_db.blocks.append(_val) 368 _new_db.blocks.append({ 'type': prototypeslg.T9USERDBBLOCK.FreeBlock_Type, 369 'value': _free_len }) 370 else: 371 if _key!='1': 372 _new_db.blocks.append({ 'type': prototypeslg.T9USERDBBLOCK.A0_Type, 373 'value': 0xA0 }) 374 _type=prototypeslg.T9USERDBBLOCK.FreeBlock_Type 375 _new_db.blocks.append({ 'type': _type, 376 'value': self._buffer_data[_key] }) 377 _total_free_size+=self._buffer_data[_key] 378 _new_db.free_space=_total_free_size 379 _buf=prototypes.buffer() 380 _new_db.writetobuffer(_buf) 381 self.writefile(self.protocolclass.T9USERDBFILENAME, 382 _buf.getvalue()) 383 # Need to reboot the phone to take effect 384 result['rebootphone']=True 385 return result
386 387 # Misc Stuff----------------------------------------------------------------
388 - def get_firmware_version(self):
389 # return the firmware version 390 req=p_brew.firmwarerequest() 391 res=self.sendbrewcommand(req, self.protocolclass.firmwareresponse) 392 return res.firmware
393 394 #------------------------------------------------------------------------------- 395 parentprofile=com_lgvx8300.Profile
396 -class Profile(parentprofile):
397 protocolclass=Phone.protocolclass 398 serialsname=Phone.serialsname 399 400 BP_Calendar_Version=3 401 phone_manufacturer='LG Electronics Inc' 402 phone_model='VX8500' 403 404 WALLPAPER_WIDTH=240 405 WALLPAPER_HEIGHT=320 406 MAX_WALLPAPER_BASENAME_LENGTH=32 407 WALLPAPER_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_() ." 408 WALLPAPER_CONVERT_FORMAT="jpg" 409 410 # the 8300 uses "W" for wait in the dialstring, it does not support "T" 411 DIALSTRING_CHARS="[^0-9PW#*]" 412 413 MAX_RINGTONE_BASENAME_LENGTH=32 414 RINGTONE_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_() ." 415 RINGTONE_LIMITS= { 416 'MAXSIZE': 200000 417 } 418 419 imageorigins={} 420 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images")) 421 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "video")) 422 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images(sd)")) 423 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "video(sd)"))
424 - def GetImageOrigins(self):
425 return self.imageorigins
426 427 ringtoneorigins=('ringers', 'sounds', 'sounds(sd)',' music', 'music(sd)') 428 excluded_ringtone_origins=('sounds', 'sounds(sd)', 'music', 'music(sd)') 429 430 # our targets are the same for all origins 431 imagetargets={} 432 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", 433 {'width': 240, 'height': 275, 'format': "JPEG"})) 434
435 - def GetTargetsForImageOrigin(self, origin):
436 return self.imagetargets
437 438
439 - def __init__(self):
440 parentprofile.__init__(self)
441
442 - def QueryAudio(self, origin, currentextension, afi):
443 _max_size=self.RINGTONE_LIMITS['MAXSIZE'] 444 setattr(afi, 'MAXSIZE', _max_size) 445 # we don't modify any of these 446 if afi.format in ("MIDI", "QCP", "PMD", "WMA"): 447 return currentextension, afi 448 # examine mp3 449 if afi.format=="MP3": 450 if afi.channels==1 and 8<=afi.bitrate<=64 and 16000<=afi.samplerate<=22050: 451 return currentextension, afi 452 # convert it 453 return ("mp3", fileinfo.AudioFileInfo(afi, **{'format': 'MP3', 454 'channels': 2, 455 'bitrate': 48, 456 'samplerate': 44100, 457 'MAXSIZE': _max_size }))
458 459 _supportedsyncs=( 460 ('phonebook', 'read', None), # all phonebook reading 461 ('calendar', 'read', None), # all calendar reading 462 ('wallpaper', 'read', None), # all wallpaper reading 463 ('ringtone', 'read', None), # all ringtone reading 464 ('call_history', 'read', None),# all call history list reading 465 ('sms', 'read', None), # all SMS list reading 466 ('memo', 'read', None), # all memo list reading 467 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 468 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 469 ('wallpaper', 'write', 'MERGE'), # merge and overwrite wallpaper 470 ('wallpaper', 'write', 'OVERWRITE'), 471 ('ringtone', 'write', 'MERGE'), # merge and overwrite ringtone 472 ('ringtone', 'write', 'OVERWRITE'), 473 ('sms', 'write', 'OVERWRITE'), # all SMS list writing 474 ('memo', 'write', 'OVERWRITE'), # all memo list writing 475 ('playlist', 'read', 'OVERWRITE'), 476 ('playlist', 'write', 'OVERWRITE'), 477 ('t9_udb', 'write', 'OVERWRITE'), 478 ) 479 if __debug__: 480 _supportedsyncs+=( 481 ('t9_udb', 'read', 'OVERWRITE'), 482 ) 483 484 field_color_data={ 485 'phonebook': { 486 'name': { 487 'first': 1, 'middle': 1, 'last': 1, 'full': 1, 488 'nickname': 0, 'details': 1 }, 489 'number': { 490 'type': 5, 'speeddial': 5, 'number': 5, 'details': 5 }, 491 'email': 2, 492 'address': { 493 'type': 0, 'company': 0, 'street': 0, 'street2': 0, 494 'city': 0, 'state': 0, 'postalcode': 0, 'country': 0, 495 'details': 0 }, 496 'url': 0, 497 'memo': 0, 498 'category': 1, 499 'wallpaper': 1, 500 'ringtone': 2, 501 'storage': 0, 502 }, 503 'calendar': { 504 'description': True, 'location': False, 'allday': False, 505 'start': True, 'end': True, 'priority': False, 506 'alarm': True, 'vibrate': True, 507 'repeat': True, 508 'memo': False, 509 'category': False, 510 'wallpaper': False, 511 'ringtone': True, 512 }, 513 'memo': { 514 'subject': True, 515 'date': True, 516 'secret': False, 517 'category': False, 518 'memo': True, 519 }, 520 'todo': { 521 'summary': False, 522 'status': False, 523 'due_date': False, 524 'percent_complete': False, 525 'completion_date': False, 526 'private': False, 527 'priority': False, 528 'category': False, 529 'memo': False, 530 }, 531 }
532