PyXR

c:\projects\bitpim\src \ phones \ com_samsungscha950.py



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