PyXR

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



0001 ### BITPIM
0002 ###
0003 ### Copyright (C) 2006 Joe Pham <djpham@bitpim.org>
0004 ### Copyright (C) 2006 Stephen Wood <saw@bitpim.org>
0005 ###
0006 ### This program is free software; you can redistribute it and/or modify
0007 ### it under the terms of the BitPim license as detailed in the LICENSE file.
0008 ###
0009 ### $Id: com_samsungscha870.py 4569 2008-01-15 01:03:05Z djpham $
0010 
0011 """Communicate with the Samsung SCH-A870 Phone"""
0012 
0013 # System Models
0014 
0015 # BitPim modules
0016 import bpcalendar
0017 import common
0018 import helpids
0019 import com_samsungscha950 as com_a950
0020 import p_samsungscha950 as p_a950
0021 import p_samsungscha870 as p_a870
0022 import prototypes
0023 
0024 parentphone=com_a950.Phone
0025 class Phone(parentphone):
0026     desc='SCH-A870'
0027     helpid=helpids.ID_PHONE_SAMSUNGSCHA870
0028     protocolclass=p_a870
0029     serialsname='scha870'
0030 
0031     # Detection stuff
0032     my_model='SCH-A870/187'
0033     my_manufacturer='SAMSUNG'
0034     detected_model='A870'
0035 
0036     ringtone_noring_range='range_tones_preloaded_el_15'
0037     ringtone_default_range='range_tones_preloaded_el_01'
0038     builtin_ringtones={
0039         'VZW Default Tone': 0x52,
0040         'Melody 1': 0x56,
0041         'Melody 2': 0x57,
0042         'Melody 3': 0x58,
0043         'Melody 4': 0x59,
0044         'Melody 5': 0x5A,
0045         'Melody 6': 0x5B,
0046         'Bell 1': 0x53,
0047         'Bell 2': 0x54,
0048         'Bell 3': 0x55,
0049         'Beep Once': 0x93,
0050         'No Ring': 0xC2,
0051         'Default': None,
0052         }
0053     builtin_sounds={
0054         'Clapping': 0x5C,
0055         'Crowd': 0x5D,
0056         'Happy Birthday': 0x5E,
0057         'Rain Forest': 0x5F,
0058         'Train': 0x60,
0059         # same as ringtones ??
0060         }
0061     builtin_wallpapers={
0062         'No Picture': None,
0063         }
0064 
0065     def __init__(self, logtarget, commport):
0066         "Calls all the constructors and sets initial modes"
0067         parentphone.__init__(self, logtarget, commport)
0068         global PBEntry, CalendarEntry
0069         self.pbentryclass=PBEntry
0070         self.calendarclass=CalendarEntry
0071 
0072     def getfilecontents(self, filename, use_cache=False):
0073         if filename and filename[0]!='/':
0074             return parentphone.getfilecontents(self, '/'+filename, use_cache)
0075         return parentphone.getfilecontents(self, filename, use_cache)
0076 
0077     def get_groups(self):
0078         _res={}
0079         _buf=prototypes.buffer(self.getfilecontents(self.protocolclass.GROUP_INDEX_FILE_NAME))
0080         _index_file=self.protocolclass.GroupIndexFile()
0081         _index_file.readfrombuffer(_buf)
0082         for _entry in _index_file.items:
0083             if _entry.name:
0084                 _res[_entry.index-1]={ 'name': _entry.name }
0085         return _res
0086 
0087     def _get_dir_index(self, idx, result, pathname, origin, excludenames=()):
0088         # build the index list by listing contents of the specified dir
0089         for _path in self.listfiles(pathname):
0090             _file=common.basename(_path)
0091             if _file in excludenames:
0092                 continue
0093             result[idx]={ 'name': _file,
0094                           'filename': _path,
0095                           'origin': origin,
0096                           }
0097             idx+=1
0098         return idx
0099 
0100     def get_ringtone_index(self):
0101         _res={}
0102         _idx=self._get_builtin_ringtone_index(0, _res)
0103         _idx=self._get_dir_index(_idx, _res,
0104                                  self.protocolclass.RT_PATH, 'ringers',
0105                                  self.protocolclass.RT_EXCLUDED_FILES)
0106         _idx=self._get_dir_index(_idx, _res,
0107                                  self.protocolclass.SND_PATH, 'sounds',
0108                                  self.protocolclass.SND_EXCLUDED_FILES)
0109         return _res
0110 
0111     def _get_file_wallpaper_index(self, idx, result):
0112         try:
0113             _buf=prototypes.buffer(self.getfilecontents(self.protocolclass.PIC_INDEX_FILE_NAME))
0114         except (com_brew.BrewNoSuchFileException,
0115                 com_brew.BrewBadPathnameException,
0116                 com_brew.BrewFileLockedException,
0117                 com_brew.BrewAccessDeniedException):
0118             return idx
0119         except:
0120             if __debug__:
0121                 raise
0122             return idx
0123         _index_file=self.protocolclass.PictureIndexFile()
0124         _index_file.readfrombuffer(_buf)
0125         for _entry in _index_file.items:
0126             result[idx]={ 'name': _entry.name,
0127                           'filename': _entry.pathname,
0128                           'origin': 'images',
0129                           }
0130             idx+=1
0131         return idx
0132 
0133     def get_wallpaper_index(self):
0134         _res={}
0135         _idx=self._get_builtin_wallpaper_index(0, _res)
0136         _idx=self._get_file_wallpaper_index(_idx, _res)
0137         return _res
0138 
0139     def _get_del_new_list(self, index_key, media_key, merge, fundamentals,
0140                           origins):
0141         """Return a list of media being deleted and being added"""
0142         _index=fundamentals.get(index_key, {})
0143         _media=fundamentals.get(media_key, {})
0144         _index_file_list=[_entry['name'] for _entry in _index.values() \
0145                           if _entry.has_key('filename') and \
0146                           _entry.get('origin', None) in origins ]
0147         _bp_file_list=[_entry['name'] for _entry in _media.values() \
0148                        if _entry.get('origin', None) in origins ]
0149         if merge:
0150             # just add the new files, don't delete anything
0151             _del_list=[]
0152             _new_list=_bp_file_list
0153         else:
0154             # Delete specified files and add everything
0155             _del_list=[x for x in _index_file_list if x not in _bp_file_list]
0156             _new_list=_bp_file_list
0157         return _del_list, _new_list
0158 
0159     def saveringtones(self, fundamentals, merge):
0160         """Save ringtones to the phone"""
0161         self.log('Writing ringtones to the phone')
0162         try:
0163             _del_list, _new_list=self._get_del_new_list('ringtone-index',
0164                                                         'ringtone',
0165                                                         merge,
0166                                                         fundamentals,
0167                                                         frozenset(('ringers', 'sounds')))
0168             if __debug__:
0169                 self.log('Delete list: '+','.join(_del_list))
0170                 self.log('New list: '+','.join(_new_list))
0171             self._replace_files('ringtone-index', 'ringtone',
0172                                 _new_list, fundamentals)
0173             self._add_files('ringtone-index', 'ringtone',
0174                             _new_list, fundamentals)
0175             fundamentals['rebootphone']=True
0176         except:
0177             if __debug__:
0178                 raise
0179         return fundamentals
0180 
0181     def _add_files(self, index_key, media_key,
0182                    new_list, fundamentals):
0183         """Add new file using BEW"""
0184         _index=fundamentals.get(index_key, {})
0185         _media=fundamentals.get(media_key, {})
0186         _files_added=[]
0187         for _file in new_list:
0188             _data=self._item_from_index(_file, 'data', _media)
0189             if not _data:
0190                 self.log('Failed to write file %s due to no data'%_file)
0191                 continue
0192             if self._item_from_index(_file, None, _index) is None:
0193                 # new file
0194                 _origin=self._item_from_index(_file, 'origin', _media)
0195                 if _origin=='ringers':
0196                     _path=self.protocolclass.RT_PATH
0197                 elif _origin=='sounds':
0198                     _path=self.protocolclass.SND_PATH
0199                 elif _origin=='images':
0200                     _path=self.protocolclass.PIC_PATH
0201                 else:
0202                     selg.log('File %s has unknown origin, skip!'%_file)
0203                     continue
0204                 _file_name=_path+'/'+_file
0205                 try:
0206                     self.writefile(_file_name, _data)
0207                     _files_added.append({ 'filename': _file,
0208                                           'filesize': len(_data) })
0209                 except:
0210                     self.log('Failed to write file '+_file_name)
0211                     if __debug__:
0212                         raise
0213         return _files_added
0214 
0215     def _update_wp_index_file(self, filelist):
0216         # update the wp/picture index file with list of new files
0217         if not filelist:
0218             # no new files to update, bail
0219             return
0220         _index_file=self.protocolclass.PictureIndexFile()
0221         try:
0222             # read existing index items ...
0223             _data=self.getfilecontents(self.protocolclass.PIC_INDEX_FILE_NAME)
0224             if _data:
0225                 _index_file.readfrombuffer(prototypes.buffer(_data))
0226         except (com_brew.BrewNoSuchFileException,
0227                 com_brew.BrewBadPathnameException,
0228                 com_brew.BrewFileLockedException,
0229                 com_brew.BrewAccessDeniedException):
0230             pass
0231         # and append the new files
0232         for _fileitem in filelist:
0233             _index_file.items.append(self.protocolclass.PictureIndexEntry(**_fileitem))
0234         # and write out the new index file
0235         _buffer=prototypes.buffer()
0236         _index_file.writetobuffer(_buffer)
0237         self.writefile(self.protocolclass.PIC_INDEX_FILE_NAME,
0238                        _buffer.getvalue())
0239 
0240     def savewallpapers(self, fundamentals, merge):
0241         # send wallpapers to the phone
0242         """Save ringtones to the phone"""
0243         self.log('Writing wallpapers to the phone')
0244         try:
0245             _del_list, _new_list=self._get_del_new_list('wallpaper-index',
0246                                                         'wallpapers',
0247                                                         merge,
0248                                                         fundamentals,
0249                                                         frozenset(['images']))
0250             if __debug__:
0251                 self.log('Delete list: '+','.join(_del_list))
0252                 self.log('New list: '+','.join(_new_list))
0253             self._replace_files('wallpaper-index', 'wallpapers',
0254                                 _new_list, fundamentals)
0255             _files_added=self._add_files('wallpaper-index', 'wallpapers',
0256                                          _new_list, fundamentals)
0257             self._update_wp_index_file(_files_added)
0258             fundamentals['rebootphone']=True
0259         except:
0260             if __debug__:
0261                 raise
0262         return fundamentals
0263 
0264     def _read_ringtone_range(self, fundamentals):
0265         pass
0266     def _add_wp_cache(self, wp, idx, fundamentals):
0267         # check to see if it already exists
0268         pass
0269 
0270 # PBEntry class-----------------------------------------------------------------
0271 parentpbentry=com_a950.PBEntry
0272 class PBEntry(parentpbentry):
0273 
0274     # Building a phonebook rec from a bp phone dict-----------------------------
0275     def _build_number(self, number, ringtone, primary):
0276         # build a number rec
0277         _num_type=self._pb_type_dict.get(number['type'], None)
0278         if not _num_type:
0279             # we don's support this type
0280             return
0281         # check for cell2
0282         if _num_type=='cell' and self.pb.cell.number:
0283             _num_type='cell2'
0284         # build a number entry
0285         _entry=self.phone.protocolclass.ss_number_entry()
0286         _entry.number=number['number']
0287         _sd=number.get('speeddial', None)
0288         if _sd is not None:
0289             _entry.speeddial=_sd
0290         if primary:
0291             _entry.primary=1
0292         # add it to the contact
0293         setattr(self.pb, _num_type, _entry)
0294 
0295     def _build_wallpaper(self, wallpaper):
0296         # set the wallpaper if specified
0297         if not wallpaper:
0298             return
0299         for _rt in self.fundamentals.get('wallpaper-index', {}).values():
0300             if _rt.get('name', None)==wallpaper and \
0301                _rt.get('filename', None):
0302                 self.pb.wallpaper='%(name)s|%(pathname)s'% {
0303                     'name': _rt['name'],
0304                     'pathname': _rt['filename'] }
0305                 break
0306 
0307     def _build_ringtone(self, ringtone):
0308         # set the ringtone if specified
0309         if not ringtone:
0310             self.pb.ringtone='Default'
0311             return
0312         for _wp in self.fundamentals.get('ringtone-index', {}).values():
0313             if _wp.get('name', None)==ringtone:
0314                 if _wp.get('filename', None):
0315                     self.pb.ringtone=_wp['filename'] if _wp['filename'][0]=='/' \
0316                                       else '/'+_wp['filename']
0317                 elif _wp.get('origin', None)=='builtin':
0318                     self.pb.ringtone=_wp['name']
0319                 break
0320         
0321     def _build(self, entry):
0322         # Build a phone dict base on the phone data
0323         super(PBEntry, self)._build(entry)
0324         self._build_ringtone(entry.get('ringtones', [{}])[0].get('ringtone', None))
0325 
0326     # Extracting data from the phone--------------------------------------------
0327     def _extract_wallpaper(self, entry, p_class):
0328         if self.pb.info&p_class.PB_FLG_WP and \
0329            self.pb.wallpaper:
0330             entry['wallpapers']=[{ 'wallpaper': self.pb.wallpaper.partition('|')[0],
0331                                    'use': 'call' }]
0332     def _extract_ringtone(self, entry, p_class):
0333         if self.pb.info&p_class.PB_FLG_CRINGTONE and \
0334            self.pb.ringtone:
0335             entry['ringtones']=[{ 'ringtone': common.basename(self.pb.ringtone),
0336                                   'use': 'call' }]
0337     def getvalue(self):
0338         _entry=super(PBEntry, self).getvalue()
0339         self._extract_ringtone(_entry, self.phone.protocolclass)
0340         return _entry
0341 
0342 # CalendarEntry class-----------------------------------------------------------
0343 calendarentryparent=com_a950.CalendarEntry
0344 class CalendarEntry(calendarentryparent):
0345     """Transient class to handle calendar data being sent to, retrieved from
0346     the phone.
0347     """
0348     # Extracting routine--------------------------------------------------------
0349     def _extract_ringtone(self):
0350         # extract the builtin ringtone value, if possible
0351         for _rt_name, _rt_code in self.phone.builtin_ringtones.items():
0352             if _rt_code==self.cal.ringtoneindex:
0353                 return _rt_name
0354         for _rt_name, _rt_code in self.phone.builtin_sounds.items():
0355             if _rt_code==self.cal.ringtoneindex:
0356                 return _rt_name
0357 
0358     def getvalue(self):
0359         # return a BitPim calendar entry equivalence
0360         _entry=bpcalendar.CalendarEntry()
0361         _entry.desc_loc=self.cal.title
0362         _entry.start=self.cal.start
0363         _entry.end=self._extract_end()
0364         _entry.alarm=self._extract_alarm()
0365         _entry.ringtone=self._extract_ringtone()
0366         _entry.vibrate=self.cal.alert==self.ALERT_VIBRATE
0367         return _entry
0368 
0369     # building routines---------------------------------------------------------
0370     def _build_ringtone(self, entry):
0371         _rt_name=entry.ringtone
0372         if self.phone.builtin_ringtones.get(_rt_name, None):
0373             return self.phone.builtin_ringtones[_rt_name]
0374         elif self.phone.builtin_sounds.get(_rt_name, None):
0375             return self.phone.builtin_sounds[_rt_name]
0376         else:
0377             return 0
0378 
0379     def _build(self, entry):
0380         # populate this object with data from BitPim
0381         self.cal.titlelen=len(entry.desc_loc)
0382         self.cal.title=entry.desc_loc
0383         self.cal.start=entry.start
0384         self.cal.exptime=entry.end[3:5]
0385         self.cal.alarm=self._build_alarm(entry)
0386         self.cal.alert=self._build_alert(entry)
0387         self.cal.duration=self._build_duration(entry)
0388         self.cal.ringtoneindex=self._build_ringtone(entry)
0389 
0390 #-------------------------------------------------------------------------------
0391 parentprofile=com_a950.Profile
0392 class Profile(parentprofile):
0393     serialsname=Phone.serialsname
0394     # main LCD resolution, (external LCD is 96x96)
0395     WALLPAPER_WIDTH=128
0396     WALLPAPER_HEIGHT=160
0397     # For phone detection
0398     phone_manufacturer=Phone.my_manufacturer
0399     phone_model=Phone.my_model
0400     autodetect_delay=5
0401     # "Warning" media size limit
0402     RINGTONE_LIMITS= {
0403         'MAXSIZE': 290000
0404     }
0405 
0406     # fill in the list of ringtone/sound origins on your phone
0407     ringtoneorigins=('ringers', 'sounds')
0408     # ringtone origins that are not available for the contact assignment
0409     excluded_ringtone_origins=()
0410 
0411     # all dumped in "images"
0412     imageorigins={}
0413     imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images"))
0414     def GetImageOrigins(self):
0415         return self.imageorigins
0416 
0417     # our targets are the same for all origins
0418     imagetargets={}
0419     imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper",
0420                                       {'width': 128, 'height': 128, 'format': "JPEG"}))
0421     imagetargets.update(common.getkv(parentprofile.stockimagetargets, "pictureid",
0422                                       {'width': 96, 'height': 84, 'format': "JPEG"}))
0423     imagetargets.update(common.getkv(parentprofile.stockimagetargets, "fullscreen",
0424                                       {'width': 128, 'height': 160, 'format': "JPEG"}))
0425     imagetargets.update(common.getkv(parentprofile.stockimagetargets, "outsidelcd",
0426                                       {'width': 96, 'height': 84, 'format': "JPEG"}))
0427     def __init__(self):
0428         parentprofile.__init__(self)
0429 
0430     _supportedsyncs=(
0431         ('phonebook', 'read', None),  # all phonebook reading
0432         ('phonebook', 'write', 'OVERWRITE'),  # only overwriting phonebook
0433         ('calendar', 'read', None),   # all calendar reading
0434         ('calendar', 'write', 'OVERWRITE'),   # only overwriting calendar
0435         ('ringtone', 'read', None),   # all ringtone reading
0436         ('ringtone', 'write', 'MERGE'),
0437         ('wallpaper', 'read', None),  # all wallpaper reading
0438         ('wallpaper', 'write', 'MERGE'),
0439         ('memo', 'read', None),     # all memo list reading DJP
0440         ('memo', 'write', 'OVERWRITE'),  # all memo list writing DJP
0441         ('call_history', 'read', None),# all call history list reading
0442         ('sms', 'read', None),     # all SMS list reading DJP
0443         )
0444 

Generated by PyXR 0.9.4