0001 #!/usr/bin/env python 0002 0003 ### BITPIM 0004 ### 0005 ### Copyright (C) 2006 Joe Pham <djpham@bitpim.org> 0006 ### 0007 ### This program is free software; you can redistribute it and/or modify 0008 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0009 ### 0010 ### $Id: com_lgvx8500.py 4305 2007-07-16 04:05:25Z djpham $ 0011 0012 """ 0013 Communicate with the LG VX8500 cell phone 0014 """ 0015 # standard modules 0016 import datetime 0017 0018 # wx modules 0019 0020 # BitPim modules 0021 import common 0022 import com_brew 0023 import com_lg 0024 import com_lgvx8300 0025 import helpids 0026 import fileinfo 0027 import guihelper 0028 import os 0029 import p_brew 0030 import p_lgvx8500 0031 import playlist 0032 import prototypes 0033 import prototypeslg 0034 import t9editor 0035 0036 parentphone=com_lgvx8300.Phone 0037 class Phone(parentphone): 0038 desc="LG-VX8500" 0039 helpid=helpids.ID_PHONE_LGVX8500 0040 0041 protocolclass=p_lgvx8500 0042 serialsname='lgvx8500' 0043 0044 my_model='VX8500' 0045 builtinringtones= ('Low Beep Once', 'Low Beeps', 'Loud Beep Once', 'Loud Beeps', 'VZW Default Ringtone') + \ 0046 tuple(['Ringtone '+`n` for n in range(1,13)]) + \ 0047 ('No Ring',) 0048 0049 ringtonelocations= ( 0050 # type index file default dir external dir max type Index 0051 ( 'ringers', 'dload/myringtone.dat','brew/16452/lk/mr','mmc1/ringers', 100, 0x01, 100), 0052 ( 'sounds', 'dload/mysound.dat', 'brew/16452/ms', '', 100, 0x02, None), 0053 ( 'sounds(sd)', 'dload/sd_sound.dat', 'mmc1/my_sounds', '', 100, 0x02, None), 0054 ( 'music', 'dload/efs_music.dat', 'my_music', '', 100, 0x104, None), 0055 ( 'music(sd)', 'dload/sd_music.dat', 'mmc1/my_music', '', 100, 0x14, None), 0056 ) 0057 0058 wallpaperlocations= ( 0059 # type index file default dir external dir max type Index 0060 ( 'images', 'dload/image.dat', 'brew/16452/mp', '', 100, 0x00, 100), 0061 ( 'images(sd)', 'dload/sd_image.dat', 'mmc1/my_pix', '', 100, 0x10, None), 0062 ( 'video', 'dload/video.dat', 'brew/16452/mf', '', 100, 0x03, None), 0063 ( 'video(sd)', 'dload/sd_video.dat', 'mmc1/my_flix', '', 100, 0x13, None), 0064 ) 0065 0066 def setDMversion(self): 0067 """Define the DM version required for this phone, default to DMv5""" 0068 _fw_version=self.get_firmware_version()[-1] 0069 self._DMv5=self.my_model=='VX8500' and _fw_version>'4' 0070 0071 # Fundamentals: 0072 # - get_esn - same as LG VX-8300 0073 # - getgroups - same as LG VX-8100 0074 # - getwallpaperindices - LGUncountedIndexedMedia 0075 # - getrintoneindices - LGUncountedIndexedMedia 0076 # - DM Version - T85VZV01 - T85VZV04: 4, T85VZV05 - : 5 0077 0078 # Phonebook stuff----------------------------------------------------------- 0079 def _build_pb_info(self, fundamentals): 0080 # build a dict of info to update pbentry 0081 pbook=fundamentals.get('phonebook', {}) 0082 wallpaper_index=fundamentals.get('wallpaper-index', {}) 0083 ringtone_index=fundamentals.get('ringtone-index', {}) 0084 r1={} 0085 for k,e in pbook.items(): 0086 r1[e['bitpimserial']['id']]={ 'wallpaper': \ 0087 self._findmediainindex(wallpaper_index, 0088 e['wallpaper'], 0089 e['name'], 0090 'wallpaper'), 0091 'msgringtone': \ 0092 self._findmediainindex(ringtone_index, 0093 e['msgringtone'], 0094 e['name'], 0095 'message ringtone')} 0096 serialupdates=fundamentals.get("serialupdates", []) 0097 r2={} 0098 for bps, serials in serialupdates: 0099 r2[serials['serial1']]=r1[bps['id']] 0100 return r2 0101 def _update_pb_file(self, pb, fundamentals, pbinfo): 0102 # update the pbentry file 0103 update_flg=False 0104 for e in pb.items: 0105 _info=pbinfo.get(e.serial1, None) 0106 if _info: 0107 wp=_info.get('wallpaper', None) 0108 if wp is not None and wp!=e.wallpaper: 0109 update_flg=True 0110 e.wallpaper=wp 0111 msgrt=_info.get('msgringtone', None) 0112 if msgrt is not None and msgrt!=e.msgringtone: 0113 update_flg=True 0114 e.msgringtone=msgrt 0115 if update_flg: 0116 self.log('Updating wallpaper index') 0117 buf=prototypes.buffer() 0118 pb.writetobuffer(buf, logtitle="Updated index "+self.protocolclass.pb_file_name) 0119 self.writefile(self.protocolclass.pb_file_name, buf.getvalue()) 0120 def _update_pb_info(self, pbentries, fundamentals): 0121 # Manually update phonebook data that the normal protocol should have 0122 _pbinfo=self._build_pb_info(fundamentals) 0123 self._update_pb_file(pbentries, fundamentals, _pbinfo) 0124 def _write_path_index(self, pbentries, pbmediakey, media_index, 0125 index_file, invalid_values): 0126 _path_entry=self.protocolclass.PathIndexEntry() 0127 _path_file=self.protocolclass.PathIndexFile() 0128 for _ in range(self.protocolclass.NUMPHONEBOOKENTRIES): 0129 _path_file.items.append(_path_entry) 0130 for _entry in pbentries.items: 0131 _idx=getattr(_entry, pbmediakey) 0132 if _idx in invalid_values or not media_index.has_key(_idx): 0133 continue 0134 if media_index[_idx].get('origin', None)=='builtin': 0135 _filename=media_index[_idx]['name'] 0136 elif media_index[_idx].get('filename', None): 0137 _filename=media_index[_idx]['filename'] 0138 else: 0139 continue 0140 _path_file.items[_entry.entrynumber]=self.protocolclass.PathIndexEntry( 0141 pathname=_filename) 0142 _buf=prototypes.buffer() 0143 _path_file.writetobuffer(_buf, logtitle='Writing Path ID') 0144 self.writefile(index_file, _buf.getvalue()) 0145 0146 def savephonebook(self, data): 0147 "Saves out the phonebook" 0148 res=parentphone.savephonebook(self, data) 0149 # retrieve the phonebook entries 0150 _buf=prototypes.buffer(self.getfilecontents( 0151 self.protocolclass.pb_file_name)) 0152 _pb_entries=self.protocolclass.pbfile() 0153 _pb_entries.readfrombuffer(_buf, logtitle="Read phonebook file "+self.protocolclass.pb_file_name) 0154 _rt_index=data.get('ringtone-index', {}) 0155 _wp_index=data.get('wallpaper-index', {}) 0156 # update info that the phone software failed to do!! 0157 self._update_pb_info(_pb_entries, data) 0158 # fix up ringtone index 0159 self._write_path_index(_pb_entries, 'ringtone', 0160 _rt_index, 0161 self.protocolclass.RTPathIndexFile, 0162 (0xffff,)) 0163 # fix up msg ringtone index 0164 self._write_path_index(_pb_entries, 'msgringtone', 0165 _rt_index, 0166 self.protocolclass.MsgRTIndexFile, 0167 (0xffff,)) 0168 # fix up wallpaer index 0169 self._write_path_index(_pb_entries, 'wallpaper', 0170 _wp_index, 0171 self.protocolclass.WPPathIndexFile, 0172 (0, 0xffff,)) 0173 return res 0174 0175 # Playlist stuff------------------------------------------------------------ 0176 def _get_all_songs(self, fundamentals): 0177 # return a list of all available songs 0178 _rt_index=fundamentals.get('ringtone-index', {}) 0179 return [x['name'] for _,x in _rt_index.items() \ 0180 if x.get('origin', None) in ('music', 'music(sd)') ] 0181 def _read_pl_list(self, filename): 0182 # read and return the contents of the specified playlist 0183 _req=self.protocolclass.PLPlayListFile() 0184 _buf=prototypes.buffer(self.getfilecontents(filename)) 0185 _req.readfrombuffer(_buf, logtitle="Read playlist "+filename) 0186 _songs=[] 0187 for _item in _req.items: 0188 _songs.append(common.basename(_item.pathname)) 0189 _entry=playlist.PlaylistEntry() 0190 _entry.name=common.stripext(common.basename(filename)) 0191 _entry.songs=_songs 0192 return _entry 0193 def getplaylist(self, result): 0194 # return a list of all available songs and playlists 0195 # first, retrieve the list of all songs 0196 result[playlist.masterlist_key]=self._get_all_songs(result) 0197 # get a list of playlists and their contents 0198 _pl_list=[] 0199 try: 0200 _req=self.protocolclass.PLIndexFile() 0201 _buf=prototypes.buffer( 0202 self.getfilecontents(self.protocolclass.PLIndexFileName)) 0203 _req.readfrombuffer(_buf, logtitle='Reading Playlist Index') 0204 for _item in _req.items: 0205 try: 0206 _pl_list.append(self._read_pl_list(_item.pathname)) 0207 except com_brew.BrewNoSuchFileException: 0208 pass 0209 except: 0210 if __debug__: 0211 raise 0212 except com_brew.BrewNoSuchFileException: 0213 pass 0214 except: 0215 if __debug__: 0216 raise 0217 result[playlist.playlist_key]=_pl_list 0218 return result 0219 0220 def _get_info_from_index(self, filename): 0221 _res={} 0222 try: 0223 _buf=prototypes.buffer( 0224 self.getfilecontents(filename)) 0225 _req=self.protocolclass.indexfile() 0226 _req.readfrombuffer(_buf, logtitle='Reading Index File') 0227 for _item in _req.items: 0228 _res[common.basename(_item.filename)]={ 'size': _item.size, 0229 'filename': _item.filename } 0230 except com_brew.BrewNoSuchFileException: 0231 pass 0232 except: 0233 if __debug__: 0234 raise 0235 return _res 0236 def _get_song_info(self): 0237 # return a dict of all songs & their info 0238 _res=self._get_info_from_index('dload/efs_music.dat') 0239 _res.update(self._get_info_from_index('dload/sd_music.dat')) 0240 return _res 0241 def _write_a_playlist(self, filename, pl, song_info): 0242 # write a single playlist 0243 _pl_file=self.protocolclass.PLPlayListFile() 0244 _dt=datetime.datetime.now() 0245 _onesec=datetime.timedelta(seconds=1) 0246 _plsize=0 0247 for _song in pl.songs: 0248 if song_info.has_key(_song): 0249 _dt-=_onesec 0250 _entry=self.protocolclass.PLSongEntry( 0251 pathname=song_info[_song]['filename'], 0252 date=_dt.timetuple()[:6], 0253 size=song_info[_song]['size']) 0254 _pl_file.items.append(_entry) 0255 _plsize+=1 0256 if _plsize>=self.protocolclass.PLMaxSize: 0257 # reached max size 0258 break 0259 _buf=prototypes.buffer() 0260 _pl_file.writetobuffer(_buf, 0261 logtitle='Writing Playlist '+pl.name) 0262 self.writefile(filename, _buf.getvalue()) 0263 def _write_playlists(self, playlists, song_info): 0264 _pl_index=self.protocolclass.PLIndexFile() 0265 for _pl in playlists: 0266 try: 0267 _filename=self.protocolclass.PLFilePath+'/'+_pl.name+\ 0268 self.protocolclass.PLExt 0269 self._write_a_playlist(_filename, _pl, song_info) 0270 _pl_index.items.append( 0271 self.protocolclass.PLIndexEntry(pathname=_filename)) 0272 except: 0273 self.log('Failed to write playlist '+_pl.name) 0274 if __debug__: 0275 raise 0276 try: 0277 _buf=prototypes.buffer() 0278 _pl_index.writetobuffer(_buf, 0279 logtitle='Writing Playlist Index') 0280 self.writefile(self.protocolclass.PLIndexFileName, 0281 _buf.getvalue()) 0282 except: 0283 self.log('Failed to write Playlist Index file') 0284 if __debug__: 0285 raise 0286 def saveplaylist(self, result, merge): 0287 # delete all existing playlists 0288 _buf=prototypes.buffer(self.getfilecontents( 0289 self.protocolclass.PLIndexFileName)) 0290 _req=self.protocolclass.PLIndexFile() 0291 _req.readfrombuffer(_buf, logtitle='Reading Playlist Index') 0292 for _item in _req.items: 0293 try: 0294 self.rmfile(_item.pathname) 0295 except com_brew. BrewNoSuchFileException: 0296 pass 0297 except: 0298 if __debug__: 0299 raise 0300 # get info on all songs 0301 _song_info=self._get_song_info() 0302 # update the new playlists 0303 self._write_playlists(result.get(playlist.playlist_key, []), 0304 _song_info) 0305 return result 0306 0307 # T9 DB stuff 0308 def _get_current_db(self): 0309 _current_db=None 0310 try: 0311 _data=self.getfilecontents( 0312 self.protocolclass.T9USERDBFILENAME) 0313 if _data: 0314 _current_db=self.protocolclass.t9udbfile() 0315 _current_db.readfrombuffer(prototypes.buffer(_data), 0316 logtitle='Reading T9 User DB File') 0317 except com_brew.BrewNoSuchFileException: 0318 pass 0319 return _current_db 0320 def _copy_fields(self, old, new): 0321 for _field in ('unknown1', 'unknown2', 'unknown3', 'unknown4'): 0322 setattr(new, _field, getattr(old, _field)) 0323 def gett9db(self, result): 0324 _req=self._get_current_db() 0325 _t9list=t9editor.T9WordsList() 0326 if _req: 0327 for _item in _req.blocks: 0328 _blk=_item.block 0329 if _blk['type']==prototypeslg.T9USERDBBLOCK.WordsList_Type: 0330 for _word in _blk['value']: 0331 _t9list.append_word(_word['word']) 0332 result[t9editor.dict_key]=_t9list 0333 return result 0334 _buffer_data={ 0335 '1': 0x7FA, '2': 0x7FA, '3': 0x7FA, '4': 0x7FA, 0336 '5': 0x7FA, '6': 0x7FA, '7': 0x7FA, '8': 0x7FA, 0337 '9': 0x7FA, '0': 0x800 } 0338 _t9keys=('1', '2', '3', '4', '5', '6', '7', '8', '9', '0') 0339 def savet9db(self, result, _): 0340 _new_db=self.protocolclass.t9udbfile() 0341 try: 0342 _current_db=self._get_current_db() 0343 if _current_db: 0344 self._copy_fields(_current_db, _new_db) 0345 except: 0346 _current_db=None 0347 _t9words={} 0348 _t9list=result.get(t9editor.dict_key, t9editor.T9WordsList()) 0349 _wordcount=0 0350 for _key in _t9list.keys: 0351 for _word in _t9list.get_words(_key): 0352 _t9words.setdefault(_key[0], []).append(_word) 0353 _wordcount+=1 0354 _new_db.word_count=_wordcount 0355 _total_free_size=0 0356 for _key in self._t9keys: 0357 _lst=_t9words.get(_key, []) 0358 if _lst: 0359 _val={ 'type': prototypeslg.T9USERDBBLOCK.WordsList_Type, 0360 'value': [ { 'word': x } for x in _lst ] } 0361 _blk=prototypeslg.T9USERDBBLOCK(_val) 0362 _free_len=self._buffer_data[_key]-_blk.packetsize() 0363 # ugly hack! 0364 if _key!='1': 0365 _free_len+=1 0366 _total_free_size+=_free_len 0367 _new_db.blocks.append(_val) 0368 _new_db.blocks.append({ 'type': prototypeslg.T9USERDBBLOCK.FreeBlock_Type, 0369 'value': _free_len }) 0370 else: 0371 if _key!='1': 0372 _new_db.blocks.append({ 'type': prototypeslg.T9USERDBBLOCK.A0_Type, 0373 'value': 0xA0 }) 0374 _type=prototypeslg.T9USERDBBLOCK.FreeBlock_Type 0375 _new_db.blocks.append({ 'type': _type, 0376 'value': self._buffer_data[_key] }) 0377 _total_free_size+=self._buffer_data[_key] 0378 _new_db.free_space=_total_free_size 0379 _buf=prototypes.buffer() 0380 _new_db.writetobuffer(_buf) 0381 self.writefile(self.protocolclass.T9USERDBFILENAME, 0382 _buf.getvalue()) 0383 # Need to reboot the phone to take effect 0384 result['rebootphone']=True 0385 return result 0386 0387 # Misc Stuff---------------------------------------------------------------- 0388 def get_firmware_version(self): 0389 # return the firmware version 0390 req=p_brew.firmwarerequest() 0391 res=self.sendbrewcommand(req, self.protocolclass.firmwareresponse) 0392 return res.firmware 0393 0394 #------------------------------------------------------------------------------- 0395 parentprofile=com_lgvx8300.Profile 0396 class Profile(parentprofile): 0397 protocolclass=Phone.protocolclass 0398 serialsname=Phone.serialsname 0399 0400 BP_Calendar_Version=3 0401 phone_manufacturer='LG Electronics Inc' 0402 phone_model='VX8500' 0403 0404 WALLPAPER_WIDTH=240 0405 WALLPAPER_HEIGHT=320 0406 MAX_WALLPAPER_BASENAME_LENGTH=32 0407 WALLPAPER_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_() ." 0408 WALLPAPER_CONVERT_FORMAT="jpg" 0409 0410 # the 8300 uses "W" for wait in the dialstring, it does not support "T" 0411 DIALSTRING_CHARS="[^0-9PW#*]" 0412 0413 MAX_RINGTONE_BASENAME_LENGTH=32 0414 RINGTONE_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_() ." 0415 RINGTONE_LIMITS= { 0416 'MAXSIZE': 200000 0417 } 0418 0419 imageorigins={} 0420 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images")) 0421 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "video")) 0422 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images(sd)")) 0423 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "video(sd)")) 0424 def GetImageOrigins(self): 0425 return self.imageorigins 0426 0427 ringtoneorigins=('ringers', 'sounds', 'sounds(sd)',' music', 'music(sd)') 0428 excluded_ringtone_origins=('sounds', 'sounds(sd)', 'music', 'music(sd)') 0429 0430 # our targets are the same for all origins 0431 imagetargets={} 0432 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", 0433 {'width': 240, 'height': 275, 'format': "JPEG"})) 0434 0435 def GetTargetsForImageOrigin(self, origin): 0436 return self.imagetargets 0437 0438 0439 def __init__(self): 0440 parentprofile.__init__(self) 0441 0442 def QueryAudio(self, origin, currentextension, afi): 0443 _max_size=self.RINGTONE_LIMITS['MAXSIZE'] 0444 setattr(afi, 'MAXSIZE', _max_size) 0445 # we don't modify any of these 0446 if afi.format in ("MIDI", "QCP", "PMD", "WMA"): 0447 return currentextension, afi 0448 # examine mp3 0449 if afi.format=="MP3": 0450 if afi.channels==1 and 8<=afi.bitrate<=64 and 16000<=afi.samplerate<=22050: 0451 return currentextension, afi 0452 # convert it 0453 return ("mp3", fileinfo.AudioFileInfo(afi, **{'format': 'MP3', 0454 'channels': 2, 0455 'bitrate': 48, 0456 'samplerate': 44100, 0457 'MAXSIZE': _max_size })) 0458 0459 _supportedsyncs=( 0460 ('phonebook', 'read', None), # all phonebook reading 0461 ('calendar', 'read', None), # all calendar reading 0462 ('wallpaper', 'read', None), # all wallpaper reading 0463 ('ringtone', 'read', None), # all ringtone reading 0464 ('call_history', 'read', None),# all call history list reading 0465 ('sms', 'read', None), # all SMS list reading 0466 ('memo', 'read', None), # all memo list reading 0467 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 0468 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 0469 ('wallpaper', 'write', 'MERGE'), # merge and overwrite wallpaper 0470 ('wallpaper', 'write', 'OVERWRITE'), 0471 ('ringtone', 'write', 'MERGE'), # merge and overwrite ringtone 0472 ('ringtone', 'write', 'OVERWRITE'), 0473 ('sms', 'write', 'OVERWRITE'), # all SMS list writing 0474 ('memo', 'write', 'OVERWRITE'), # all memo list writing 0475 ('playlist', 'read', 'OVERWRITE'), 0476 ('playlist', 'write', 'OVERWRITE'), 0477 ('t9_udb', 'write', 'OVERWRITE'), 0478 ) 0479 if __debug__: 0480 _supportedsyncs+=( 0481 ('t9_udb', 'read', 'OVERWRITE'), 0482 ) 0483 0484 field_color_data={ 0485 'phonebook': { 0486 'name': { 0487 'first': 1, 'middle': 1, 'last': 1, 'full': 1, 0488 'nickname': 0, 'details': 1 }, 0489 'number': { 0490 'type': 5, 'speeddial': 5, 'number': 5, 'details': 5 }, 0491 'email': 2, 0492 'address': { 0493 'type': 0, 'company': 0, 'street': 0, 'street2': 0, 0494 'city': 0, 'state': 0, 'postalcode': 0, 'country': 0, 0495 'details': 0 }, 0496 'url': 0, 0497 'memo': 0, 0498 'category': 1, 0499 'wallpaper': 1, 0500 'ringtone': 2, 0501 'storage': 0, 0502 }, 0503 'calendar': { 0504 'description': True, 'location': False, 'allday': False, 0505 'start': True, 'end': True, 'priority': False, 0506 'alarm': True, 'vibrate': True, 0507 'repeat': True, 0508 'memo': False, 0509 'category': False, 0510 'wallpaper': False, 0511 'ringtone': True, 0512 }, 0513 'memo': { 0514 'subject': True, 0515 'date': True, 0516 'secret': False, 0517 'category': False, 0518 'memo': True, 0519 }, 0520 'todo': { 0521 'summary': False, 0522 'status': False, 0523 'due_date': False, 0524 'percent_complete': False, 0525 'completion_date': False, 0526 'private': False, 0527 'priority': False, 0528 'category': False, 0529 'memo': False, 0530 }, 0531 } 0532
Generated by PyXR 0.9.4