0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2006 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_samsungscha950.py 4567 2008-01-14 02:59:46Z djpham $ 0009 0010 """Communicate with the Samsung SCH-A950 Phone""" 0011 0012 # System Models 0013 import calendar 0014 import datetime 0015 import sha 0016 import time 0017 0018 import wx 0019 0020 # BitPim modules 0021 import bpcalendar 0022 import call_history 0023 import common 0024 import commport 0025 import com_brew 0026 import com_phone 0027 import datetime 0028 import fileinfo 0029 import memo 0030 import nameparser 0031 import prototypes 0032 import pubsub 0033 import p_samsungscha950 0034 import sqlite2_file 0035 import sms 0036 import helpids 0037 0038 class Phone(com_phone.Phone, com_brew.BrewProtocol): 0039 desc='SCH-A950' 0040 helpid=helpids.ID_PHONE_SAMSUNGSCHA950 0041 protocolclass=p_samsungscha950 0042 serialsname='scha950' 0043 0044 ringtone_noring_range='range_tones_preloaded_el_15' 0045 ringtone_default_range='range_tones_preloaded_el_01' 0046 builtin_ringtones={ 0047 'VZW Default Tone': ringtone_default_range, 0048 'Melody 1': 'range_tones_preloaded_el_02', 0049 'Melody 2': 'range_tones_preloaded_el_03', 0050 'Bell 1': 'range_tones_preloaded_el_04', 0051 'Bell 2': 'range_tones_preloaded_el_05', 0052 'Beep Once': 'range_tones_preloaded_el_06', 0053 'No Ring': ringtone_noring_range, 0054 } 0055 builtin_sounds={ 0056 'Birthday': 'range_sound_preloaded_el_birthday', 0057 'Crowd Roar': 'range_sound_preloaded_el_crowed_roar', 0058 'Train': 'range_sound_preloaded_el_train', 0059 'Rainforest': 'range_sound_preloaded_el_rainforest', 0060 'Clapping': 'range_sound_preloaded_el_clapping', 0061 # same as ringtones ?? 0062 'Sound Beep Once': 'range_sound_preloaded_el_beep_once', 0063 'Sound No Ring': 'range_sound_preloaded_el_no_rings', 0064 } 0065 builtin_wallpapers={ 0066 'Wallpaper 1': 'range_f_wallpaper_preloaded_el_01', 0067 'Wallpaper 2': 'range_f_wallpaper_preloaded_el_02', 0068 'Wallpaper 3': 'range_f_wallpaper_preloaded_el_03', 0069 'Wallpaper 4': 'range_f_wallpaper_preloaded_el_04', 0070 'Wallpaper 5': 'range_f_wallpaper_preloaded_el_05', 0071 'Wallpaper 6': 'range_f_wallpaper_preloaded_el_06', 0072 'Wallpaper 7': 'range_f_wallpaper_preloaded_el_07', 0073 'Wallpaper 8': 'range_f_wallpaper_preloaded_el_08', 0074 'Wallpaper 9': 'range_f_wallpaper_preloaded_el_09', 0075 } 0076 builtin_groups={ 0077 1: 'Business', 0078 2: 'Colleague', 0079 3: 'Family', 0080 4: 'Friends' 0081 } 0082 0083 def __init__(self, logtarget, commport): 0084 "Calls all the constructors and sets initial modes" 0085 com_phone.Phone.__init__(self, logtarget, commport) 0086 com_brew.BrewProtocol.__init__(self) 0087 self.pbentryclass=PBEntry 0088 self.calendarclass=CalendarEntry 0089 # born to be in BREW mode! 0090 self.mode=self.MODEBREW 0091 0092 # common stuff 0093 def get_esn(self): 0094 if hasattr(self, '_fs_path'): 0095 # we're debugging, just return something 0096 return '12345678' 0097 _req=self.protocolclass.ESN_req() 0098 _resp=self.sendbrewcommand(_req, self.protocolclass.ESN_resp) 0099 return '%08X'%_resp.esn 0100 0101 def _time_now(self): 0102 return datetime.datetime.now().timetuple()[:5] 0103 0104 def get_groups(self): 0105 _res={ 0: { 'name': 'No Group' } } 0106 try: 0107 _file_name=None 0108 _path_name=self.protocolclass.GROUP_INDEX_FILE_NAME 0109 for i in range(256): 0110 _name='%s%d'%(_path_name, i) 0111 if self.statfile(_name): 0112 _file_name=_name 0113 break 0114 if not _file_name: 0115 return _res 0116 _buf=prototypes.buffer(self.getfilecontents(_file_name)) 0117 _index_file=self.protocolclass.GroupIndexFile() 0118 _index_file.readfrombuffer(_buf) 0119 for _entry in _index_file.items[1:]: 0120 if _entry.name: 0121 _res[_entry.index]={ 'name': _entry.name } 0122 elif self.builtin_groups.get(_entry.index, None): 0123 _res[_entry.index]={ 'name': self.builtin_groups[_entry.index] } 0124 except IndexError: 0125 pass 0126 except: 0127 if __debug__: 0128 raise 0129 return _res 0130 0131 def _get_builtin_ringtone_index(self, idx, result): 0132 for _entry in self.builtin_ringtones: 0133 result[idx]= { 'name': _entry, 0134 'origin': 'builtin', 0135 } 0136 idx+=1 0137 for _entry in self.builtin_sounds: 0138 result[idx]={ 'name': _entry, 0139 'origin': 'builtin', 0140 } 0141 idx+=1 0142 return idx 0143 def _get_file_ringtone_index(self, idx, result, 0144 index_file_name, index_file_class, 0145 origin): 0146 try: 0147 _buf=prototypes.buffer(self.getfilecontents(index_file_name)) 0148 except (com_brew.BrewNoSuchFileException, 0149 com_brew.BrewBadPathnameException, 0150 com_brew.BrewFileLockedException, 0151 com_brew.BrewAccessDeniedException): 0152 return idx 0153 except: 0154 if __debug__: 0155 raise 0156 return idx 0157 _index_file=index_file_class() 0158 _index_file.readfrombuffer(_buf) 0159 for _entry in _index_file.items: 0160 if _entry.pathname.startswith('/ff/'): 0161 _file_name=_entry.pathname[4:] 0162 else: 0163 _file_name=_entry.pathname 0164 result[idx]= { 'name': common.basename(_entry.pathname), 0165 'filename': _file_name, 0166 'origin': origin, 0167 } 0168 idx+=1 0169 return idx 0170 def get_ringtone_index(self): 0171 _res={} 0172 _idx=self._get_builtin_ringtone_index(0, _res) 0173 _idx=self._get_file_ringtone_index(_idx, _res, 0174 self.protocolclass.RT_INDEX_FILE_NAME, 0175 self.protocolclass.RRingtoneIndexFile, 0176 'ringers') 0177 _idx=self._get_file_ringtone_index(_idx, _res, 0178 self.protocolclass.SND_INDEX_FILE_NAME, 0179 self.protocolclass.RSoundsIndexFile, 0180 'sounds') 0181 return _res 0182 def _get_builtin_wallpaper_index(self, idx, result): 0183 for _entry in self.builtin_wallpapers: 0184 result[idx]={ 'name': _entry, 0185 'origin': 'builtin', 0186 } 0187 idx+=1 0188 return idx 0189 def _get_file_wallpaper_index(self, idx, result): 0190 try: 0191 _buf=prototypes.buffer(self.getfilecontents(self.protocolclass.PIC_INDEX_FILE_NAME)) 0192 except (com_brew.BrewNoSuchFileException, 0193 com_brew.BrewBadPathnameException, 0194 com_brew.BrewFileLockedException, 0195 com_brew.BrewAccessDeniedException): 0196 return idx 0197 except: 0198 if __debug__: 0199 raise 0200 return idx 0201 _index_file=self.protocolclass.RPictureIndexFile() 0202 _index_file.readfrombuffer(_buf) 0203 for _entry in _index_file.items[1:]: 0204 if _entry.pathname.startswith('/ff/'): 0205 _file_name=_entry.pathname[4:] 0206 else: 0207 _file_name=_entry.pathname 0208 result[idx]={ 'name': _entry.name, 0209 'filename': _file_name, 0210 'origin': 'images', 0211 } 0212 idx+=1 0213 return idx 0214 def get_wallpaper_index(self): 0215 _res={} 0216 _idx=self._get_file_wallpaper_index(0, _res) 0217 return _res 0218 def _read_ringtone_range(self, fundamentals): 0219 _res={} 0220 try: 0221 _data=self.getfilecontents(self.protocolclass.PREF_DB_FILE_NAME) 0222 _db=sqlite2_file.DBFile(_data) 0223 for _row in _db.get_table_data('dynamic_range_els'): 0224 _res[_row[2]]=_row[0] 0225 except: 0226 if __debug__: 0227 raise 0228 fundamentals['ringtone-range']=_res 0229 0230 def get_ringtone_range(self, name, fundamentals): 0231 if not name: 0232 # return No Rings 0233 return self.ringtone_default_range 0234 # check the builtin ringtones 0235 if self.builtin_ringtones.has_key(name): 0236 return self.builtin_ringtones[name] 0237 if self.builtin_sounds.has_key(name): 0238 return self.builtin_sounds[name] 0239 if not fundamentals.has_key('ringtone-range'): 0240 self._read_ringtone_range(fundamentals) 0241 _rt_range=fundamentals['ringtone-range'] 0242 return _rt_range.get(name, None) 0243 0244 def ringtone_name_from_range(self, range, fundamentals): 0245 # check for builtin ringtones 0246 for _key,_value in self.builtin_ringtones.items(): 0247 if range==_value: 0248 return _key 0249 # check for builtin sounds 0250 for _key,_value in self.builtin_sounds.items(): 0251 if range==_value: 0252 return _key 0253 # now check for the "custom" ones 0254 if not fundamentals.has_key('ringtone-range'): 0255 self._read_ringtone_range(fundamentals) 0256 for _key,_value in fundamentals['ringtone-range'].items(): 0257 if _value==range: 0258 return _key 0259 0260 def getfundamentals(self, results): 0261 """Gets information fundamental to interopating with the phone and UI. 0262 0263 Currently this is: 0264 0265 - 'uniqueserial' a unique serial number representing the phone 0266 - 'groups' the phonebook groups 0267 - 'wallpaper-index' map index numbers to names 0268 - 'ringtone-index' map index numbers to ringtone names 0269 0270 This method is called before we read the phonebook data or before we 0271 write phonebook data. 0272 """ 0273 0274 # use a hash of ESN and other stuff (being paranoid) 0275 self.log("Retrieving fundamental phone information") 0276 self.log("Phone serial number") 0277 results['uniqueserial']=sha.new(self.get_esn()).hexdigest() 0278 results['groups']=self.get_groups() 0279 results['ringtone-index']=self.get_ringtone_index() 0280 results['wallpaper-index']=self.get_wallpaper_index() 0281 return results 0282 0283 # Ringtone Stuff------------------------------------------------------------ 0284 def _get_media_from_index(self, index_key, media_key, 0285 fundamentals): 0286 _index=fundamentals.get(index_key, {}) 0287 _media={} 0288 for _key,_entry in _index.items(): 0289 if _entry.has_key('filename') and _entry['filename']: 0290 try: 0291 _media[_entry['name']]=self.getfilecontents(_entry['filename'], 0292 True) 0293 except: 0294 self.log('Failed to read file %s'%_entry['filename']) 0295 fundamentals[media_key]=_media 0296 return fundamentals 0297 0298 def getringtones(self, fundamentals): 0299 # reading ringers & sounds files 0300 return self._get_media_from_index('ringtone-index', 'ringtone', 0301 fundamentals) 0302 0303 def _get_del_new_list(self, index_key, media_key, merge, fundamentals, 0304 ignored_origins=()): 0305 """Return a list of media being deleted and being added""" 0306 _index=fundamentals.get(index_key, {}) 0307 _media=fundamentals.get(media_key, {}) 0308 _index_file_list=[_entry['name'] for _,_entry in _index.items() \ 0309 if _entry.has_key('filename') and \ 0310 _entry.get('origin', None) not in ignored_origins] 0311 _bp_file_list=[_entry['name'] for _,_entry in _media.items() \ 0312 if _entry.get('origin', None) not in ignored_origins] 0313 if merge: 0314 # just add the new files, don't delete anything 0315 _del_list=[] 0316 _new_list=_bp_file_list 0317 else: 0318 # Delete specified files and add everything 0319 _del_list=[x for x in _index_file_list if x not in _bp_file_list] 0320 _new_list=_bp_file_list 0321 return _del_list, _new_list 0322 0323 def _item_from_index(self, name, item_key, index_dict): 0324 for _key,_entry in index_dict.items(): 0325 if _entry.get('name', None)==name: 0326 if item_key: 0327 # return a field 0328 return _entry.get(item_key, None) 0329 else: 0330 # return the key 0331 return _key 0332 0333 def _del_files(self, index_key, _del_list, fundamentals): 0334 """Delete specified media files, need to be in OBEX mode""" 0335 _index=fundamentals.get(index_key, {}) 0336 for _file in _del_list: 0337 _file_name=self._item_from_index(_file, 'filename', _index) 0338 if _file_name: 0339 try: 0340 self.rmfile(_file_name) 0341 except Exception, e: 0342 self.log('Failed to delete file %s: %s'%(_file_name, str(e))) 0343 0344 def _replace_files(self, index_key, media_key, new_list, fundamentals): 0345 """Replace existing files with new contents using BREW""" 0346 _index=fundamentals.get(index_key, {}) 0347 _media=fundamentals.get(media_key, {}) 0348 for _file in new_list: 0349 _data=self._item_from_index(_file, 'data', _media) 0350 if not _data: 0351 self.log('Failed to write file %s due to no data'%_file) 0352 continue 0353 _file_name=self._item_from_index(_file, 'filename', _index) 0354 if _file_name: 0355 # existing file, check if the same one 0356 _stat=self.statfile(_file_name) 0357 if _stat and _stat['size']!=len(_data): 0358 # different size, replace it 0359 try: 0360 self.writefile(_file_name, _data) 0361 except: 0362 self.log('Failed to write BREW file '+_file_name) 0363 if __debug__: 0364 raise 0365 0366 def _add_files(self, index_key, media_key, 0367 new_list, fundamentals): 0368 """Add new file using BEW""" 0369 _index=fundamentals.get(index_key, {}) 0370 _media=fundamentals.get(media_key, {}) 0371 for _file in new_list: 0372 _data=self._item_from_index(_file, 'data', _media) 0373 if not _data: 0374 self.log('Failed to write file %s due to no data'%_file) 0375 continue 0376 if self._item_from_index(_file, None, _index) is None: 0377 # new file 0378 _origin=self._item_from_index(_file, 'origin', _media) 0379 if _origin=='ringers': 0380 _path=self.protocolclass.RT_PATH 0381 elif _origin=='sounds': 0382 _path=self.protocolclass.SND_PATH 0383 elif _origin=='images': 0384 _path=self.protocolclass.PIC_PATH 0385 else: 0386 selg.log('File %s has unknown origin, skip!'%_file) 0387 continue 0388 _file_name=_path+'/'+_file 0389 try: 0390 self.writefile(_file_name, _data) 0391 except: 0392 self.log('Failed to write file '+_file_name) 0393 if __debug__: 0394 raise 0395 0396 def _update_media_index(self, index_file_class, index_entry_class, 0397 media_path, excluded_files, 0398 index_file_name): 0399 # Update the index file 0400 _index_file=index_file_class() 0401 _filelists={} 0402 for _path in media_path: 0403 _filelists.update(self.listfiles(_path)) 0404 _files=_filelists.keys() 0405 _files.sort() 0406 for _f in _files: 0407 _file_name=common.basename(_f) 0408 if _file_name in excluded_files: 0409 # do not include this one 0410 continue 0411 _entry=index_entry_class() 0412 _entry.name=_file_name 0413 _entry.pathname=_f 0414 _index_file.items.append(_entry) 0415 _buf=prototypes.buffer() 0416 _index_file.writetobuffer(_buf) 0417 self.writefile(index_file_name, _buf.getvalue()) 0418 ## file(common.basename(index_file_name), 'wb').write(_buf.getvalue()) 0419 0420 def saveringtones(self, fundamentals, merge): 0421 """Save ringtones to the phone""" 0422 self.log('Writing ringtones to the phone') 0423 try: 0424 _del_list, _new_list=self._get_del_new_list('ringtone-index', 0425 'ringtone', 0426 merge, 0427 fundamentals) 0428 if __debug__: 0429 self.log('Delete list: '+','.join(_del_list)) 0430 self.log('New list: '+','.join(_new_list)) 0431 self._replace_files('ringtone-index', 'ringtone', 0432 _new_list, fundamentals) 0433 self._del_files('ringtone-index', 0434 _del_list, fundamentals) 0435 self._add_files('ringtone-index', 'ringtone', 0436 _new_list, fundamentals) 0437 self._update_media_index(self.protocolclass.WRingtoneIndexFile, 0438 self.protocolclass.WRingtoneIndexEntry, 0439 [self.protocolclass.RT_PATH, 0440 self.protocolclass.RT_PATH2], 0441 self.protocolclass.RT_EXCLUDED_FILES, 0442 self.protocolclass.RT_INDEX_FILE_NAME) 0443 self._update_media_index(self.protocolclass.WSoundsIndexFile, 0444 self.protocolclass.WSoundsIndexEntry, 0445 [self.protocolclass.SND_PATH, 0446 self.protocolclass.SND_PATH2], 0447 self.protocolclass.SND_EXCLUDED_FILES, 0448 self.protocolclass.SND_INDEX_FILE_NAME) 0449 fundamentals['rebootphone']=True 0450 except: 0451 if __debug__: 0452 raise 0453 return fundamentals 0454 0455 # Wallpaper stuff----------------------------------------------------------- 0456 def getwallpapers(self, fundamentals): 0457 # reading pictures & wallpapers 0458 return self._get_media_from_index('wallpaper-index', 'wallpapers', 0459 fundamentals) 0460 0461 def savewallpapers(self, fundamentals, merge): 0462 # send wallpapers to the phone 0463 """Save ringtones to the phone""" 0464 self.log('Writing wallpapers to the phone') 0465 try: 0466 _del_list, _new_list=self._get_del_new_list('wallpaper-index', 0467 'wallpapers', 0468 merge, 0469 fundamentals) 0470 if __debug__: 0471 self.log('Delete list: '+','.join(_del_list)) 0472 self.log('New list: '+','.join(_new_list)) 0473 self._replace_files('wallpaper-index', 'wallpapers', 0474 _new_list, fundamentals) 0475 self._del_files('wallpaper-index', 0476 _del_list, fundamentals) 0477 self._add_files('wallpaper-index', 'wallpapers', 0478 _new_list, fundamentals) 0479 self._update_media_index(self.protocolclass.WPictureIndexFile, 0480 self.protocolclass.WPictureIndexEntry, 0481 [self.protocolclass.PIC_PATH, 0482 self.protocolclass.PIC_PATH2], 0483 self.protocolclass.PIC_EXCLUDED_FILES, 0484 self.protocolclass.PIC_INDEX_FILE_NAME) 0485 fundamentals['rebootphone']=True 0486 except: 0487 if __debug__: 0488 raise 0489 return fundamentals 0490 0491 # Calendar stuff------------------------------------------------------------ 0492 def _read_calendar_index(self): 0493 _buf=prototypes.buffer(self.getfilecontents(self.protocolclass.CAL_INDEX_FILE_NAME)) 0494 _res=self.protocolclass.CalIndexFile() 0495 _res.readfrombuffer(_buf) 0496 return _res 0497 0498 def getcalendar(self, fundamentals): 0499 self.log('Reading calendar') 0500 _cal_index=self._read_calendar_index() 0501 _res={} 0502 for _cnt in range(_cal_index.numofevents): 0503 _cal_file_name='%s%04d'%(self.protocolclass.CAL_FILE_NAME_PREFIX, 0504 _cal_index.events[_cnt].index) 0505 _buf=prototypes.buffer(self.getfilecontents(_cal_file_name)) 0506 _bpcal=self.calendarclass(self, _buf, fundamentals).getvalue() 0507 _res[_bpcal.id]=_bpcal 0508 fundamentals['calendar']=_res 0509 return fundamentals 0510 0511 def _del_existing_cal_entries(self): 0512 self.log('Deleting existing calendar entries') 0513 _cal_index=self._read_calendar_index() 0514 for _idx in range(_cal_index.numofevents): 0515 _cal_file_name='%s%04d'%(self.protocolclass.CAL_FILE_NAME_PREFIX, 0516 _cal_index.events[_idx].index) 0517 try: 0518 self.rmfile(_cal_file_name) 0519 except: 0520 self.log('Failed to delete file: '+_cal_file_name) 0521 return _cal_index.next_index 0522 0523 def _write_cal_entries(self, next_index, fundamentals): 0524 # write each and every calendar entries, each in a separate file 0525 _cal_dict=fundamentals.get('calendar', {}) 0526 _idx=next_index 0527 _cnt=0 0528 for _key,_entry in _cal_dict.items(): 0529 if _cnt>=self.protocolclass.CAL_MAX_EVENTS: 0530 # enough events already! 0531 break 0532 try: 0533 _cal_entry=self.calendarclass(self, _entry, fundamentals) 0534 _buf=prototypes.buffer() 0535 _cal_entry.writetobuffer(_buf) 0536 _cal_file_name='%s%04d'%(self.protocolclass.CAL_FILE_NAME_PREFIX, 0537 _idx) 0538 self.writefile(_cal_file_name, _buf.getvalue()) 0539 _idx+=1 0540 _cnt+=1 0541 except: 0542 self.log('Failed to write calendar entry') 0543 if __debug__: 0544 raise 0545 return _idx 0546 0547 def _write_cal_index(self, next_index, fundamentals): 0548 _cal_index=self._read_calendar_index() 0549 # clear out the old entries 0550 for _idx in range(_cal_index.numofevents): 0551 _cal_index.events[_idx].index=0 0552 for _idx in range(_cal_index.numofactiveevents): 0553 _cal_index.activeevents[_idx].index=0 0554 # update with new info 0555 _old_next_index=_cal_index.next_index 0556 _num_entries=next_index-_old_next_index 0557 _cal_index.next_index=next_index 0558 _cal_index.numofevents=_num_entries 0559 _cal_index.numofactiveevents=_num_entries 0560 _cnt=0 0561 for _idx in range(_old_next_index, next_index): 0562 _cal_index.events[_cnt].index=_idx 0563 _cal_index.activeevents[_cnt].index=_idx 0564 _cnt+=1 0565 _buf=prototypes.buffer() 0566 _cal_index.writetobuffer(_buf) 0567 self.writefile(self.protocolclass.CAL_INDEX_FILE_NAME, 0568 _buf.getvalue()) 0569 0570 def savecalendar(self, fundamentals, merge): 0571 self.log("Sending calendar entries") 0572 _next_idx=self._del_existing_cal_entries() 0573 _next_idx=self._write_cal_entries(_next_idx, fundamentals) 0574 self._write_cal_index(_next_idx, fundamentals) 0575 # need to reboot the phone afterward 0576 fundamentals['rebootphone']=True 0577 return fundamentals 0578 0579 # Memo/Notepad stuff-------------------------------------------------------- 0580 def getmemo(self, fundamentals): 0581 self.log('Reading note pad items') 0582 _index_file=self._read_calendar_index() 0583 _res={} 0584 for _idx in range(_index_file.numofnotes): 0585 _file_name='%s%04d'%(self.protocolclass.NP_FILE_NAME_PREFIX, 0586 _index_file.notes[_idx].index) 0587 _buf=prototypes.buffer(self.getfilecontents(_file_name)) 0588 _note=self.protocolclass.NotePadEntry() 0589 _note.readfrombuffer(_buf) 0590 _memo=memo.MemoEntry() 0591 _memo.text=_note.text 0592 _res[_memo.id]=_memo 0593 fundamentals['memo']=_res 0594 return fundamentals 0595 0596 def _del_existing_memo_entries(self): 0597 self.log('Deleting existing memo entries') 0598 _file_index=self._read_calendar_index() 0599 for _idx in range(_file_index.numofnotes): 0600 _file_name='%s%04d'%(self.protocolclass.NP_FILE_NAME_PREFIX, 0601 _file_index.notes[_idx].index) 0602 try: 0603 self.rmfile(_file_name) 0604 except: 0605 self.log('Failed to delete file: '+_file_name) 0606 return _file_index.next_index 0607 0608 def _write_memo_entries(self, next_index, fundamentals): 0609 # write each and every memo entries, each in a separate file 0610 _memo_dict=fundamentals.get('memo', {}) 0611 _idx=next_index 0612 _cnt=0 0613 for _key,_entry in _memo_dict.items(): 0614 if _cnt>=self.protocolclass.NP_MAX_ENTRIES: 0615 # enough memo already! 0616 break 0617 try: 0618 _memo_entry=self.protocolclass.NotePadEntry() 0619 _text_len=min(self.protocolclass.NP_MAX_LEN, 0620 len(_entry.text)) 0621 _memo_entry.textlen=_text_len 0622 _memo_entry.text=_entry.text[:_text_len] 0623 _memo_entry.creation=self._time_now() 0624 _buf=prototypes.buffer() 0625 _memo_entry.writetobuffer(_buf) 0626 _file_name='%s%04d'%(self.protocolclass.NP_FILE_NAME_PREFIX, 0627 _idx) 0628 self.writefile(_file_name, _buf.getvalue()) 0629 _idx+=1 0630 _cnt+=1 0631 except: 0632 self.log('Failed to write memo endar entry') 0633 if __debug__: 0634 raise 0635 return _idx 0636 0637 def _write_memo_index(self, next_index, fundamentals): 0638 _file_index=self._read_calendar_index() 0639 # clear out the old entries 0640 for _idx in range(_file_index.numofnotes): 0641 _file_index.notes[_idx].index=0 0642 # update with new info 0643 _old_next_index=_file_index.next_index 0644 _num_entries=next_index-_old_next_index 0645 _file_index.next_index=next_index 0646 _file_index.numofnotes=_num_entries 0647 _cnt=0 0648 for _idx in range(_old_next_index, next_index): 0649 _file_index.notes[_cnt].index=_idx 0650 _cnt+=1 0651 _buf=prototypes.buffer() 0652 _file_index.writetobuffer(_buf) 0653 self.writefile(self.protocolclass.CAL_INDEX_FILE_NAME, 0654 _buf.getvalue()) 0655 0656 def savememo(self, fundamentals, merge): 0657 self.log('Writing memo/notepad items') 0658 _next_index=self._del_existing_memo_entries() 0659 _next_index=self._write_memo_entries(_next_index, fundamentals) 0660 self._write_memo_index(_next_index, fundamentals) 0661 fundamentals['rebootphone']=True 0662 return fundamentals 0663 0664 # Phone Detection----------------------------------------------------------- 0665 my_model='SCH-A950/DM' 0666 my_manufacturer='SAMSUNG' 0667 detected_model='A950' 0668 def is_mode_brew(self): 0669 # Borrowed from the VX4400 0670 req=self.protocolclass.memoryconfigrequest() 0671 respc=self.protocolclass.memoryconfigresponse 0672 for baud in 0, 38400, 115200: 0673 if baud: 0674 if not self.comm.setbaudrate(baud): 0675 continue 0676 try: 0677 self.sendbrewcommand(req, respc, callsetmode=False) 0678 return True 0679 except com_phone.modeignoreerrortypes: 0680 pass 0681 return False 0682 def check_my_phone(self, res): 0683 # check if this is an A950 0684 try: 0685 _req=self.protocolclass.firmwarerequest() 0686 _resp=self.sendbrewcommand(_req, self.protocolclass.DefaultResponse) 0687 if _resp.data[31:35]==self.detected_model: 0688 # yup, this's it! 0689 res['model']=self.my_model 0690 res['manufacturer']=self.my_manufacturer 0691 res['esn']=self.get_esn() 0692 except: 0693 if __debug__: 0694 raise 0695 @classmethod 0696 def detectphone(_, coms, likely_ports, res, _module, _log): 0697 if not likely_ports: 0698 # cannot detect any likely ports 0699 return None 0700 for port in likely_ports: 0701 if not res.has_key(port): 0702 res[port]={ 'mode_modem': None, 'mode_brew': None, 0703 'manufacturer': None, 'model': None, 0704 'firmware_version': None, 'esn': None, 0705 'firmwareresponse': None } 0706 try: 0707 if res[port]['mode_brew']==False or \ 0708 res[port]['model']: 0709 # either phone is not in BREW, or a model has already 0710 # been found, not much we can do now 0711 continue 0712 p=_module.Phone(_log, commport.CommConnection(_log, port, timeout=1)) 0713 if res[port]['mode_brew'] is None: 0714 res[port]['mode_brew']=p.is_mode_brew() 0715 if res[port]['mode_brew']: 0716 p.check_my_phone(res[port]) 0717 p.comm.close() 0718 except: 0719 if __debug__: 0720 raise 0721 0722 #Phonebook stuff------------------------------------------------------------ 0723 def _del_private_dicts(self, fundamentals): 0724 # delete the stuff that we created 0725 for _key in ('ringtone-range', 'wallpaper-range'): 0726 if fundamentals.has_key(_key): 0727 del fundamentals[_key] 0728 0729 def _extract_entries(self, filename, res, fundamentals): 0730 try: 0731 _buf=prototypes.buffer(self.getfilecontents(filename)) 0732 _rec_file=self.protocolclass.PBFileHeader() 0733 _rec_file.readfrombuffer(_buf) 0734 for _len in _rec_file.lens: 0735 if _len.itemlen: 0736 _entry=self.protocolclass.PBEntry() 0737 _entry.readfrombuffer(_buf) 0738 _pbentry=self.pbentryclass(self, _entry, fundamentals).getvalue() 0739 res[len(res)]=_pbentry 0740 except: 0741 self.log('Failed to read file: %s'%filename) 0742 if __debug__: 0743 raise 0744 0745 def getphonebook(self, fundamentals): 0746 self.log('Reading phonebook contacts') 0747 _file_cnt=0 0748 _res={} 0749 while True: 0750 _file_name='%s%04d'%(self.protocolclass.PB_ENTRY_FILE_PREFIX, 0751 _file_cnt) 0752 if self.statfile(_file_name): 0753 self._extract_entries(_file_name, _res, fundamentals) 0754 _file_cnt+=1 0755 else: 0756 break 0757 fundamentals['phonebook']=_res 0758 fundamentals['categories']=[x['name'] for _,x in \ 0759 fundamentals.get('groups', {}).items()] 0760 self._del_private_dicts(fundamentals) 0761 return fundamentals 0762 0763 def _get_wp_filename(self, wp, wp_index): 0764 # return the filename associated with this wallpaper 0765 for _,_entry in wp_index.items(): 0766 if _entry.get('name', None)==wp: 0767 return _entry.get('filename', None) 0768 0769 def _rescale_and_cache(self, wp, filename, idx, 0770 fundamentals): 0771 # rescale the wp and add it to the cache dir 0772 try: 0773 _data=self.getfilecontents(filename, True) 0774 _tmpname=common.gettempfilename('tmp') 0775 file(_tmpname, 'wb').write(_data) 0776 _img=wx.Image(_tmpname) 0777 if not _img.Ok(): 0778 self.log('Failed to understand image: '+filename) 0779 return 0780 _img.Rescale(128, 96) 0781 _img.SaveFile(_tmpname, wx.BITMAP_TYPE_JPEG) 0782 _newfilename=self.protocolclass.PB_WP_CACHE_PATH+'/$'+filename.replace('/', '$') 0783 _data=file(_tmpname, 'rb').read() 0784 self.writefile(_newfilename, _data) 0785 return _newfilename 0786 except: 0787 if __debug__: 0788 self.log('Failed to add cache image: '+wp) 0789 raise 0790 0791 def _add_wp_cache(self, wp, idx, fundamentals): 0792 # check to see if it already exists 0793 _wp_range=fundamentals.get('wallpaper-range', {}) 0794 if _wp_range.has_key(wp): 0795 # already in there 0796 return 0797 # add this wallpaper into the cache dir 0798 _wp_index=fundamentals.get('wallpaper-index', {}) 0799 # look for the file name 0800 _filename=self._get_wp_filename(wp, _wp_index) 0801 if not _filename: 0802 # couldn't find the filename 0803 return 0804 # copy the image file, rescale, and put it in the cache dir 0805 _newfilename=self._rescale_and_cache(wp, _filename, idx, fundamentals) 0806 if _newfilename: 0807 # rescale successful, update the dict 0808 _wp_range[wp]='/ff/'+_newfilename 0809 fundamentals['wallpaper-range']=_wp_range 0810 0811 def get_wallpaper_range(self, wallpaper, fundamentals): 0812 # return the wallpaper cache name for the specific wallpaper 0813 return fundamentals.get('wallpaper-range', {}).get(wallpaper, None) 0814 0815 def savephonebook(self, fundamentals): 0816 self.log('Writing phonebook contacts') 0817 self._read_ringtone_range(fundamentals) 0818 _pb_dict=fundamentals.get('phonebook', {}) 0819 # alphabetize the list based on name 0820 _pb_list=[(nameparser.getfullname(_entry['names'][0]), _key) \ 0821 for _key,_entry in _pb_dict.items()] 0822 _pb_list.sort() 0823 _req=self.protocolclass.ss_pb_clear_req() 0824 _rp=self.sendbrewcommand(_req, self.protocolclass.ss_pb_clear_resp) 0825 if _rp.flg: 0826 self.log('Failed to clear phonebook') 0827 self._del_private_dicts(fundamentals) 0828 return fundamentals 0829 _req=self.protocolclass.ss_pb_write_req() 0830 _total_cnt=len(_pb_list) 0831 _cnt=1 0832 for _name,_key in _pb_list: 0833 try: 0834 _entry=_pb_dict[_key] 0835 # set up all the picture ID (wallpaper) images 0836 _wp=_entry.get('wallpapers', [{}])[0].get('wallpaper', None) 0837 if _wp: 0838 self._add_wp_cache(_wp, _cnt, fundamentals) 0839 # setting up a new contact to send over 0840 _pbentry=self.pbentryclass(self, _entry, fundamentals) 0841 _req.entry=_pbentry.pb 0842 _cnt+=1 0843 self.progress(_cnt, _total_cnt, 0844 'Writing entry" %s'%_req.entry.name) 0845 _resp=self.sendbrewcommand(_req, 0846 self.protocolclass.ss_pb_write_resp) 0847 except: 0848 self.log('Failed to write entry') 0849 if __debug__: 0850 raise 0851 fundamentals['rebootphone']=True 0852 self._del_private_dicts(fundamentals) 0853 return fundamentals 0854 0855 # Call History stuff-------------------------------------------------------- 0856 def _get_ch_index(self): 0857 # read the index file and return the number of incoming, outgoing, and 0858 # missed calls 0859 try: 0860 _buf=prototypes.buffer(self.getfilecontents(self.protocolclass.CL_INDEX_FILE)) 0861 _req=self.protocolclass.cl_index_file() 0862 _req.readfrombuffer(_buf, 'Reading Call Log Index File') 0863 return ([x.index for x in _req.incoming[:_req.incoming_count]], 0864 [x.index for x in _req.outgoing[:_req.outgoing_count]], 0865 [x.index for x in _req.missed[:_req.missed_count]]) 0866 except com_brew.BrewNoSuchFileException: 0867 return ([], [], []) 0868 except: 0869 if __debug__: 0870 raise 0871 return ([], [], []) 0872 def _get_ch(self, call_list, folder, res): 0873 # read the call history files 0874 _req=self.protocolclass.cl_file() 0875 for _idx in call_list: 0876 try: 0877 _buf=prototypes.buffer(self.getfilecontents( 0878 '%s%02d'%(self.protocolclass.CL_PREFIX, _idx))) 0879 _req.readfrombuffer(_buf, 'Reading Call Log File') 0880 if _req.valid: 0881 _entry=call_history.CallHistoryEntry() 0882 _entry.folder=folder 0883 _entry.number=_req.number 0884 _entry.datetime=_req.datetime 0885 if _req.duration: 0886 _entry.duration=_req.duration 0887 res[_entry.id]=_entry 0888 except com_brew.BrewNoSuchFileException: 0889 pass 0890 except: 0891 if __debug__: 0892 raise 0893 0894 def getcallhistory(self, fundamentals): 0895 # retrieve the call history data from the phone 0896 res={} 0897 _incoming_list, _outgoing_list, _missed_list=self._get_ch_index() 0898 self._get_ch(_incoming_list, 0899 call_history.CallHistoryEntry.Folder_Incoming, res) 0900 self._get_ch(_outgoing_list, 0901 call_history.CallHistoryEntry.Folder_Outgoing, res) 0902 self._get_ch(_missed_list, 0903 call_history.CallHistoryEntry.Folder_Missed, res) 0904 fundamentals['call_history']=res 0905 0906 # SMS Stuff---------------------------------------------------------------- 0907 def _build_common_msg(self, entry, sms_hdr): 0908 entry.text=sms_hdr.body.msg 0909 entry.datetime=sms_hdr.body.datetime[:5] 0910 if sms_hdr.body.has_callback: 0911 entry.callback=sms_hdr.body.callback 0912 if sms_hdr.body.has_priority: 0913 if sms_hdr.body.priority: 0914 entry.priority=sms.SMSEntry.Priority_High 0915 else: 0916 entry.priority=sms.SMSEntry.Priority_Normal 0917 0918 def _build_locked_field(self, entry, buf): 0919 _locked=self.protocolclass.UINT(sizeinbytes=4) 0920 _locked.readfrombuffer(buf) 0921 entry.locked=bool(_locked.getvalue()) 0922 0923 def _build_in_msg(self, sms_hdr, buf, res): 0924 _entry=sms.SMSEntry() 0925 _entry.folder=sms.SMSEntry.Folder_Inbox 0926 _entry._from=sms_hdr.body.addr0 0927 self._build_common_msg(_entry, sms_hdr) 0928 _entry.read=sms_hdr.body.msg_stat[0].status==self.protocolclass.SMS_STATUS_READ 0929 self._build_locked_field(_entry, buf) 0930 res[_entry.id]=_entry 0931 0932 def _build_sent_msg(self, sms_hdr, buf, res): 0933 _entry=sms.SMSEntry() 0934 _entry.folder=sms.SMSEntry.Folder_Sent 0935 self._build_common_msg(_entry, sms_hdr) 0936 _confirmed_flg=False 0937 for _stat in sms_hdr.body.msg_stat: 0938 if _stat.status==self.protocolclass.SMS_STATUS_DELIVERED: 0939 _confirmed_flg=True 0940 break 0941 if _confirmed_flg: 0942 _datetime_list=self.protocolclass.sms_delivered_datetime() 0943 _datetime_list.readfrombuffer(buf, 'Reading Confirmed Datetime field') 0944 for _idx in range(10): 0945 if getattr(sms_hdr.body, 'addr_len%d'%_idx): 0946 if sms_hdr.body.msg_stat[_idx].status==self.protocolclass.SMS_STATUS_DELIVERED: 0947 _entry.add_recipient(getattr(sms_hdr.body, 'addr%d'%_idx), 0948 True, 0949 _datetime_list.datetime[_idx].datetime[:5]) 0950 else: 0951 _entry.add_recipient(getattr(sms_hdr.body, 'addr%d'%_idx)) 0952 self._build_locked_field(_entry, buf) 0953 res[_entry.id]=_entry 0954 0955 def _build_draft_msg(self, sms_hdr, buf, res): 0956 _entry=sms.SMSEntry() 0957 _entry.folder=sms.SMSEntry.Folder_Saved 0958 self._build_common_msg(_entry, sms_hdr) 0959 self._build_locked_field(_entry, buf) 0960 for _idx in range(10): 0961 if getattr(sms_hdr.body, 'addr_len%d'%_idx): 0962 _entry.add_recipient(getattr(sms_hdr.body, 'addr%d'%_idx)) 0963 res[_entry.id]=_entry 0964 0965 def _read_sms(self, filename, res, fundamentals): 0966 _buf=prototypes.buffer(self.getfilecontents(filename)) 0967 _sms=self.protocolclass.sms_header() 0968 _sms.readfrombuffer(_buf, 'Reading SMS File') 0969 if not _sms.is_txt_msg.value: 0970 # not a text message 0971 return 0972 if _sms.in_msg.value: 0973 self._build_in_msg(_sms, _buf, res) 0974 elif _sms.sent_msg.value: 0975 self._build_sent_msg(_sms, _buf, res) 0976 else: 0977 self._build_draft_msg(_sms, _buf, res) 0978 0979 def getsms(self, fundamentals): 0980 res={} 0981 for _filename in self.listfiles(self.protocolclass.SMS_PATH): 0982 try: 0983 self._read_sms(_filename, res, fundamentals) 0984 except: 0985 self.log('Failed to read SMS File '+_filename) 0986 if __debug__: 0987 raise 0988 fundamentals['sms']=res 0989 fundamentals['canned_msg']=[] 0990 return fundamentals 0991 0992 # CalendarEntry class----------------------------------------------------------- 0993 class CalendarEntry(object): 0994 """Transient class to handle calendar data being sent to, retrieved from 0995 the phone. 0996 """ 0997 # Repeat Constants 0998 REP_NONE=0 0999 REP_ONCE=0 1000 REP_DAILY=2 1001 REP_WEEKLY=5 1002 REP_MONTHLY=6 1003 REP_YEARLY=7 1004 # Alarm constants 1005 ALARM_ONTIME=0 1006 ALARM_5M=1 1007 ALARM_10M=2 1008 ALARM_15M=3 1009 ALARM_30M=4 1010 ALARM_1HR=5 1011 ALARM_3HR=6 1012 ALARM_5HR=7 1013 ALARM_1D=8 1014 # Alert 1015 ALERT_TONE=0 1016 ALERT_VIBRATE=1 1017 ALERT_LIGHT=2 1018 # Timezone 1019 TZ_EST=0 1020 TZ_EDT=1 1021 TZ_CST=2 1022 TZ_CDT=3 1023 TZ_MST=4 1024 TZ_MDT=5 1025 TZ_PST=6 1026 TZ_PDT=7 1027 TZ_AKST=8 1028 TZ_AKDT=9 1029 TZ_HAST=10 1030 TZ_HADT=11 1031 TZ_GMT=12 1032 def __init__(self, phone, value, fundamentals): 1033 self.phone=phone 1034 self.fundamentals=fundamentals 1035 self.cal=phone.protocolclass.CalEntry() 1036 if isinstance(value, bpcalendar.CalendarEntry): 1037 self._build(value) 1038 elif isinstance(value, prototypes.buffer): 1039 self.cal.readfrombuffer(value) 1040 else: 1041 raise TypeError('Expecting type bpcalendar.CalendarEntry or prototypes.buffer') 1042 1043 def writetobuffer(self, buf): 1044 self.cal.writetobuffer(buf) 1045 1046 # building routines--------------------------------------------------------- 1047 _build_repeat_dict={ 1048 bpcalendar.RepeatEntry.daily: REP_DAILY, 1049 bpcalendar.RepeatEntry.weekly: REP_WEEKLY, 1050 bpcalendar.RepeatEntry.monthly: REP_MONTHLY, 1051 bpcalendar.RepeatEntry.yearly: REP_YEARLY, 1052 } 1053 _build_alarm_dict={ 1054 0: ALARM_ONTIME, 1055 5: ALARM_5M, 1056 10: ALARM_10M, 1057 15: ALARM_15M, 1058 30: ALARM_30M, 1059 60: ALARM_1HR, 1060 180: ALARM_3HR, 1061 300: ALARM_5HR, 1062 1440: ALARM_1D, 1063 } 1064 _build_tz_dict={ 1065 0: TZ_GMT, 1066 18000: TZ_EST, 1067 21600: TZ_CST, 1068 25200: TZ_MST, 1069 28800: TZ_PST, 1070 32400: TZ_AKST, 1071 36000: TZ_HAST, 1072 } 1073 def _build_duration(self, entry): 1074 return (datetime.datetime(*entry.end)-\ 1075 datetime.datetime(*entry.start)).seconds 1076 def _build_repeat(self, entry): 1077 rep=entry.repeat 1078 if not rep: 1079 return self.REP_ONCE 1080 return self._build_repeat_dict.get(rep.repeat_type, self.REP_ONCE) 1081 def _build_alarm(self, entry): 1082 _keys=self._build_alarm_dict.keys() 1083 _keys.sort() 1084 _alarm=entry.alarm 1085 for k in _keys: 1086 if _alarm<=k: 1087 return self._build_alarm_dict[k] 1088 return self.ALARM_ONTIME 1089 def _build_alert(self, entry): 1090 if entry.vibrate: 1091 return self.ALERT_VIBRATE 1092 return self.ALERT_TONE 1093 _tz_code=None 1094 def _build_tz(self): 1095 if CalendarEntry._tz_code is None: 1096 CalendarEntry._tz_code=self._build_tz_dict.get(time.timezone, 1097 self.TZ_EST) 1098 if time.localtime()[-1]==1: 1099 # daylight saving time 1100 CalendarEntry._tz_code+=1 1101 return CalendarEntry._tz_code 1102 1103 def _build(self, entry): 1104 # populate this object with data from BitPim 1105 self.cal.titlelen=len(entry.desc_loc) 1106 self.cal.title=entry.desc_loc 1107 self.cal.start=entry.start 1108 self.cal.exptime=entry.end[3:5] 1109 self.cal.repeat=self._build_repeat(entry) 1110 self.cal.alarm=self._build_alarm(entry) 1111 self.cal.alert=self._build_alert(entry) 1112 self.cal.duration=self._build_duration(entry) 1113 self.cal.timezone=self._build_tz() 1114 _now=self.phone._time_now() 1115 self.cal.creationtime=_now 1116 self.cal.modifiedtime=_now 1117 _ringtone=self.phone.get_ringtone_range(entry.ringtone, 1118 self.fundamentals) 1119 self.cal.ringtonelen=len(_ringtone) 1120 self.cal.ringtone=_ringtone 1121 1122 # Extracting routine-------------------------------------------------------- 1123 def _extract_end(self): 1124 return (datetime.datetime(*self.cal.start)+\ 1125 datetime.timedelta(seconds=self.cal.duration)).timetuple()[:5] 1126 def _extract_alarm(self): 1127 for _value,_code in self._build_alarm_dict.items(): 1128 if self.cal.alarm==_code: 1129 return _value 1130 def _extract_repeat(self): 1131 if self.cal.repeat==self.REP_ONCE: 1132 return None 1133 _rep_type=None 1134 for _type, _code in self._build_repeat_dict.items(): 1135 if self.cal.repeat==_code: 1136 _rep_type=_type 1137 break 1138 if not _rep_type: 1139 return None 1140 _rep=bpcalendar.RepeatEntry(_rep_type) 1141 if _rep_type==_rep.daily: 1142 _rep.interval=1 1143 elif _rep_type==_rep.weekly: 1144 _rep.interval=1 1145 elif _rep_type==_rep.monthly: 1146 _rep.interval2=1 1147 _rep.dow=0 1148 return _rep 1149 1150 def getvalue(self): 1151 # return a BitPim calendar entry equivalence 1152 _entry=bpcalendar.CalendarEntry() 1153 _entry.desc_loc=self.cal.title 1154 _entry.start=self.cal.start 1155 _entry.end=self._extract_end() 1156 _entry.alarm=self._extract_alarm() 1157 _entry.repeat=self._extract_repeat() 1158 if _entry.repeat: 1159 # forever repeat event 1160 _entry.end=_entry.no_end_date+_entry.end[3:] 1161 _entry.ringtone=self.phone.ringtone_name_from_range(self.cal.ringtone, 1162 self.fundamentals) 1163 _entry.vibrate=self.cal.alert==self.ALERT_VIBRATE 1164 return _entry 1165 1166 # PBEntry class----------------------------------------------------------------- 1167 class PBEntry(object): 1168 1169 def __init__(self, phone, data, fundamentals): 1170 self.phone=phone 1171 self.fundamentals=fundamentals 1172 if isinstance(data, phone.protocolclass.PBEntry): 1173 self.pb=data 1174 elif isinstance(data, dict): 1175 # assume it's a phonebook dict 1176 self.pb=phone.protocolclass.ss_pb_entry() 1177 self._build(data) 1178 else: 1179 raise TypeError('Should be PBEntry or phone dict') 1180 1181 def writetobuffer(self, buf): 1182 self.pb.writetobuffer(buf) 1183 1184 # Building a phonebook rec from a bp phone dict----------------------------- 1185 _pb_type_dict={ 1186 'home': 'home', 1187 'office': 'work', 1188 'cell': 'cell', 1189 'fax': 'fax', 1190 } 1191 def _build_number(self, number, ringtone, primary): 1192 # build a number rec 1193 _num_type=self._pb_type_dict.get(number['type'], None) 1194 if not _num_type: 1195 # we don's support this type 1196 return 1197 # check for cell2 1198 if _num_type=='cell' and self.pb.cell.number: 1199 _num_type='cell2' 1200 # build a number entry 1201 _entry=self.phone.protocolclass.ss_number_entry() 1202 _entry.number=number['number'] 1203 _sd=number.get('speeddial', None) 1204 if ringtone: 1205 _rt=self.phone.get_ringtone_range(ringtone, self.fundamentals) 1206 else: 1207 _rt=None 1208 if _sd is not None: 1209 _entry.speeddial=_sd 1210 if _rt is not None: 1211 _entry.ringtone=_rt 1212 if primary: 1213 _entry.primary=1 1214 # add it to the contact 1215 setattr(self.pb, _num_type, _entry) 1216 1217 def _build_email(self, emails): 1218 # build an email rec 1219 if len(emails) and emails[0].get('email', None): 1220 # at least 1 email 1221 self.pb.email=emails[0]['email'] 1222 if len(emails)>1 and emails[1].get('email', None): 1223 # 2 or more emails 1224 self.pb.email2=emails[1]['email'] 1225 1226 def _build_group(self, cat): 1227 # set the group if specified 1228 if not cat: 1229 return 1230 _cat_list=self.fundamentals.get('groups', {}) 1231 for _key,_cat in _cat_list.items(): 1232 if _key and _cat.get('name', None)==cat: 1233 self.pb.group=_key 1234 break 1235 1236 def _build_wallpaper(self, wallpaper): 1237 # set the wallpaper if specified 1238 if not wallpaper: 1239 return 1240 _wp=self.phone.get_wallpaper_range(wallpaper, self.fundamentals) 1241 if _wp: 1242 self.pb.wallpaper=_wp 1243 1244 def _build(self, entry): 1245 # Build a phone dict base on the phone data 1246 self.pb.name=nameparser.getfullname(entry['names'][0]) 1247 # global ringtone 1248 _ringtone=entry.get('ringtones', [{}])[0].get('ringtone', None) 1249 # build the numbers 1250 _primary=True # the first number is the primary one 1251 for _number in entry.get('numbers', []): 1252 self._build_number(_number, _ringtone, _primary) 1253 _primary=False 1254 # build the email 1255 self._build_email(entry.get('emails', [])) 1256 # group 1257 self._build_group(entry.get('categories', [{}])[0].get('category', None)) 1258 # wallpaper 1259 self._build_wallpaper(entry.get('wallpapers', [{}])[0].get('wallpaper', None)) 1260 1261 # Extracting data from the phone-------------------------------------------- 1262 def _extract_emails(self, entry, p_class): 1263 # extract emails 1264 if self.pb.info & p_class.PB_FLG_EMAIL: 1265 entry['emails']=[{ 'email': self.pb.email }] 1266 if self.pb.info & p_class.PB_FLG_EMAIL2: 1267 entry.setdefault('emails', []).append({ 'email': self.pb.email2 }) 1268 _number_type_dict={ 1269 'cell': (Phone.protocolclass.PB_FLG_CELL, 'cell'), 1270 'home': (Phone.protocolclass.PB_FLG_HOME, 'home'), 1271 'work': (Phone.protocolclass.PB_FLG_WORK, 'office'), 1272 'fax': (Phone.protocolclass.PB_FLG_FAX, 'fax'), 1273 'cell2': (Phone.protocolclass.PB_FLG_CELL2, 'cell'), 1274 } 1275 def _extract_numbers(self, entry, p_class): 1276 # extract phone numbers 1277 entry['numbers']=[] 1278 for _key,_info_list in self._number_type_dict.items(): 1279 if self.pb.info&_info_list[0]: 1280 _num_entry=getattr(self.pb, _key) 1281 _number={ 'number': _num_entry.number, 1282 'type': _info_list[1] } 1283 if _num_entry.option&p_class.PB_FLG_SPEEDDIAL: 1284 _number['speeddial']=_num_entry.speeddial 1285 if _num_entry.option&p_class.PB_FLG_RINGTONE and \ 1286 not entry.has_key('ringtones'): 1287 _ringtone=self.phone.ringtone_name_from_range( 1288 _num_entry.ringtone, self.fundamentals) 1289 if _ringtone: 1290 entry['ringtones']=[{ 'ringtone': _ringtone, 1291 'use': 'call' }] 1292 if _num_entry.option & p_class.PB_FLG_PRIMARY: 1293 # this is the primary number, insert to the beginning 1294 entry['numbers']=[_number]+entry['numbers'] 1295 else: 1296 entry['numbers'].append(_number) 1297 def _extract_group(self, entry, p_class): 1298 if not self.pb.info&p_class.PB_FLG_GROUP: 1299 # no group specified 1300 return 1301 _groups=self.fundamentals.get('groups', {}) 1302 if _groups.has_key(self.pb.group): 1303 entry['categories']=[{ 'category': _groups[self.pb.group]['name'] }] 1304 def _extract_wallpaper(self, entry, p_class): 1305 if not self.pb.info&p_class.PB_FLG_WP: 1306 return 1307 _idx=self.pb.wallpaper.rfind('$')+1 1308 _wp=self.pb.wallpaper[_idx:] 1309 entry['wallpapers']=[{ 'wallpaper': _wp, 1310 'use': 'call' }] 1311 def getvalue(self): 1312 _entry={} 1313 _p_class=self.phone.protocolclass 1314 _entry['names']=[{ 'full': self.pb.name }] 1315 self._extract_emails(_entry, _p_class) 1316 self._extract_numbers(_entry, _p_class) 1317 self._extract_group(_entry, _p_class) 1318 self._extract_wallpaper(_entry, _p_class) 1319 return _entry 1320 1321 #------------------------------------------------------------------------------- 1322 parentprofile=com_phone.Profile 1323 class Profile(parentprofile): 1324 serialsname=Phone.serialsname 1325 WALLPAPER_WIDTH=176 1326 WALLPAPER_HEIGHT=220 1327 # 128x96: outside LCD 1328 autodetect_delay=3 1329 usbids=( ( 0x04e8, 0x6640, 2),) 1330 deviceclasses=("serial",) 1331 BP_Calendar_Version=3 1332 # For phone detection 1333 phone_manufacturer=Phone.my_manufacturer 1334 phone_model=Phone.my_model 1335 # arbitrary ringtone file size limit 1336 RINGTONE_LIMITS= { 1337 'MAXSIZE': 100000 1338 } 1339 WALLPAPER_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789 ._:" 1340 RINGTONE_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789 ._:" 1341 1342 def __init__(self): 1343 parentprofile.__init__(self) 1344 1345 _supportedsyncs=( 1346 ('phonebook', 'read', None), # all phonebook reading 1347 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 1348 ('calendar', 'read', None), # all calendar reading 1349 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 1350 ('ringtone', 'read', None), # all ringtone reading 1351 ('ringtone', 'write', 'MERGE'), 1352 ('wallpaper', 'read', None), # all wallpaper reading 1353 ('wallpaper', 'write', None), 1354 ('memo', 'read', None), # all memo list reading DJP 1355 ('memo', 'write', 'OVERWRITE'), # all memo list writing DJP 1356 ('call_history', 'read', None),# all call history list reading 1357 ('sms', 'read', None), # all SMS list reading DJP 1358 ) 1359 1360 def QueryAudio(self, origin, currentextension, afi): 1361 _max_size=self.RINGTONE_LIMITS['MAXSIZE'] 1362 setattr(afi, 'MAXSIZE', _max_size) 1363 # we don't modify any of these 1364 if afi.format in ("MIDI", "QCP", "PMD"): 1365 return currentextension, afi 1366 # examine mp3 1367 if afi.format=="MP3": 1368 if afi.channels==1 and 8<=afi.bitrate<=64 and 16000<=afi.samplerate<=22050: 1369 return currentextension, afi 1370 # convert it 1371 return ("mp3", fileinfo.AudioFileInfo(afi, **{'format': 'MP3', 1372 'channels': 2, 1373 'bitrate': 48, 1374 'samplerate': 44100, 1375 'MAXSIZE': _max_size })) 1376 1377 # all dumped in "images" 1378 imageorigins={} 1379 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images")) 1380 def GetImageOrigins(self): 1381 return self.imageorigins 1382 1383 # our targets are the same for all origins 1384 imagetargets={} 1385 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", 1386 {'width': 176, 'height': 186, 'format': "JPEG"})) 1387 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "outsidelcd", 1388 {'width': 128, 'height': 96, 'format': "JPEG"})) 1389 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "fullscreen", 1390 {'width': 176, 'height': 220, 'format': "JPEG"})) 1391 def GetTargetsForImageOrigin(self, origin): 1392 return self.imagetargets 1393 1394 def convertphonebooktophone(self, helper, data): 1395 return data 1396 1397 field_color_data={ 1398 'phonebook': { 1399 'name': { 1400 'first': 1, 'middle': 1, 'last': 1, 'full': 1, 1401 'nickname': 0, 'details': 1 }, 1402 'number': { 1403 'type': 5, 'speeddial': 5, 'number': 5, 1404 'details': 5, 1405 'ringtone': False, 'wallpaper': False }, 1406 'email': 2, 1407 'email_details': { 1408 'emailspeeddial': False, 'emailringtone': False, 1409 'emailwallpaper': False }, 1410 'address': { 1411 'type': 0, 'company': 0, 'street': 0, 'street2': 0, 1412 'city': 0, 'state': 0, 'postalcode': 0, 'country': 0, 1413 'details': 0 }, 1414 'url': 0, 1415 'memo': 0, 1416 'category': 1, 1417 'wallpaper': 1, 1418 'ringtone': 1, 1419 'storage': 0, 1420 }, 1421 'calendar': { 1422 'description': True, 'location': True, 'allday': False, 1423 'start': True, 'end': True, 'priority': False, 1424 'alarm': True, 'vibrate': True, 1425 'repeat': True, 1426 'memo': False, 1427 'category': False, 1428 'wallpaper': False, 1429 'ringtone': True, 1430 }, 1431 'memo': { 1432 'subject': False, 1433 'date': False, 1434 'secret': False, 1435 'category': False, 1436 'memo': True, 1437 }, 1438 'todo': { 1439 'summary': False, 1440 'status': False, 1441 'due_date': False, 1442 'percent_complete': False, 1443 'completion_date': False, 1444 'private': False, 1445 'priority': False, 1446 'category': False, 1447 'memo': False, 1448 }, 1449 } 1450
Generated by PyXR 0.9.4