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

Source Code for Module phones.com_samsungscha950

   1  ### BITPIM 
   2  ### 
   3  ### Copyright (C) 2006 Joe Pham <djpham@bitpim.org> 
   4  ### 
   5  ### This program is free software; you can redistribute it and/or modify 
   6  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
   7  ### 
   8  ### $Id: com_samsungscha950.py 4784 2010-01-15 01:44:50Z djpham $ 
   9   
  10  """Communicate with the Samsung SCH-A950 Phone""" 
  11   
  12  # System Models 
  13  import calendar 
  14  import datetime 
  15  import sha 
  16  import time 
  17   
  18  import wx 
  19   
  20  # BitPim modules 
  21  import bpcalendar 
  22  import call_history 
  23  import common 
  24  import commport 
  25  import com_brew 
  26  import com_phone 
  27  import datetime 
  28  import fileinfo 
  29  import memo 
  30  import nameparser 
  31  import prototypes 
  32  import pubsub 
  33  import p_samsungscha950 
  34  import sqlite2_file 
  35  import sms 
  36  import helpids 
37 38 -class Phone(com_phone.Phone, com_brew.BrewProtocol):
39 desc='SCH-A950' 40 helpid=helpids.ID_PHONE_SAMSUNGSCHA950 41 protocolclass=p_samsungscha950 42 serialsname='scha950' 43 44 ringtone_noring_range='range_tones_preloaded_el_15' 45 ringtone_default_range='range_tones_preloaded_el_01' 46 builtin_ringtones={ 47 'VZW Default Tone': ringtone_default_range, 48 'Melody 1': 'range_tones_preloaded_el_02', 49 'Melody 2': 'range_tones_preloaded_el_03', 50 'Bell 1': 'range_tones_preloaded_el_04', 51 'Bell 2': 'range_tones_preloaded_el_05', 52 'Beep Once': 'range_tones_preloaded_el_06', 53 'No Ring': ringtone_noring_range, 54 } 55 builtin_sounds={ 56 'Birthday': 'range_sound_preloaded_el_birthday', 57 'Crowd Roar': 'range_sound_preloaded_el_crowed_roar', 58 'Train': 'range_sound_preloaded_el_train', 59 'Rainforest': 'range_sound_preloaded_el_rainforest', 60 'Clapping': 'range_sound_preloaded_el_clapping', 61 # same as ringtones ?? 62 'Sound Beep Once': 'range_sound_preloaded_el_beep_once', 63 'Sound No Ring': 'range_sound_preloaded_el_no_rings', 64 } 65 builtin_wallpapers={ 66 'Wallpaper 1': 'range_f_wallpaper_preloaded_el_01', 67 'Wallpaper 2': 'range_f_wallpaper_preloaded_el_02', 68 'Wallpaper 3': 'range_f_wallpaper_preloaded_el_03', 69 'Wallpaper 4': 'range_f_wallpaper_preloaded_el_04', 70 'Wallpaper 5': 'range_f_wallpaper_preloaded_el_05', 71 'Wallpaper 6': 'range_f_wallpaper_preloaded_el_06', 72 'Wallpaper 7': 'range_f_wallpaper_preloaded_el_07', 73 'Wallpaper 8': 'range_f_wallpaper_preloaded_el_08', 74 'Wallpaper 9': 'range_f_wallpaper_preloaded_el_09', 75 } 76 builtin_groups={ 77 1: 'Business', 78 2: 'Colleague', 79 3: 'Family', 80 4: 'Friends' 81 } 82
83 - def __init__(self, logtarget, commport):
84 "Calls all the constructors and sets initial modes" 85 com_phone.Phone.__init__(self, logtarget, commport) 86 com_brew.BrewProtocol.__init__(self) 87 self.pbentryclass=PBEntry 88 self.calendarclass=CalendarEntry 89 # born to be in BREW mode! 90 self.mode=self.MODEBREW
91 92 # common stuff
93 - def get_esn(self):
94 if hasattr(self, '_fs_path'): 95 # we're debugging, just return something 96 return '12345678' 97 _req=self.protocolclass.ESN_req() 98 _resp=self.sendbrewcommand(_req, self.protocolclass.ESN_resp) 99 return '%08X'%_resp.esn
100
101 - def _time_now(self):
102 return datetime.datetime.now().timetuple()[:5]
103
104 - def get_groups(self):
105 _res={ 0: { 'name': 'No Group' } } 106 try: 107 _file_name=None 108 _path_name=self.protocolclass.GROUP_INDEX_FILE_NAME 109 for i in range(256): 110 _name='%s%d'%(_path_name, i) 111 if self.exists(_name): 112 _file_name=_name 113 break 114 if not _file_name: 115 return _res 116 _index_file=self.readobject(_file_name, 117 self.protocolclass.GroupIndexFile) 118 for _entry in _index_file.items[1:]: 119 if _entry.name: 120 _res[_entry.index]={ 'name': _entry.name } 121 elif self.builtin_groups.get(_entry.index, None): 122 _res[_entry.index]={ 'name': self.builtin_groups[_entry.index] } 123 except IndexError: 124 pass 125 except: 126 if __debug__: 127 raise 128 return _res
129
130 - def _get_builtin_ringtone_index(self, idx, result):
131 for _entry in self.builtin_ringtones: 132 result[idx]= { 'name': _entry, 133 'origin': 'builtin', 134 } 135 idx+=1 136 for _entry in self.builtin_sounds: 137 result[idx]={ 'name': _entry, 138 'origin': 'builtin', 139 } 140 idx+=1 141 return idx
142 - def _get_file_ringtone_index(self, idx, result, 143 index_file_name, index_file_class, 144 origin):
145 try: 146 _buf=prototypes.buffer(self.getfilecontents(index_file_name)) 147 except (com_brew.BrewNoSuchFileException, 148 com_brew.BrewBadPathnameException, 149 com_brew.BrewFileLockedException, 150 com_brew.BrewAccessDeniedException): 151 return idx 152 except: 153 if __debug__: 154 raise 155 return idx 156 _index_file=index_file_class() 157 _index_file.readfrombuffer(_buf) 158 for _entry in _index_file.items: 159 if _entry.pathname.startswith('/ff/'): 160 _file_name=_entry.pathname[4:] 161 else: 162 _file_name=_entry.pathname 163 result[idx]= { 'name': common.basename(_entry.pathname), 164 'filename': _file_name, 165 'origin': origin, 166 } 167 idx+=1 168 return idx
169 - def get_ringtone_index(self):
170 _res={} 171 _idx=self._get_builtin_ringtone_index(0, _res) 172 _idx=self._get_file_ringtone_index(_idx, _res, 173 self.protocolclass.RT_INDEX_FILE_NAME, 174 self.protocolclass.RRingtoneIndexFile, 175 'ringers') 176 _idx=self._get_file_ringtone_index(_idx, _res, 177 self.protocolclass.SND_INDEX_FILE_NAME, 178 self.protocolclass.RSoundsIndexFile, 179 'sounds') 180 return _res
181 - def _get_builtin_wallpaper_index(self, idx, result):
182 for _entry in self.builtin_wallpapers: 183 result[idx]={ 'name': _entry, 184 'origin': 'builtin', 185 } 186 idx+=1 187 return idx
188 - def _get_file_wallpaper_index(self, idx, result):
189 try: 190 _buf=prototypes.buffer(self.getfilecontents(self.protocolclass.PIC_INDEX_FILE_NAME)) 191 except (com_brew.BrewNoSuchFileException, 192 com_brew.BrewBadPathnameException, 193 com_brew.BrewFileLockedException, 194 com_brew.BrewAccessDeniedException): 195 return idx 196 except: 197 if __debug__: 198 raise 199 return idx 200 _index_file=self.protocolclass.RPictureIndexFile() 201 _index_file.readfrombuffer(_buf) 202 for _entry in _index_file.items[1:]: 203 if _entry.pathname.startswith('/ff/'): 204 _file_name=_entry.pathname[4:] 205 else: 206 _file_name=_entry.pathname 207 result[idx]={ 'name': _entry.name, 208 'filename': _file_name, 209 'origin': 'images', 210 } 211 idx+=1 212 return idx
213 - def get_wallpaper_index(self):
214 _res={} 215 _idx=self._get_file_wallpaper_index(0, _res) 216 return _res
217 - def _read_ringtone_range(self, fundamentals):
218 _res={} 219 try: 220 _data=self.getfilecontents(self.protocolclass.PREF_DB_FILE_NAME) 221 _db=sqlite2_file.DBFile(_data) 222 for _row in _db.get_table_data('dynamic_range_els'): 223 _res[_row[2]]=_row[0] 224 except: 225 if __debug__: 226 raise 227 fundamentals['ringtone-range']=_res
228
229 - def get_ringtone_range(self, name, fundamentals):
230 if not name: 231 # return No Rings 232 return self.ringtone_default_range 233 # check the builtin ringtones 234 if self.builtin_ringtones.has_key(name): 235 return self.builtin_ringtones[name] 236 if self.builtin_sounds.has_key(name): 237 return self.builtin_sounds[name] 238 if not fundamentals.has_key('ringtone-range'): 239 self._read_ringtone_range(fundamentals) 240 _rt_range=fundamentals['ringtone-range'] 241 return _rt_range.get(name, None)
242
243 - def ringtone_name_from_range(self, range, fundamentals):
244 # check for builtin ringtones 245 for _key,_value in self.builtin_ringtones.items(): 246 if range==_value: 247 return _key 248 # check for builtin sounds 249 for _key,_value in self.builtin_sounds.items(): 250 if range==_value: 251 return _key 252 # now check for the "custom" ones 253 if not fundamentals.has_key('ringtone-range'): 254 self._read_ringtone_range(fundamentals) 255 for _key,_value in fundamentals['ringtone-range'].items(): 256 if _value==range: 257 return _key
258
259 - def getfundamentals(self, results):
260 """Gets information fundamental to interopating with the phone and UI. 261 262 Currently this is: 263 264 - 'uniqueserial' a unique serial number representing the phone 265 - 'groups' the phonebook groups 266 - 'wallpaper-index' map index numbers to names 267 - 'ringtone-index' map index numbers to ringtone names 268 269 This method is called before we read the phonebook data or before we 270 write phonebook data. 271 """ 272 273 # use a hash of ESN and other stuff (being paranoid) 274 self.log("Retrieving fundamental phone information") 275 self.log("Phone serial number") 276 results['uniqueserial']=sha.new(self.get_esn()).hexdigest() 277 results['groups']=self.get_groups() 278 results['ringtone-index']=self.get_ringtone_index() 279 results['wallpaper-index']=self.get_wallpaper_index() 280 return results
281 282 # Ringtone Stuff------------------------------------------------------------
283 - def _get_media_from_index(self, index_key, media_key, 284 fundamentals):
285 _index=fundamentals.get(index_key, {}) 286 _media={} 287 for _entry in _index.values(): 288 if _entry.has_key('filename') and _entry['filename']: 289 try: 290 _media[_entry['name']]=self.getfilecontents(_entry['filename'], 291 True) 292 except: 293 self.log('Failed to read file %s'%_entry['filename']) 294 fundamentals[media_key]=_media 295 return fundamentals
296
297 - def getringtones(self, fundamentals):
298 # reading ringers & sounds files 299 return self._get_media_from_index('ringtone-index', 'ringtone', 300 fundamentals)
301
302 - def _get_del_new_list(self, index_key, media_key, merge, fundamentals, 303 ignored_origins=()):
304 """Return a list of media being deleted and being added""" 305 _index=fundamentals.get(index_key, {}) 306 _media=fundamentals.get(media_key, {}) 307 _index_file_list=[_entry['name'] for _,_entry in _index.items() \ 308 if _entry.has_key('filename') and \ 309 _entry.get('origin', None) not in ignored_origins] 310 _bp_file_list=[_entry['name'] for _,_entry in _media.items() \ 311 if _entry.get('origin', None) not in ignored_origins] 312 if merge: 313 # just add the new files, don't delete anything 314 _del_list=[] 315 _new_list=_bp_file_list 316 else: 317 # Delete specified files and add everything 318 _del_list=[x for x in _index_file_list if x not in _bp_file_list] 319 _new_list=_bp_file_list 320 return _del_list, _new_list
321
322 - def _item_from_index(self, name, item_key, index_dict):
323 for _key,_entry in index_dict.items(): 324 if _entry.get('name', None)==name: 325 if item_key: 326 # return a field 327 return _entry.get(item_key, None) 328 else: 329 # return the key 330 return _key
331
332 - def _del_files(self, index_key, _del_list, fundamentals):
333 """Delete specified media files, need to be in OBEX mode""" 334 _index=fundamentals.get(index_key, {}) 335 for _file in _del_list: 336 _file_name=self._item_from_index(_file, 'filename', _index) 337 if _file_name: 338 try: 339 self.rmfile(_file_name) 340 except Exception, e: 341 self.log('Failed to delete file %s: %s'%(_file_name, str(e)))
342
343 - def _replace_files(self, index_key, media_key, new_list, fundamentals):
344 """Replace existing files with new contents using BREW""" 345 _index=fundamentals.get(index_key, {}) 346 _media=fundamentals.get(media_key, {}) 347 for _file in new_list: 348 _data=self._item_from_index(_file, 'data', _media) 349 if not _data: 350 self.log('Failed to write file %s due to no data'%_file) 351 continue 352 _file_name=self._item_from_index(_file, 'filename', _index) 353 if _file_name: 354 # existing file, check if the same one 355 _stat=self.statfile(_file_name) 356 if _stat and _stat['size']!=len(_data): 357 # different size, replace it 358 try: 359 self.writefile(_file_name, _data) 360 except: 361 self.log('Failed to write BREW file '+_file_name) 362 if __debug__: 363 raise
364
365 - def _add_files(self, index_key, media_key, 366 new_list, fundamentals):
367 """Add new file using BREW""" 368 _index=fundamentals.get(index_key, {}) 369 _media=fundamentals.get(media_key, {}) 370 _res=[] 371 for _file in new_list: 372 _data=self._item_from_index(_file, 'data', _media) 373 if not _data: 374 self.log('Failed to write file %s due to no data'%_file) 375 continue 376 if self._item_from_index(_file, None, _index) is None: 377 # new file 378 _origin=self._item_from_index(_file, 'origin', _media) 379 if _origin=='ringers': 380 _path=self.protocolclass.RT_PATH 381 elif _origin=='sounds': 382 _path=self.protocolclass.SND_PATH 383 elif _origin=='images': 384 _path=self.protocolclass.PIC_PATH 385 else: 386 selg.log('File %s has unknown origin, skip!'%_file) 387 continue 388 _file_name=_path+'/'+_file 389 try: 390 self.writefile(_file_name, _data) 391 _res.append(_file) 392 except: 393 self.log('Failed to write file '+_file_name) 394 if __debug__: 395 raise 396 return _res
397
398 - def _update_media_index(self, index_file_class, index_entry_class, 399 media_path, excluded_files, 400 index_file_name):
401 # Update the index file 402 _index_file=index_file_class() 403 _filelists={} 404 for _path in media_path: 405 _filelists.update(self.listfiles(_path)) 406 _files=_filelists.keys() 407 _files.sort() 408 for _f in _files: 409 _file_name=common.basename(_f) 410 if _file_name in excluded_files: 411 # do not include this one 412 continue 413 _entry=index_entry_class() 414 _entry.name=_file_name 415 _entry.pathname=_f 416 _index_file.items.append(_entry) 417 _buf=prototypes.buffer() 418 _index_file.writetobuffer(_buf) 419 self.writefile(index_file_name, _buf.getvalue())
420 ## file(common.basename(index_file_name), 'wb').write(_buf.getvalue()) 421
422 - def saveringtones(self, fundamentals, merge):
423 """Save ringtones to the phone""" 424 self.log('Writing ringtones to the phone') 425 try: 426 _del_list, _new_list=self._get_del_new_list('ringtone-index', 427 'ringtone', 428 merge, 429 fundamentals) 430 if __debug__: 431 self.log('Delete list: '+','.join(_del_list)) 432 self.log('New list: '+','.join(_new_list)) 433 self._replace_files('ringtone-index', 'ringtone', 434 _new_list, fundamentals) 435 self._del_files('ringtone-index', 436 _del_list, fundamentals) 437 self._add_files('ringtone-index', 'ringtone', 438 _new_list, fundamentals) 439 self._update_media_index(self.protocolclass.WRingtoneIndexFile, 440 self.protocolclass.WRingtoneIndexEntry, 441 [self.protocolclass.RT_PATH, 442 self.protocolclass.RT_PATH2], 443 self.protocolclass.RT_EXCLUDED_FILES, 444 self.protocolclass.RT_INDEX_FILE_NAME) 445 self._update_media_index(self.protocolclass.WSoundsIndexFile, 446 self.protocolclass.WSoundsIndexEntry, 447 [self.protocolclass.SND_PATH, 448 self.protocolclass.SND_PATH2], 449 self.protocolclass.SND_EXCLUDED_FILES, 450 self.protocolclass.SND_INDEX_FILE_NAME) 451 fundamentals['rebootphone']=True 452 except: 453 if __debug__: 454 raise 455 return fundamentals
456 457 # Wallpaper stuff-----------------------------------------------------------
458 - def getwallpapers(self, fundamentals):
459 # reading pictures & wallpapers 460 return self._get_media_from_index('wallpaper-index', 'wallpapers', 461 fundamentals)
462
463 - def savewallpapers(self, fundamentals, merge):
464 # send wallpapers to the phone 465 """Save ringtones to the phone""" 466 self.log('Writing wallpapers to the phone') 467 try: 468 _del_list, _new_list=self._get_del_new_list('wallpaper-index', 469 'wallpapers', 470 merge, 471 fundamentals) 472 if __debug__: 473 self.log('Delete list: '+','.join(_del_list)) 474 self.log('New list: '+','.join(_new_list)) 475 self._replace_files('wallpaper-index', 'wallpapers', 476 _new_list, fundamentals) 477 self._del_files('wallpaper-index', 478 _del_list, fundamentals) 479 self._add_files('wallpaper-index', 'wallpapers', 480 _new_list, fundamentals) 481 self._update_media_index(self.protocolclass.WPictureIndexFile, 482 self.protocolclass.WPictureIndexEntry, 483 [self.protocolclass.PIC_PATH, 484 self.protocolclass.PIC_PATH2], 485 self.protocolclass.PIC_EXCLUDED_FILES, 486 self.protocolclass.PIC_INDEX_FILE_NAME) 487 fundamentals['rebootphone']=True 488 except: 489 if __debug__: 490 raise 491 return fundamentals
492 493 # Calendar stuff------------------------------------------------------------
494 - def _read_calendar_index(self):
495 return self.readobject(self.protocolclass.CAL_INDEX_FILE_NAME, 496 self.protocolclass.CalIndexFile, 497 'Reading Calendar Index File')
498
499 - def getcalendar(self, fundamentals):
500 self.log('Reading calendar') 501 _cal_index=self._read_calendar_index() 502 _res={} 503 _buf=prototypes.buffer() 504 for _cnt in range(_cal_index.numofevents): 505 _cal_file_name='%s%04d'%(self.protocolclass.CAL_FILE_NAME_PREFIX, 506 _cal_index.events[_cnt].index) 507 _buf.reset(self.getfilecontents(_cal_file_name)) 508 _bpcal=self.calendarclass(self, _buf, fundamentals).getvalue() 509 _res[_bpcal.id]=_bpcal 510 fundamentals['calendar']=_res 511 return fundamentals
512
513 - def _del_existing_cal_entries(self):
514 self.log('Deleting existing calendar entries') 515 _cal_index=self._read_calendar_index() 516 for _idx in range(_cal_index.numofevents): 517 _cal_file_name='%s%04d'%(self.protocolclass.CAL_FILE_NAME_PREFIX, 518 _cal_index.events[_idx].index) 519 try: 520 self.rmfile(_cal_file_name) 521 except: 522 self.log('Failed to delete file: '+_cal_file_name) 523 return _cal_index.next_index
524
525 - def _write_cal_entries(self, next_index, fundamentals):
526 # write each and every calendar entries, each in a separate file 527 _cal_dict=fundamentals.get('calendar', {}) 528 _idx=next_index 529 _cnt=0 530 for _key,_entry in _cal_dict.items(): 531 if _cnt>=self.protocolclass.CAL_MAX_EVENTS: 532 # enough events already! 533 break 534 try: 535 _cal_entry=self.calendarclass(self, _entry, fundamentals) 536 _cal_file_name='%s%04d'%(self.protocolclass.CAL_FILE_NAME_PREFIX, 537 _idx) 538 self.writeobject(_cal_file_name, _cal_entry, 539 'Writing Calendar Entry') 540 _idx+=1 541 _cnt+=1 542 except: 543 self.log('Failed to write calendar entry') 544 if __debug__: 545 raise 546 return _idx
547
548 - def _write_cal_index(self, next_index, fundamentals):
549 _cal_index=self._read_calendar_index() 550 # clear out the old entries 551 for _idx in range(_cal_index.numofevents): 552 _cal_index.events[_idx].index=0 553 for _idx in range(_cal_index.numofactiveevents): 554 _cal_index.activeevents[_idx].index=0 555 # update with new info 556 _old_next_index=_cal_index.next_index 557 _num_entries=next_index-_old_next_index 558 _cal_index.next_index=next_index 559 _cal_index.numofevents=_num_entries 560 _cal_index.numofactiveevents=_num_entries 561 _cnt=0 562 for _idx in range(_old_next_index, next_index): 563 _cal_index.events[_cnt].index=_idx 564 _cal_index.activeevents[_cnt].index=_idx 565 _cnt+=1 566 self.writeobject(self.protocolclass.CAL_INDEX_FILE_NAME, 567 _cal_index, 568 'Writing Calendar Index File')
569
570 - def savecalendar(self, fundamentals, merge):
571 self.log("Sending calendar entries") 572 _next_idx=self._del_existing_cal_entries() 573 _next_idx=self._write_cal_entries(_next_idx, fundamentals) 574 self._write_cal_index(_next_idx, fundamentals) 575 # need to reboot the phone afterward 576 fundamentals['rebootphone']=True 577 return fundamentals
578 579 # Memo/Notepad stuff--------------------------------------------------------
580 - def getmemo(self, fundamentals):
581 self.log('Reading note pad items') 582 _index_file=self._read_calendar_index() 583 _res={} 584 for _idx in range(_index_file.numofnotes): 585 _file_name='%s%04d'%(self.protocolclass.NP_FILE_NAME_PREFIX, 586 _index_file.notes[_idx].index) 587 _note=self.readobject(_file_name, 588 self.protocolclass.NotePadEntry) 589 _memo=memo.MemoEntry() 590 _memo.text=_note.text 591 _memo.set_date_isostr('%04d%02d%02dT%02d%02d00'%_note.modified[:5]) 592 _res[_memo.id]=_memo 593 fundamentals['memo']=_res 594 return fundamentals
595
596 - def _del_existing_memo_entries(self):
597 self.log('Deleting existing memo entries') 598 _file_index=self._read_calendar_index() 599 for _idx in range(_file_index.numofnotes): 600 _file_name='%s%04d'%(self.protocolclass.NP_FILE_NAME_PREFIX, 601 _file_index.notes[_idx].index) 602 try: 603 self.rmfile(_file_name) 604 except: 605 self.log('Failed to delete file: '+_file_name) 606 return _file_index.next_index
607
608 - def _write_memo_entries(self, next_index, fundamentals):
609 # write each and every memo entries, each in a separate file 610 _memo_dict=fundamentals.get('memo', {}) 611 _idx=next_index 612 _cnt=0 613 for _key,_entry in _memo_dict.items(): 614 if _cnt>=self.protocolclass.NP_MAX_ENTRIES: 615 # enough memo already! 616 break 617 try: 618 _memo_entry=self.protocolclass.NotePadEntry() 619 _text_len=min(self.protocolclass.NP_MAX_LEN, 620 len(_entry.text)) 621 _memo_entry.textlen=_text_len 622 _memo_entry.text=_entry.text[:_text_len] 623 _memo_entry.creation=self._time_now() 624 _file_name='%s%04d'%(self.protocolclass.NP_FILE_NAME_PREFIX, 625 _idx) 626 self.writeobject(_file_name, _memo_entry, 627 logtitle='Writing memo entry') 628 _idx+=1 629 _cnt+=1 630 except: 631 self.log('Failed to write memo endar entry') 632 if __debug__: 633 raise 634 return _idx
635
636 - def _write_memo_index(self, next_index, fundamentals):
637 _file_index=self._read_calendar_index() 638 # clear out the old entries 639 for _idx in range(_file_index.numofnotes): 640 _file_index.notes[_idx].index=0 641 # update with new info 642 _old_next_index=_file_index.next_index 643 _num_entries=next_index-_old_next_index 644 _file_index.next_index=next_index 645 _file_index.numofnotes=_num_entries 646 _cnt=0 647 for _idx in range(_old_next_index, next_index): 648 _file_index.notes[_cnt].index=_idx 649 _cnt+=1 650 self.writeobject(self.protocolclass.CAL_INDEX_FILE_NAME, 651 _file_index, 652 logtitle='Writing calendar/memo file index')
653
654 - def savememo(self, fundamentals, merge):
655 self.log('Writing memo/notepad items') 656 _next_index=self._del_existing_memo_entries() 657 _next_index=self._write_memo_entries(_next_index, fundamentals) 658 self._write_memo_index(_next_index, fundamentals) 659 fundamentals['rebootphone']=True 660 return fundamentals
661 662 # Phone Detection----------------------------------------------------------- 663 my_model='SCH-A950/DM' 664 my_manufacturer='SAMSUNG' 665 detected_model='A950'
666 - def is_mode_brew(self):
667 # Borrowed from the VX4400 668 req=self.protocolclass.memoryconfigrequest() 669 respc=self.protocolclass.memoryconfigresponse 670 for baud in 0, 38400, 115200: 671 if baud: 672 if not self.comm.setbaudrate(baud): 673 continue 674 try: 675 self.sendbrewcommand(req, respc, callsetmode=False) 676 return True 677 except (com_phone.modeignoreerrortypes, 678 ValueError): 679 pass 680 return False
681 - def check_my_phone(self, res):
682 # check if this is an A950 683 try: 684 _req=self.protocolclass.firmwarerequest() 685 _resp=self.sendbrewcommand(_req, self.protocolclass.DefaultResponse) 686 if _resp.data[31:35]==self.detected_model: 687 # yup, this's it! 688 res['model']=self.my_model 689 res['manufacturer']=self.my_manufacturer 690 res['esn']=self.get_esn() 691 except: 692 if __debug__: 693 raise
694 @classmethod
695 - def detectphone(_, coms, likely_ports, res, _module, _log):
696 if not likely_ports: 697 # cannot detect any likely ports 698 return None 699 for port in likely_ports: 700 if not res.has_key(port): 701 res[port]={ 'mode_modem': None, 'mode_brew': None, 702 'manufacturer': None, 'model': None, 703 'firmware_version': None, 'esn': None, 704 'firmwareresponse': None } 705 try: 706 if res[port]['mode_brew']==False or \ 707 res[port]['model']: 708 # either phone is not in BREW, or a model has already 709 # been found, not much we can do now 710 continue 711 p=_module.Phone(_log, commport.CommConnection(_log, port, timeout=1)) 712 if res[port]['mode_brew'] is None: 713 res[port]['mode_brew']=p.is_mode_brew() 714 if res[port]['mode_brew']: 715 p.check_my_phone(res[port]) 716 p.comm.close() 717 except: 718 if __debug__: 719 raise
720 721 #Phonebook stuff------------------------------------------------------------
722 - def _del_private_dicts(self, fundamentals):
723 # delete the stuff that we created 724 for _key in ('ringtone-range', 'wallpaper-range'): 725 if fundamentals.has_key(_key): 726 del fundamentals[_key]
727
728 - def _extract_entries(self, filename, res, fundamentals):
729 try: 730 _buf=prototypes.buffer(self.getfilecontents(filename)) 731 _rec_file=self.protocolclass.PBFileHeader() 732 _rec_file.readfrombuffer(_buf) 733 _entry=self.protocolclass.PBEntry() 734 for _len in _rec_file.lens: 735 if _len.itemlen: 736 _buf_ofs=_buf.offset 737 _entry.readfrombuffer(_buf) 738 _buf.offset=_buf_ofs+_len.itemlen 739 res[len(res)]=self.pbentryclass(self, _entry, 740 fundamentals).getvalue() 741 except: 742 self.log('Failed to read file: %s'%filename) 743 if __debug__: 744 raise
745
746 - def getphonebook(self, fundamentals):
747 self.log('Reading phonebook contacts') 748 _file_cnt=0 749 _res={} 750 while True: 751 _file_name='%s%04d'%(self.protocolclass.PB_ENTRY_FILE_PREFIX, 752 _file_cnt) 753 if self.exists(_file_name): 754 self._extract_entries(_file_name, _res, fundamentals) 755 _file_cnt+=1 756 else: 757 break 758 fundamentals['phonebook']=_res 759 fundamentals['categories']=[x['name'] for _,x in \ 760 fundamentals.get('groups', {}).items()] 761 self._del_private_dicts(fundamentals) 762 return fundamentals
763
764 - def _get_wp_filename(self, wp, wp_index):
765 # return the filename associated with this wallpaper 766 for _,_entry in wp_index.items(): 767 if _entry.get('name', None)==wp: 768 return _entry.get('filename', None)
769
770 - def _rescale_and_cache(self, wp, filename, idx, 771 fundamentals):
772 # rescale the wp and add it to the cache dir 773 try: 774 _data=self.getfilecontents(filename, True) 775 _tmpname=common.gettempfilename('tmp') 776 file(_tmpname, 'wb').write(_data) 777 _img=wx.Image(_tmpname) 778 if not _img.Ok(): 779 self.log('Failed to understand image: '+filename) 780 return 781 _img.Rescale(self.protocolclass.PB_WP_CACHE_WIDTH, 782 self.protocolclass.PB_WP_CACHE_HEIGHT) 783 _img.SaveFile(_tmpname, wx.BITMAP_TYPE_JPEG) 784 _newfilename=self.protocolclass.PB_WP_CACHE_PATH+'/$'+filename.replace('/', '$') 785 _data=file(_tmpname, 'rb').read() 786 self.writefile(_newfilename, _data) 787 return _newfilename 788 except: 789 if __debug__: 790 self.log('Failed to add cache image: '+wp) 791 raise
792
793 - def _add_wp_cache(self, wp, idx, fundamentals):
794 # check to see if it already exists 795 _wp_range=fundamentals.get('wallpaper-range', {}) 796 if _wp_range.has_key(wp): 797 # already in there 798 return 799 # add this wallpaper into the cache dir 800 _wp_index=fundamentals.get('wallpaper-index', {}) 801 # look for the file name 802 _filename=self._get_wp_filename(wp, _wp_index) 803 if not _filename: 804 # couldn't find the filename 805 return 806 # copy the image file, rescale, and put it in the cache dir 807 _newfilename=self._rescale_and_cache(wp, _filename, idx, fundamentals) 808 if _newfilename: 809 # rescale successful, update the dict 810 _wp_range[wp]='/ff/'+_newfilename 811 fundamentals['wallpaper-range']=_wp_range
812
813 - def get_wallpaper_range(self, wallpaper, fundamentals):
814 # return the wallpaper cache name for the specific wallpaper 815 return fundamentals.get('wallpaper-range', {}).get(wallpaper, None)
816
817 - def savephonebook(self, fundamentals):
818 self.log('Writing phonebook contacts') 819 self._read_ringtone_range(fundamentals) 820 _pb_dict=fundamentals.get('phonebook', {}) 821 # alphabetize the list based on name 822 _pb_list=[(nameparser.getfullname(_entry['names'][0]), _key) \ 823 for _key,_entry in _pb_dict.items()] 824 _pb_list.sort() 825 _req=self.protocolclass.ss_pb_clear_req() 826 _rp=self.sendbrewcommand(_req, self.protocolclass.ss_pb_clear_resp) 827 if _rp.flg: 828 self.log('Failed to clear phonebook') 829 self._del_private_dicts(fundamentals) 830 return fundamentals 831 _req=self.protocolclass.ss_pb_write_req() 832 _total_cnt=len(_pb_list) 833 _cnt=1 834 for _name,_key in _pb_list: 835 try: 836 _entry=_pb_dict[_key] 837 # set up all the picture ID (wallpaper) images 838 _wp=_entry.get('wallpapers', [{}])[0].get('wallpaper', None) 839 if _wp: 840 self._add_wp_cache(_wp, _cnt, fundamentals) 841 # setting up a new contact to send over 842 _pbentry=self.pbentryclass(self, _entry, fundamentals) 843 _req.entry=_pbentry.pb 844 _cnt+=1 845 self.progress(_cnt, _total_cnt, 846 'Writing entry" %s'%_req.entry.name) 847 _resp=self.sendbrewcommand(_req, 848 self.protocolclass.ss_pb_write_resp) 849 except: 850 self.log('Failed to write entry') 851 if __debug__: 852 raise 853 fundamentals['rebootphone']=True 854 self._del_private_dicts(fundamentals) 855 return fundamentals
856 857 # Call History stuff--------------------------------------------------------
858 - def _get_ch_index(self):
859 # read the index file and return the number of incoming, outgoing, and 860 # missed calls 861 try: 862 _req=self.readobject(self.protocolclass.CL_INDEX_FILE, 863 self.protocolclass.cl_index_file, 864 logtitle='Reading Call Log Index File') 865 return ([x.index for x in _req.incoming[:_req.incoming_count]], 866 [x.index for x in _req.outgoing[:_req.outgoing_count]], 867 [x.index for x in _req.missed[:_req.missed_count]]) 868 except com_brew.BrewNoSuchFileException: 869 return ([], [], []) 870 except: 871 if __debug__: 872 raise 873 return ([], [], [])
874 - def _get_ch(self, call_list, folder, res):
875 # read the call history files 876 _req=self.protocolclass.cl_file() 877 _buf=prototypes.buffer() 878 for _idx in call_list: 879 try: 880 _buf.reset(self.getfilecontents( 881 '%s%02d'%(self.protocolclass.CL_PREFIX, _idx))) 882 _req.readfrombuffer(_buf, 'Reading Call Log File') 883 if _req.valid: 884 _entry=call_history.CallHistoryEntry() 885 _entry.folder=folder 886 _entry.number=_req.number 887 _entry.datetime=_req.datetime 888 if _req.duration: 889 _entry.duration=_req.duration 890 res[_entry.id]=_entry 891 except com_brew.BrewNoSuchFileException: 892 pass 893 except: 894 if __debug__: 895 raise
896
897 - def getcallhistory(self, fundamentals):
898 # retrieve the call history data from the phone 899 res={} 900 _incoming_list, _outgoing_list, _missed_list=self._get_ch_index() 901 self._get_ch(_incoming_list, 902 call_history.CallHistoryEntry.Folder_Incoming, res) 903 self._get_ch(_outgoing_list, 904 call_history.CallHistoryEntry.Folder_Outgoing, res) 905 self._get_ch(_missed_list, 906 call_history.CallHistoryEntry.Folder_Missed, res) 907 fundamentals['call_history']=res
908 909 # SMS Stuff----------------------------------------------------------------
910 - def _build_common_msg(self, entry, sms_hdr):
911 entry.text=sms_hdr.body.msg 912 entry.datetime=sms_hdr.body.datetime[:5] 913 if sms_hdr.body.has_callback: 914 entry.callback=sms_hdr.body.callback 915 if sms_hdr.body.has_priority: 916 if sms_hdr.body.priority: 917 entry.priority=sms.SMSEntry.Priority_High 918 else: 919 entry.priority=sms.SMSEntry.Priority_Normal
920
921 - def _build_locked_field(self, entry, buf):
922 _locked=self.protocolclass.UINT(sizeinbytes=4) 923 _locked.readfrombuffer(buf) 924 entry.locked=bool(_locked.getvalue())
925
926 - def _build_in_msg(self, sms_hdr, buf, res):
927 _entry=sms.SMSEntry() 928 _entry.folder=sms.SMSEntry.Folder_Inbox 929 _entry._from=sms_hdr.body.addr0 930 self._build_common_msg(_entry, sms_hdr) 931 _entry.read=sms_hdr.body.msg_stat[0].status==self.protocolclass.SMS_STATUS_READ 932 self._build_locked_field(_entry, buf) 933 res[_entry.id]=_entry
934
935 - def _build_sent_msg(self, sms_hdr, buf, res):
936 _entry=sms.SMSEntry() 937 _entry.folder=sms.SMSEntry.Folder_Sent 938 self._build_common_msg(_entry, sms_hdr) 939 _confirmed_flg=False 940 for _stat in sms_hdr.body.msg_stat: 941 if _stat.status==self.protocolclass.SMS_STATUS_DELIVERED: 942 _confirmed_flg=True 943 break 944 if _confirmed_flg: 945 _datetime_list=self.protocolclass.sms_delivered_datetime() 946 _datetime_list.readfrombuffer(buf, 'Reading Confirmed Datetime field') 947 for _idx in range(10): 948 if getattr(sms_hdr.body, 'addr_len%d'%_idx): 949 if sms_hdr.body.msg_stat[_idx].status==self.protocolclass.SMS_STATUS_DELIVERED: 950 _entry.add_recipient(getattr(sms_hdr.body, 'addr%d'%_idx), 951 True, 952 _datetime_list.datetime[_idx].datetime[:5]) 953 else: 954 _entry.add_recipient(getattr(sms_hdr.body, 'addr%d'%_idx)) 955 self._build_locked_field(_entry, buf) 956 res[_entry.id]=_entry
957
958 - def _build_draft_msg(self, sms_hdr, buf, res):
959 _entry=sms.SMSEntry() 960 _entry.folder=sms.SMSEntry.Folder_Saved 961 self._build_common_msg(_entry, sms_hdr) 962 self._build_locked_field(_entry, buf) 963 for _idx in range(10): 964 if getattr(sms_hdr.body, 'addr_len%d'%_idx): 965 _entry.add_recipient(getattr(sms_hdr.body, 'addr%d'%_idx)) 966 res[_entry.id]=_entry
967
968 - def _read_sms(self, filename, res, fundamentals):
969 _buf=prototypes.buffer(self.getfilecontents(filename)) 970 _sms=self.protocolclass.sms_header() 971 _sms.readfrombuffer(_buf, 'Reading SMS File') 972 if not _sms.is_txt_msg.value: 973 # not a text message 974 return 975 if _sms.in_msg.value: 976 self._build_in_msg(_sms, _buf, res) 977 elif _sms.sent_msg.value: 978 self._build_sent_msg(_sms, _buf, res) 979 else: 980 self._build_draft_msg(_sms, _buf, res)
981
982 - def getsms(self, fundamentals):
983 res={} 984 for _filename in self.listfiles(self.protocolclass.SMS_PATH): 985 try: 986 self._read_sms(_filename, res, fundamentals) 987 except: 988 self.log('Failed to read SMS File '+_filename) 989 if __debug__: 990 raise 991 fundamentals['sms']=res 992 fundamentals['canned_msg']=[] 993 return fundamentals
994
995 # CalendarEntry class----------------------------------------------------------- 996 -class CalendarEntry(object):
997 """Transient class to handle calendar data being sent to, retrieved from 998 the phone. 999 """ 1000 # Repeat Constants 1001 REP_NONE=0 1002 REP_ONCE=0 1003 REP_DAILY=2 1004 REP_WEEKLY=5 1005 REP_MONTHLY=6 1006 REP_YEARLY=7 1007 # Alarm constants 1008 ALARM_ONTIME=0 1009 ALARM_5M=1 1010 ALARM_10M=2 1011 ALARM_15M=3 1012 ALARM_30M=4 1013 ALARM_1HR=5 1014 ALARM_3HR=6 1015 ALARM_5HR=7 1016 ALARM_1D=8 1017 # Alert 1018 ALERT_TONE=0 1019 ALERT_VIBRATE=1 1020 ALERT_LIGHT=2 1021 # Timezone 1022 TZ_EST=0 1023 TZ_EDT=1 1024 TZ_CST=2 1025 TZ_CDT=3 1026 TZ_MST=4 1027 TZ_MDT=5 1028 TZ_PST=6 1029 TZ_PDT=7 1030 TZ_AKST=8 1031 TZ_AKDT=9 1032 TZ_HAST=10 1033 TZ_HADT=11 1034 TZ_GMT=12
1035 - def __init__(self, phone, value, fundamentals):
1036 self.phone=phone 1037 self.fundamentals=fundamentals 1038 self.cal=phone.protocolclass.CalEntry() 1039 if isinstance(value, bpcalendar.CalendarEntry): 1040 self._build(value) 1041 elif isinstance(value, prototypes.buffer): 1042 self.cal.readfrombuffer(value) 1043 else: 1044 raise TypeError('Expecting type bpcalendar.CalendarEntry or prototypes.buffer')
1045
1046 - def writetobuffer(self, buf, logtitle=None):
1047 self.cal.writetobuffer(buf, logtitle=logtitle)
1048 1049 # building routines--------------------------------------------------------- 1050 _build_repeat_dict={ 1051 bpcalendar.RepeatEntry.daily: REP_DAILY, 1052 bpcalendar.RepeatEntry.weekly: REP_WEEKLY, 1053 bpcalendar.RepeatEntry.monthly: REP_MONTHLY, 1054 bpcalendar.RepeatEntry.yearly: REP_YEARLY, 1055 } 1056 _build_alarm_dict={ 1057 0: ALARM_ONTIME, 1058 5: ALARM_5M, 1059 10: ALARM_10M, 1060 15: ALARM_15M, 1061 30: ALARM_30M, 1062 60: ALARM_1HR, 1063 180: ALARM_3HR, 1064 300: ALARM_5HR, 1065 1440: ALARM_1D, 1066 } 1067 _build_tz_dict={ 1068 0: TZ_GMT, 1069 18000: TZ_EST, 1070 21600: TZ_CST, 1071 25200: TZ_MST, 1072 28800: TZ_PST, 1073 32400: TZ_AKST, 1074 36000: TZ_HAST, 1075 }
1076 - def _build_duration(self, entry):
1077 return (datetime.datetime(*entry.end)-\ 1078 datetime.datetime(*entry.start)).seconds
1079 - def _build_repeat(self, entry):
1080 rep=entry.repeat 1081 if not rep: 1082 return self.REP_ONCE 1083 return self._build_repeat_dict.get(rep.repeat_type, self.REP_ONCE)
1084 - def _build_alarm(self, entry):
1085 _keys=self._build_alarm_dict.keys() 1086 _keys.sort() 1087 _alarm=entry.alarm 1088 for k in _keys: 1089 if _alarm<=k: 1090 return self._build_alarm_dict[k] 1091 return self.ALARM_ONTIME
1092 - def _build_alert(self, entry):
1093 if entry.vibrate: 1094 return self.ALERT_VIBRATE 1095 return self.ALERT_TONE
1096 _tz_code=None
1097 - def _build_tz(self):
1098 if CalendarEntry._tz_code is None: 1099 CalendarEntry._tz_code=self._build_tz_dict.get(time.timezone, 1100 self.TZ_EST) 1101 if time.localtime()[-1]==1: 1102 # daylight saving time 1103 CalendarEntry._tz_code+=1 1104 return CalendarEntry._tz_code
1105
1106 - def _build(self, entry):
1107 # populate this object with data from BitPim 1108 self.cal.titlelen=len(entry.desc_loc) 1109 self.cal.title=entry.desc_loc 1110 self.cal.start=entry.start 1111 self.cal.exptime=entry.end[3:5] 1112 self.cal.repeat=self._build_repeat(entry) 1113 self.cal.alarm=self._build_alarm(entry) 1114 self.cal.alert=self._build_alert(entry) 1115 self.cal.duration=self._build_duration(entry) 1116 self.cal.timezone=self._build_tz() 1117 _now=self.phone._time_now() 1118 self.cal.creationtime=_now 1119 self.cal.modifiedtime=_now 1120 _ringtone=self.phone.get_ringtone_range(entry.ringtone, 1121 self.fundamentals) 1122 self.cal.ringtonelen=len(_ringtone) 1123 self.cal.ringtone=_ringtone
1124 1125 # Extracting routine--------------------------------------------------------
1126 - def _extract_end(self):
1127 return (datetime.datetime(*self.cal.start)+\ 1128 datetime.timedelta(seconds=self.cal.duration)).timetuple()[:5]
1129 - def _extract_alarm(self):
1130 for _value,_code in self._build_alarm_dict.items(): 1131 if self.cal.alarm==_code: 1132 return _value
1133 - def _extract_repeat(self):
1134 if self.cal.repeat==self.REP_ONCE: 1135 return None 1136 _rep_type=None 1137 for _type, _code in self._build_repeat_dict.items(): 1138 if self.cal.repeat==_code: 1139 _rep_type=_type 1140 break 1141 if not _rep_type: 1142 return None 1143 _rep=bpcalendar.RepeatEntry(_rep_type) 1144 if _rep_type==_rep.daily: 1145 _rep.interval=1 1146 elif _rep_type==_rep.weekly: 1147 _rep.interval=1 1148 elif _rep_type==_rep.monthly: 1149 _rep.interval2=1 1150 _rep.dow=0 1151 return _rep
1152
1153 - def getvalue(self):
1154 # return a BitPim calendar entry equivalence 1155 _entry=bpcalendar.CalendarEntry() 1156 _entry.desc_loc=self.cal.title 1157 _entry.start=self.cal.start 1158 _entry.end=self._extract_end() 1159 _entry.alarm=self._extract_alarm() 1160 _entry.repeat=self._extract_repeat() 1161 if _entry.repeat: 1162 # forever repeat event 1163 _entry.end=_entry.no_end_date+_entry.end[3:] 1164 _entry.ringtone=self.phone.ringtone_name_from_range(self.cal.ringtone, 1165 self.fundamentals) 1166 _entry.vibrate=self.cal.alert==self.ALERT_VIBRATE 1167 return _entry
1168
1169 # PBEntry class----------------------------------------------------------------- 1170 -class PBEntry(object):
1171
1172 - def __init__(self, phone, data, fundamentals):
1173 self.phone=phone 1174 self.fundamentals=fundamentals 1175 if isinstance(data, phone.protocolclass.PBEntry): 1176 self.pb=data 1177 elif isinstance(data, dict): 1178 # assume it's a phonebook dict 1179 self.pb=phone.protocolclass.ss_pb_entry() 1180 self._build(data) 1181 else: 1182 raise TypeError('Should be PBEntry or phone dict')
1183
1184 - def writetobuffer(self, buf, logtitle=None):
1185 self.pb.writetobuffer(buf, logtitle=logtitle)
1186 1187 # Building a phonebook rec from a bp phone dict----------------------------- 1188 _pb_type_dict={ 1189 'home': 'home', 1190 'office': 'work', 1191 'cell': 'cell', 1192 'fax': 'fax', 1193 }
1194 - def _build_number(self, number, ringtone, primary):
1195 # build a number rec 1196 _num_type=self._pb_type_dict.get(number['type'], None) 1197 if not _num_type: 1198 # we don's support this type 1199 return 1200 # check for cell2 1201 if _num_type=='cell' and self.pb.cell.number: 1202 _num_type='cell2' 1203 # build a number entry 1204 _entry=self.phone.protocolclass.ss_number_entry() 1205 _entry.number=number['number'] 1206 _sd=number.get('speeddial', None) 1207 if ringtone: 1208 _rt=self.phone.get_ringtone_range(ringtone, self.fundamentals) 1209 else: 1210 _rt=None 1211 if _sd is not None: 1212 _entry.speeddial=_sd 1213 if _rt is not None: 1214 _entry.ringtone=_rt 1215 if primary: 1216 _entry.primary=1 1217 # add it to the contact 1218 setattr(self.pb, _num_type, _entry)
1219
1220 - def _build_email(self, emails):
1221 # build an email rec 1222 if len(emails) and emails[0].get('email', None): 1223 # at least 1 email 1224 self.pb.email=emails[0]['email'] 1225 if len(emails)>1 and emails[1].get('email', None): 1226 # 2 or more emails 1227 self.pb.email2=emails[1]['email']
1228
1229 - def _build_group(self, cat):
1230 # set the group if specified 1231 if not cat: 1232 return 1233 _cat_list=self.fundamentals.get('groups', {}) 1234 for _key,_cat in _cat_list.items(): 1235 if _key and _cat.get('name', None)==cat: 1236 self.pb.group=_key 1237 break
1238
1239 - def _build_wallpaper(self, wallpaper):
1240 # set the wallpaper if specified 1241 if not wallpaper: 1242 return 1243 _wp=self.phone.get_wallpaper_range(wallpaper, self.fundamentals) 1244 if _wp: 1245 self.pb.wallpaper=_wp
1246
1247 - def _build(self, entry):
1248 # Build a phone dict base on the phone data 1249 self.pb.name=nameparser.getfullname(entry['names'][0]) 1250 # global ringtone 1251 _ringtone=entry.get('ringtones', [{}])[0].get('ringtone', None) 1252 # build the numbers 1253 _primary=True # the first number is the primary one 1254 for _number in entry.get('numbers', []): 1255 self._build_number(_number, _ringtone, _primary) 1256 _primary=False 1257 # build the email 1258 self._build_email(entry.get('emails', [])) 1259 # group 1260 self._build_group(entry.get('categories', [{}])[0].get('category', None)) 1261 # wallpaper 1262 self._build_wallpaper(entry.get('wallpapers', [{}])[0].get('wallpaper', None))
1263 1264 # Extracting data from the phone--------------------------------------------
1265 - def _extract_emails(self, entry, p_class):
1266 # extract emails 1267 if self.pb.has_email: 1268 entry['emails']=[{ 'email': self.pb.email }] 1269 if self.pb.has_email2: 1270 entry.setdefault('emails', []).append({ 'email': self.pb.email2 })
1271 _number_type_dict={ 1272 'cell': (Phone.protocolclass.PB_FLG_CELL, 'cell'), 1273 'home': (Phone.protocolclass.PB_FLG_HOME, 'home'), 1274 'work': (Phone.protocolclass.PB_FLG_WORK, 'office'), 1275 'fax': (Phone.protocolclass.PB_FLG_FAX, 'fax'), 1276 'cell2': (Phone.protocolclass.PB_FLG_CELL2, 'cell'), 1277 }
1278 - def _extract_numbers(self, entry, p_class):
1279 # extract phone numbers 1280 entry['numbers']=[] 1281 for _key,_info_list in self._number_type_dict.items(): 1282 if self.pb.info&_info_list[0]: 1283 _num_entry=getattr(self.pb, _key) 1284 _number={ 'number': _num_entry.number, 1285 'type': _info_list[1] } 1286 if _num_entry.has_speeddial: 1287 _number['speeddial']=_num_entry.speeddial 1288 if _num_entry.has_ringtone and \ 1289 not entry.has_key('ringtones'): 1290 _ringtone=self.phone.ringtone_name_from_range( 1291 _num_entry.ringtone, self.fundamentals) 1292 if _ringtone: 1293 entry['ringtones']=[{ 'ringtone': _ringtone, 1294 'use': 'call' }] 1295 if _num_entry.is_primary: 1296 # this is the primary number, insert to the beginning 1297 entry['numbers']=[_number]+entry['numbers'] 1298 else: 1299 entry['numbers'].append(_number)
1300 - def _extract_group(self, entry, p_class):
1301 if not self.pb.has_group: 1302 # no group specified 1303 return 1304 _groups=self.fundamentals.get('groups', {}) 1305 if _groups.has_key(self.pb.group): 1306 entry['categories']=[{ 'category': _groups[self.pb.group]['name'] }]
1307 - def _extract_wallpaper(self, entry, p_class):
1308 if not self.pb.has_wallpaper: 1309 return 1310 _idx=self.pb.wallpaper.rfind('$')+1 1311 _wp=self.pb.wallpaper[_idx:] 1312 entry['wallpapers']=[{ 'wallpaper': _wp, 1313 'use': 'call' }]
1314 - def getvalue(self):
1315 _entry={} 1316 _p_class=self.phone.protocolclass 1317 _entry['names']=[{ 'full': self.pb.name }] 1318 self._extract_emails(_entry, _p_class) 1319 self._extract_numbers(_entry, _p_class) 1320 self._extract_group(_entry, _p_class) 1321 self._extract_wallpaper(_entry, _p_class) 1322 return _entry
1323 1324 #------------------------------------------------------------------------------- 1325 parentprofile=com_phone.Profile
1326 -class Profile(parentprofile):
1327 serialsname=Phone.serialsname 1328 WALLPAPER_WIDTH=176 1329 WALLPAPER_HEIGHT=220 1330 # 128x96: outside LCD 1331 autodetect_delay=3 1332 usbids=( ( 0x04e8, 0x6640, 2),) 1333 deviceclasses=("serial",) 1334 BP_Calendar_Version=3 1335 # For phone detection 1336 phone_manufacturer=Phone.my_manufacturer 1337 phone_model=Phone.my_model 1338 # arbitrary ringtone file size limit 1339 RINGTONE_LIMITS= { 1340 'MAXSIZE': 100000 1341 } 1342 WALLPAPER_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789 ._:" 1343 RINGTONE_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789 ._:" 1344
1345 - def __init__(self):
1346 parentprofile.__init__(self)
1347 1348 _supportedsyncs=( 1349 ('phonebook', 'read', None), # all phonebook reading 1350 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 1351 ('calendar', 'read', None), # all calendar reading 1352 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 1353 ('ringtone', 'read', None), # all ringtone reading 1354 ('ringtone', 'write', 'MERGE'), 1355 ('wallpaper', 'read', None), # all wallpaper reading 1356 ('wallpaper', 'write', None), 1357 ('memo', 'read', None), # all memo list reading DJP 1358 ('memo', 'write', 'OVERWRITE'), # all memo list writing DJP 1359 ('call_history', 'read', None),# all call history list reading 1360 ('sms', 'read', None), # all SMS list reading DJP 1361 ) 1362
1363 - def QueryAudio(self, origin, currentextension, afi):
1364 _max_size=self.RINGTONE_LIMITS['MAXSIZE'] 1365 setattr(afi, 'MAXSIZE', _max_size) 1366 # we don't modify any of these 1367 if afi.format in ("MIDI", "QCP", "PMD"): 1368 return currentextension, afi 1369 # examine mp3 1370 if afi.format=="MP3": 1371 if afi.channels==1 and 8<=afi.bitrate<=64 and 16000<=afi.samplerate<=22050: 1372 return currentextension, afi 1373 # convert it 1374 return ("mp3", fileinfo.AudioFileInfo(afi, **{'format': 'MP3', 1375 'channels': 2, 1376 'bitrate': 48, 1377 'samplerate': 44100, 1378 'MAXSIZE': _max_size }))
1379 1380 # all dumped in "images" 1381 imageorigins={} 1382 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images"))
1383 - def GetImageOrigins(self):
1384 return self.imageorigins
1385 1386 # our targets are the same for all origins 1387 imagetargets={} 1388 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", 1389 {'width': 176, 'height': 186, 'format': "JPEG"})) 1390 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "outsidelcd", 1391 {'width': 128, 'height': 96, 'format': "JPEG"})) 1392 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "fullscreen", 1393 {'width': 176, 'height': 220, 'format': "JPEG"}))
1394 - def GetTargetsForImageOrigin(self, origin):
1395 return self.imagetargets
1396
1397 - def convertphonebooktophone(self, helper, data):
1398 return data
1399 1400 field_color_data={ 1401 'phonebook': { 1402 'name': { 1403 'first': 1, 'middle': 1, 'last': 1, 'full': 1, 1404 'nickname': 0, 'details': 1 }, 1405 'number': { 1406 'type': 5, 'speeddial': 5, 'number': 5, 1407 'details': 5, 1408 'ringtone': False, 'wallpaper': False }, 1409 'email': 2, 1410 'email_details': { 1411 'emailspeeddial': False, 'emailringtone': False, 1412 'emailwallpaper': False }, 1413 'address': { 1414 'type': 0, 'company': 0, 'street': 0, 'street2': 0, 1415 'city': 0, 'state': 0, 'postalcode': 0, 'country': 0, 1416 'details': 0 }, 1417 'url': 0, 1418 'memo': 0, 1419 'category': 1, 1420 'wallpaper': 1, 1421 'ringtone': 1, 1422 'storage': 0, 1423 }, 1424 'calendar': { 1425 'description': True, 'location': True, 'allday': False, 1426 'start': True, 'end': True, 'priority': False, 1427 'alarm': True, 'vibrate': True, 1428 'repeat': True, 1429 'memo': False, 1430 'category': False, 1431 'wallpaper': False, 1432 'ringtone': True, 1433 }, 1434 'memo': { 1435 'subject': False, 1436 'date': False, 1437 'secret': False, 1438 'category': False, 1439 'memo': True, 1440 }, 1441 'todo': { 1442 'summary': False, 1443 'status': False, 1444 'due_date': False, 1445 'percent_complete': False, 1446 'completion_date': False, 1447 'private': False, 1448 'priority': False, 1449 'category': False, 1450 'memo': False, 1451 }, 1452 }
1453