0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2005 Joe Pham <djpham@bitpim.org> 0004 ### 0005 ### This program is free software; you can redistribute it and/or modify 0006 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0007 ### 0008 ### $Id: com_lgvx4650.py 4370 2007-08-21 21:39:36Z djpham $ 0009 0010 """Communicate with the LG VX4650 cell phone 0011 0012 The VX4600 is substantially similar to the VX4400. 0013 0014 """ 0015 0016 # standard modules 0017 from __future__ import with_statement 0018 import contextlib 0019 import datetime 0020 import time 0021 import cStringIO 0022 import re 0023 import sha 0024 0025 # my modules 0026 import bpcalendar 0027 import call_history 0028 import common 0029 import conversions 0030 import copy 0031 import p_brew 0032 import p_lgvx4650 0033 import com_lgvx4400 0034 import com_brew 0035 import com_phone 0036 import com_lg 0037 import commport 0038 import helpids 0039 import memo 0040 import prototypes 0041 import sms 0042 0043 class Phone(com_lgvx4400.Phone): 0044 "Talk to the LG VX4650 cell phone" 0045 0046 desc="LG-VX4650" 0047 helpid=helpids.ID_PHONE_LGVX4650 0048 protocolclass=p_lgvx4650 0049 serialsname='lgvx4650' 0050 0051 # 4650 index files 0052 imagelocations=( 0053 # offset, index file, files location, type, maximumentries 0054 ( 30, "download/dloadindex/brewImageIndex.map", "dload/img", "images", 30), 0055 ) 0056 0057 ringtonelocations=( 0058 # offset, index file, files location, type, maximumentries 0059 ( 50, "download/dloadindex/brewRingerIndex.map", "user/sound/ringer", "ringers", 30), 0060 ) 0061 0062 builtinimages={ 80: ('Large Pic. 1', 'Large Pic. 2', 'Large Pic. 3', 0063 'Large Pic. 4', 'Large Pic. 5', 'Large Pic. 6', 0064 'Large Pic. 7', 'Large Pic. 8', 'Large Pic. 9', 0065 'Large Pic. 10', 'Large Pic. 11', 'Large Pic. 12', 0066 'Large Pic. 13', 'Large Pic. 14', 'Large Pic. 15', 0067 'Large Pic. 16', 'Large Pic. 17', 'Large Pic. 18', 0068 'Large Pic. 19', 'Large Pic. 20', 'Large Pic. 21', 0069 'Large Pic. 22', 'Large Pic. 23', 'Large Pic. 24', 0070 'Large Pic. 25', 'Large Pic. 26', 'Large Pic. 27', 0071 'Large Pic. 28', 'Large Pic. 29', 'Large Pic. 30', 0072 'Large Pic. 31', 'Large Pic. 32', 'Large Pic. 33' ) } 0073 0074 builtinringtones={ 1: ('Ring 1', 'Ring 2', 'Ring 3', 'Ring 4', 'Ring 5', 0075 'VZW Default Tone', 'Arabesque', 'Piano Sonata', 0076 'Latin', 'When the saints go', 'Bach Cello suite', 0077 'Speedy Way', 'CanCan', 'Grand Waltz', 'Toccata and Fugue', 0078 'Bumble Bee', 'March', 'Circus Band', 0079 'Sky Garden', 'Carmen Habanera', 'Hallelujah', 0080 'Sting', 'Farewell', 'Pachelbel Canon', 'Carol 1', 0081 'Carol 2'), # 'Vibrate', 'Lamp' ), 0082 100: ( 'Chimes high', 'Chimes low', 'Ding', 'TaDa', 0083 'Notify', 'Drum', 'Claps', 'FanFare', 'Chord high', 0084 'Chord low' ) 0085 } 0086 VoiceMemoDir='VoiceDB/All/Memos' 0087 0088 def __init__(self, logtarget, commport): 0089 com_lgvx4400.Phone.__init__(self, logtarget, commport) 0090 self.mode=self.MODENONE 0091 0092 def getfirmwareinformation(self): 0093 self.log("Getting firmware information") 0094 req=self.protocolclass.firmwarerequest() 0095 res=self.sendbrewcommand(req, self.protocolclass.firmwareresponse) 0096 return res 0097 0098 def getmediaindex(self, builtins, maps, results, key): 0099 """Gets the media (wallpaper/ringtone) index 0100 0101 @param builtins: the builtin list on the phone 0102 @param results: places results in this dict 0103 @param maps: the list of index files and locations 0104 @param key: key to place results in 0105 """ 0106 media=com_lgvx4400.Phone.getmediaindex(self, (), maps, results, key) 0107 0108 # builtins 0109 for k,e in builtins.items(): 0110 c=k 0111 for name in e: 0112 media[c]={ 'name': name, 'origin': 'builtin' } 0113 c+=1 0114 # voice memos index 0115 if key=='ringtone-index': 0116 try: 0117 vmemo_files=self.listfiles(self.VoiceMemoDir) 0118 keys=vmemo_files.keys() 0119 keys.sort() 0120 _idx_cnt=210 0121 for k in keys: 0122 if k.endswith('.qcp'): 0123 num_str=k[-8:-4] 0124 media[_idx_cnt]={ 'name': 'VoiceMemo'+num_str, 0125 'origin': 'voicememo' } 0126 _idx_cnt+=1 0127 except: 0128 if __debug__: 0129 raise 0130 return media 0131 0132 # Ringtone stuff------------------------------------------------------------ 0133 def getringtones(self, result): 0134 result=com_lgvx4400.Phone.getringtones(self, result) 0135 if not conversions.helperavailable('pvconv'): 0136 return result 0137 media=result['ringtone'] 0138 # get& convert the voice memo files 0139 with contextlib.nested(common.usetempfile('qcp'), 0140 common.usetempfile('wav')) as (_qcp_file, _wav_file): 0141 try: 0142 vmemo_files=self.listfiles(self.VoiceMemoDir) 0143 keys=vmemo_files.keys() 0144 for k in keys: 0145 if k.endswith('.qcp'): 0146 key_name='VoiceMemo'+k[-8:-4] 0147 file(_qcp_file, 'wb').write(self.getfilecontents(k, True)) 0148 conversions.convertqcptowav(_qcp_file, _wav_file) 0149 media[key_name]=file(_wav_file, 'rb').read() 0150 except: 0151 if __debug__: 0152 raise 0153 result['ringtone']=media 0154 return result 0155 0156 def saveringtones(self, results, merge): 0157 _new_ringtones=results.get('ringtone', {}) 0158 _rt_index=results.get('ringtone-index', {}) 0159 # list of voicememo names 0160 _voice_memo_l=[x['name'] for k,x in _rt_index.items() \ 0161 if x.get('origin', '')=='voicememo'] 0162 # list of media to delete 0163 _del_keys=[k for k,x in _new_ringtones.items() \ 0164 if x.get('name', None) in _voice_memo_l] 0165 for k in _del_keys: 0166 del _new_ringtones[k] 0167 results['ringtone']=_new_ringtones 0168 return com_lgvx4400.Phone.saveringtones(self, results, merge) 0169 0170 # Phonebook stuff----------------------------------------------------------- 0171 def savephonebook(self, data): 0172 "Saves out the phonebook" 0173 res=com_lgvx4400.Phone.savephonebook(self, data) 0174 # build a dict to manually update the wp index 0175 pbook=res.get('phonebook', {}) 0176 wallpaper_index=res.get('wallpaper-index', {}) 0177 r1={} 0178 for k,e in pbook.items(): 0179 r1[e['bitpimserial']['id']]={ 'wallpaper': \ 0180 self._findmediainindex(wallpaper_index, 0181 e['wallpaper'], 0182 e['name'], 0183 'wallpaper'), 0184 'group': e['group'] } 0185 serialupdates=data.get("serialupdates", []) 0186 r2={} 0187 for bps, serials in serialupdates: 0188 r2[serials['serial1']]=r1[bps['id']] 0189 if self._update_wallpaper_index(r2): 0190 data["rebootphone"]=True 0191 return res 0192 0193 def _update_wallpaper_index(self, wpi): 0194 # manually update wallpaper indices since the normal update process 0195 # does not seem to work 0196 buf=prototypes.buffer(self.getfilecontents( 0197 self.protocolclass.pb_file_name)) 0198 pb=self.protocolclass.pbfile() 0199 pb.readfrombuffer(buf, logtitle="Read "+self.protocolclass.pb_file_name) 0200 update_flg=False 0201 for e in pb.items: 0202 _info=wpi.get(e.serial1, None) 0203 if _info: 0204 wp=_info.get('wallpaper', None) 0205 if wp is not None and wp!=e.wallpaper: 0206 update_flg=True 0207 e.wallpaper=wp 0208 gr=_info.get('group', None) 0209 if gr is not None and gr!=e.group: 0210 update_flg=True 0211 e.group=gr 0212 if update_flg: 0213 self.log('Updating wallpaper index') 0214 buf=prototypes.buffer() 0215 pb.writetobuffer(buf, logtitle="Updated index "+self.protocolclass.pb_file_name) 0216 self.writefile(self.protocolclass.pb_file_name, buf.getvalue()) 0217 return update_flg 0218 0219 # Calendar stuff------------------------------------------------------------ 0220 # all taken care by the VX4400 0221 0222 # Text Memo stuff----------------------------------------------------------- 0223 # all taken care by the VX4400 0224 0225 # Call History stuff-------------------------------------------------------- 0226 _call_history_info={ 0227 call_history.CallHistoryEntry.Folder_Incoming: protocolclass.incoming_call_file, 0228 call_history.CallHistoryEntry.Folder_Outgoing: protocolclass.outgoing_call_file, 0229 call_history.CallHistoryEntry.Folder_Missed: protocolclass.missed_call_file 0230 } 0231 def getcallhistory(self, result): 0232 # read the call history files 0233 res={} 0234 for _folder, _file_name in Phone._call_history_info.items(): 0235 try: 0236 buf=prototypes.buffer(self.getfilecontents(_file_name)) 0237 hist_file=self.protocolclass.callhistoryfile() 0238 hist_file.readfrombuffer(buf, logtitle="Read call history") 0239 for i in range(hist_file.itemcount): 0240 hist_call=hist_file.items[i] 0241 entry=call_history.CallHistoryEntry() 0242 entry.folder=_folder 0243 entry.datetime=hist_call.datetime 0244 entry.number=hist_call.number 0245 entry.name=hist_call.name 0246 if _folder!=call_history.CallHistoryEntry.Folder_Missed: 0247 entry.duration=hist_call.duration 0248 res[entry.id]=entry 0249 except com_brew.BrewNoSuchFileException: 0250 pass 0251 result['call_history']=res 0252 return result 0253 0254 # SMS stuff----------------------------------------------------------------- 0255 def _setquicktext(self, result): 0256 canned_file=Phone.SMSCannedFile() 0257 canned_file.set_sms_canned_data(result.get('canned_msg', [])) 0258 buf=prototypes.buffer() 0259 canned_file.writetobuffer(buf, logtitle="Updated "+self.protocolclass.sms_canned_file) 0260 self.writefile(self.protocolclass.sms_canned_file, buf.getvalue()) 0261 0262 def _getquicktext(self): 0263 try: 0264 buf=prototypes.buffer(self.getfilecontents( 0265 self.protocolclass.sms_canned_file)) 0266 canned_file=Phone.SMSCannedFile() 0267 canned_file.readfrombuffer(buf, logtitle="Read SMS canned text") 0268 return canned_file.get_sms_canned_data() 0269 except: 0270 if __debug__: 0271 raise 0272 return [] 0273 0274 my_model='VX4650' 0275 0276 class SMSCannedFile(protocolclass.SMSCannedFile): 0277 def __init__(self, *args, **kwargs): 0278 Phone.protocolclass.SMSCannedFile.__init__(self, *args, **kwargs) 0279 0280 def get_sms_canned_data(self): 0281 return [{ 'text': e.text, 0282 'type': sms.CannedMsgEntry.user_type } for e in self.items] 0283 0284 def set_sms_canned_data(self, canned_list): 0285 msg_lst=[x['text'] for x in canned_list \ 0286 if x['type']==sms.CannedMsgEntry.user_type] 0287 item_count=min(Phone.protocolclass.SMS_CANNED_MAX_ITEMS, len(msg_lst)) 0288 for i in range(item_count): 0289 entry=Phone.protocolclass.SMSCannedMsg() 0290 entry.text=msg_lst[i] 0291 self.items.append(entry) 0292 entry=Phone.protocolclass.SMSCannedMsg() 0293 entry.text='' 0294 for i in range(item_count, Phone.protocolclass.SMS_CANNED_MAX_ITEMS): 0295 self.items.append(entry) 0296 0297 # Phone Info stuff---------------------------------------------------------- 0298 def _get_phone_number(self): 0299 # return the phone number of this phone 0300 s='' 0301 try: 0302 buf=self.getfilecontents('nvm/nvm/nvm_0000') 0303 ofs=0x240 0304 if buf[ofs]=='\x01': 0305 ofs+=1 0306 while buf[ofs]!='\x01': 0307 s+=buf[ofs] 0308 ofs+=1 0309 except: 0310 if __debug__: 0311 raise 0312 return s 0313 def getphoneinfo(self, phone_info): 0314 # returning some basic phone info 0315 # double check if this's the right phone. 0316 try: 0317 if self.getfilecontents(self.brew_version_file)[:len(self.my_model)]==self.my_model: 0318 phone_info.model=self.my_model 0319 phone_info.manufacturer=Profile.phone_manufacturer 0320 phone_info.phone_number=self._get_phone_number() 0321 phone_info.firmware_version=self.getfirmwareinformation().firmwareversion 0322 phone_info.esn=self.get_esn() 0323 except: 0324 if __debug__: 0325 raise 0326 0327 #------------------------------------------------------------------------------ 0328 parentprofile=com_lgvx4400.Profile 0329 class Profile(parentprofile): 0330 protocolclass=Phone.protocolclass 0331 serialsname=Phone.serialsname 0332 0333 WALLPAPER_WIDTH=128 0334 WALLPAPER_HEIGHT=128 0335 MAX_WALLPAPER_BASENAME_LENGTH=19 0336 WALLPAPER_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ." 0337 WALLPAPER_CONVERT_FORMAT="bmp" 0338 0339 MAX_RINGTONE_BASENAME_LENGTH=19 0340 RINGTONE_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ." 0341 0342 BP_Calendar_Version=3 0343 phone_manufacturer='LG Electronics Inc' 0344 phone_model='VX4650' 0345 0346 imageorigins={} 0347 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images")) 0348 def GetImageOrigins(self): 0349 return self.imageorigins 0350 0351 # our targets are the same for all origins 0352 imagetargets={} 0353 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", 0354 {'width': 128, 'height': 114, 'format': "JPEG"})) 0355 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "fullscreen", 0356 {'width': 128, 'height': 128, 'format': "JPEG"})) 0357 0358 def GetTargetsForImageOrigin(self, origin): 0359 return self.imagetargets 0360 0361 _supportedsyncs=( 0362 ('phonebook', 'read', None), # all phonebook reading 0363 ('calendar', 'read', None), # all calendar reading 0364 ('wallpaper', 'read', None), # all wallpaper reading 0365 ('ringtone', 'read', None), # all ringtone reading 0366 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 0367 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 0368 ('wallpaper', 'write', 'MERGE'), # merge and overwrite wallpaper 0369 ('wallpaper', 'write', 'OVERWRITE'), 0370 ('ringtone', 'write', 'MERGE'), # merge and overwrite ringtone 0371 ('ringtone', 'write', 'OVERWRITE'), 0372 ('memo', 'read', None), # all memo list reading DJP 0373 ('memo', 'write', 'OVERWRITE'), # all memo list writing DJP 0374 ('call_history', 'read', None), 0375 ('sms', 'read', None), 0376 ('sms', 'write', 'OVERWRITE'), 0377 ) 0378 0379 def __init__(self): 0380 parentprofile.__init__(self) 0381
Generated by PyXR 0.9.4