PyXR

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



0001 ### BITPIM
0002 ###
0003 ### Copyright (C) 2007 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_samsungsphm300media.py 4530 2007-12-28 03:15:46Z djpham $
0009 
0010 """Communicate with the Samsung SPH-M300 through the diag port (Diag)"""
0011 
0012 import sha
0013 
0014 import common
0015 import commport
0016 import com_brew
0017 import com_phone
0018 import com_samsung_packet
0019 import fileinfo
0020 import helpids
0021 import prototypes
0022 import p_brew
0023 import p_samsungsphm300
0024 import re
0025 
0026 class Phone(com_phone.Phone, com_brew.BrewProtocol):
0027     "Talk to a Samsung SPH-M300 (Diag) phone"
0028 
0029     desc="SPH-M300"
0030     helpid=helpids.ID_PHONE_SAMSUNGSPHM300
0031     protocolclass=p_samsungsphm300
0032     serialsname='sphm300'
0033 
0034     builtinringtones=tuple(['Ring %d'%x for x in range(1, 11)])+\
0035                       ('After The Summer', 'Focus on It', 'Get Happy',
0036                        'Here It Comes', 'In a Circle', 'Look Back',
0037                        'Right Here', 'Secret Life',  'Shadow of Your Smile',
0038                        'Sunday Morning', 'Default')
0039 
0040     builtinimages=tuple(['People %d'%x for x in range(1, 11)])+\
0041                    tuple(['Animal %d'%x for x in range(1, 11)])+\
0042                    ('No Image',)
0043     numbertypetab=('cell', 'home', 'office', 'pager', 'fax')
0044     builtingroups=('Unassigned', 'Family', 'Friends', 'Colleagues',
0045                    'VIPs', None)
0046 
0047     __audio_mimetype={ 'mid': 'audio/midi', 'qcp': 'audio/vnd.qcelp', 'pmd': 'application/x-pmd'}
0048     __image_mimetype={ 'jpg': 'image/jpeg', 'jpeg': 'image/jpeg', 'gif': 'image/gif', 'bmp': 'image/bmp', 'png': 'image/png'}
0049 
0050     def __init__(self, logtarget, commport):
0051         com_phone.Phone.__init__(self, logtarget, commport)
0052         com_brew.BrewProtocol.__init__(self)
0053         self.mode=self.MODENONE
0054 
0055     def read_groups(self):
0056         """Read and return the group dict"""
0057         _buf=prototypes.buffer(self.getfilecontents(self.protocolclass.group_file_name))
0058         _groups=self.protocolclass.GroupFile()
0059         _groups.readfrombuffer(_buf, logtitle='Reading Group File')
0060         _res={}
0061         for _idx,_item in enumerate(_groups.entry):
0062             _res[_idx]={ 'name': _item.name if _item.num0 else \
0063                          self.builtingroups[_idx] }
0064         return _res
0065     def getfundamentals(self, results):
0066         """Gets information fundamental to interopating with the phone and UI."""
0067         self.log("Retrieving fundamental phone information")
0068         self.log("Phone serial number")
0069         results['uniqueserial']=sha.new(self.get_brew_esn()).hexdigest()
0070         results['groups']=self.read_groups()
0071         self.getmediaindex(results)
0072         return results
0073 
0074     def _get_camera_index(self, res):
0075         """Get the index of images stored in the camera"""
0076         _cnt=self.protocolclass.camera_index
0077         for _item in self.listfiles(self.protocolclass.camera_dir).values():
0078             res[_cnt]={ 'name': self.basename(_item['name']),
0079                         'location': _item['name'],
0080                         'origin': self.protocolclass.camera_origin }
0081             _cnt+=1
0082     def _get_savedtophone_index(self, res):
0083         """Get the index of saved-to-phone images"""
0084         _cnt=self.protocolclass.savedtophone_index
0085         for _item in self.listfiles(self.protocolclass.savedtophone_dir).values():
0086             res[_cnt]={ 'name': self.basename(_item['name']),
0087                         'location': _item['name'],
0088                         'origin': self.protocolclass.savedtophone_origin }
0089             _cnt+=1
0090     def _get_builtin_index(self, rt_index, wp_index):
0091         """Get the indices of builtin ringtones and wallpapers"""
0092         for _idx,_item in enumerate(self.builtinringtones):
0093             rt_index[_idx]={ 'name': _item,
0094                              'origin': 'builtin' }
0095         for _idx, _item in enumerate(self.builtinimages):
0096             wp_index[_idx]={ 'name': _item,
0097                              'origin': 'builtin' }
0098     def _get_ams_index(self, rt_index, wp_index):
0099         """Get the index of ringtones and wallpapers in AmsRegistry"""
0100         buf=prototypes.buffer(self.getfilecontents(self.protocolclass.AMSREGISTRY))
0101         ams=self.protocolclass.amsregistry()
0102         ams.readfrombuffer(buf, logtitle="Read AMS registry")
0103         _wp_cnt=self.protocolclass.ams_index
0104         _rt_cnt=self.protocolclass.ams_index
0105         for i in range(ams.nfiles):
0106             _type=ams.info[i].filetype
0107             if _type==self.protocolclass.FILETYPE_RINGER:
0108                 rt_index[_rt_cnt]={ 'name': ams.filename(i),
0109                                     'location': ams.filepath(i),
0110                                     'origin': 'ringers' }
0111                 _rt_cnt+=1
0112             elif _type==self.protocolclass.FILETYPE_WALLPAPER:
0113                 wp_index[_wp_cnt]={ 'name': ams.filename(i),
0114                                     'location': ams.filepath(i),
0115                                     'origin': 'images' }
0116                 _wp_cnt+=1
0117 
0118     def getmediaindex(self, results):
0119         wp_index={}
0120         rt_index={}
0121         self._get_builtin_index(rt_index, wp_index)
0122         self._get_camera_index(wp_index)
0123         self._get_savedtophone_index(wp_index)
0124         self._get_ams_index(rt_index, wp_index)
0125         results['ringtone-index']=rt_index
0126         results['wallpaper-index']=wp_index
0127 
0128     def getringtones(self, results):
0129         _media={}
0130         _rt_index=results.get('ringtone-index', {})
0131         for _item in _rt_index.values():
0132             if _item.get('origin', None)=='ringers':
0133                 _media[_item['name']]=self.getfilecontents(_item['location'],
0134                                                            True)
0135         results['ringtone']=_media
0136 
0137     def getwallpapers(self, results):
0138         _media={}
0139         _wp_index=results.get('wallpaper-index', {})
0140         for _item in _wp_index.values():
0141             _origin=_item.get('origin', None)
0142             _name=_item['name']
0143             if _origin=='images':
0144                 _media[_name]=self.getfilecontents(_item['location'],
0145                                                    True)
0146             elif _origin in (self.protocolclass.camera_origin,
0147                              self.protocolclass.savedtophone_origin):
0148                 _buf=prototypes.buffer(self.getfilecontents(_item['location'],
0149                                                             True))
0150                 _cam=self.protocolclass.CamFile()
0151                 _cam.readfrombuffer(_buf, logtitle='Reading CAM file')
0152                 _item['name']=_cam.filename
0153                 _media[_item['name']]=_cam.jpeg
0154         results['wallpapers']=_media
0155 
0156     # Following code lifted from module com_samsungspha620 
0157     def makegcd(self, filename,size,mimetable):
0158         "Build a GCD file for filename"
0159         ext=common.getext(filename.lower())
0160         try:
0161             mimetype=mimetable[ext]
0162         except:
0163             return ""
0164         
0165         noextname=common.stripext(filename)
0166         gcdcontent="Content-Type: "+mimetype+"\nContent-Name: "+noextname+"\nContent-Version: 1.0\nContent-Vendor: BitPim\nContent-URL: file:"+filename+"\nContent-Size: "+`size`+"\n\n\n"
0167         return gcdcontent
0168         
0169     def saveringtones(self, result, merge):
0170         dircache=self.DirCache(self)
0171         media_prefix=self.protocolclass.RINGERPREFIX
0172         endtransactionpath=self.protocolclass.ENDTRANSACTION
0173         media=result.get('ringtone', {})
0174         media_index=result.get('ringtone-index', {})
0175         media_names=[x['name'] for x in media.values() \
0176                      if x.get('origin', None)=='ringers' ]
0177         index_names=[x['name'] for x in media_index.values() \
0178                      if x.get('origin', None)=='ringers' ]
0179         if merge:
0180             del_names=[]
0181         else:
0182             del_names=[common.stripext(x) for x in index_names if x not in media_names]
0183             gcdpattern=re.compile("[\n\r]Content-Name: +(.*?)[\n\r]")
0184         new_names=[x for x in media_names if x not in index_names]
0185 
0186         progressmax=len(del_names)+len(new_names)
0187         progresscur=0
0188         self.log("Writing ringers")
0189         self.progress(progresscur, progressmax, "Writing ringers")
0190 
0191         for icnt in range(1,101):
0192             if not (new_names or del_names):
0193                 break
0194             fname=media_prefix+`icnt`
0195             fnamegcd=media_prefix+`icnt`+".gcd"
0196             fstat=dircache.stat(fnamegcd)
0197             if del_names and fstat:
0198                 # See if this file is in list of files to delete
0199                 gcdcontents=dircache.readfile(fnamegcd)
0200                 thisfile=gcdpattern.search(gcdcontents).groups()[0]
0201                 if thisfile in del_names:
0202                     self.log("Deleting ringer "+thisfile)
0203                     self.progress(progresscur, progressmax, "Deleting ringer "+thisfile)
0204                     progresscur+=1
0205                     dircache.rmfile(fname)
0206                     dircache.rmfile(fnamegcd)
0207                     del_names.remove(thisfile)
0208                     fstat=False
0209             if new_names and not fstat:
0210                 newname=new_names.pop()
0211                 contents=""
0212                 for k in media.keys():
0213                     if media[k]['name']==newname:
0214                         contents=media[k]['data']
0215                         break
0216 
0217                 contentsize=len(contents)
0218                 if contentsize:
0219                     gcdcontents=self.makegcd(newname,contentsize,self.__audio_mimetype)
0220                     self.log("Writing ringer "+newname)
0221                     self.progress(progresscur, progressmax, "Deleting ringer "+newname)
0222                     progresscur+=1
0223                     dircache.writefile(fname,contents)
0224                     dircache.writefile(fnamegcd,gcdcontents)
0225 
0226         fstat=dircache.stat(endtransactionpath)
0227         self.log("Finished writing ringers")
0228         self.progress(progressmax, progressmax, "Finished writing ringers")
0229         if fstat:
0230             dircache.rmfile(endtransactionpath)
0231         result['rebootphone']=True
0232 
0233         return
0234 
0235     def savewallpapers(self, result, merge):
0236         dircache=self.DirCache(self)
0237         media_prefix=self.protocolclass.WALLPAPERPREFIX
0238         endtransactionpath=self.protocolclass.ENDTRANSACTION
0239         media=result.get('wallpapers', {})
0240         media_index=result.get('wallpaper-index', {})
0241         media_names=[x['name'] for x in media.values() \
0242                      if x.get('origin', None)=='images' ]
0243         index_names=[x['name'] for x in media_index.values() \
0244                      if x.get('origin', None)=='images' ]
0245         if merge:
0246             del_names=[]
0247         else:
0248             del_names=[common.stripext(x) for x in index_names if x not in media_names]
0249             gcdpattern=re.compile("[\n\r]Content-Name: +(.*?)[\n\r]")
0250         new_names=[x for x in media_names if x not in index_names]
0251 
0252         progressmax=len(del_names)+len(new_names)
0253         progresscur=0
0254         self.log("Writing images")
0255         self.progress(progresscur, progressmax, "Writing images")
0256 
0257         for icnt in range(1,101):
0258             if not (new_names or del_names):
0259                 break
0260             fname=media_prefix+`icnt`
0261             fnamegcd=media_prefix+`icnt`+".gcd"
0262             fstat=dircache.stat(fnamegcd)
0263             if del_names and fstat:
0264                 # See if this file is in list of files to delete
0265                 gcdcontents=dircache.readfile(fnamegcd)
0266                 thisfile=gcdpattern.search(gcdcontents).groups()[0]
0267                 if thisfile in del_names:
0268                     self.log("Deleting image "+thisfile)
0269                     self.progress(progresscur, progressmax, "Deleting image "+thisfile)
0270                     progresscur+=1
0271                     dircache.rmfile(fname)
0272                     dircache.rmfile(fnamegcd)
0273                     del_names.remove(thisfile)
0274                     fstat=False
0275             if new_names and not fstat:
0276                 newname=new_names.pop()
0277                 contents=""
0278                 for k in media.keys():
0279                     if media[k]['name']==newname:
0280                         contents=media[k]['data']
0281                         break
0282 
0283                 contentsize=len(contents)
0284                 if contentsize:
0285                     gcdcontents=self.makegcd(newname,contentsize,self.__image_mimetype)
0286                     self.log("Writing image "+newname)
0287                     self.progress(progresscur, progressmax, "Deleting image "+newname)
0288                     progresscur+=1
0289                     dircache.writefile(fname,contents)
0290                     dircache.writefile(fnamegcd,gcdcontents)
0291 
0292         fstat=dircache.stat(endtransactionpath)
0293         self.log("Finished writing images")
0294         self.progress(progressmax, progressmax, "Finished writing images")
0295         if fstat:
0296             dircache.rmfile(endtransactionpath)
0297         result['rebootphone']=True
0298 
0299         return
0300 
0301     # Phone detection stuff-----------------------------------------------------
0302     def is_mode_brew(self):
0303         req=p_brew.memoryconfigrequest()
0304         respc=p_brew.memoryconfigresponse
0305         
0306         for baud in 0, 38400, 115200:
0307             if baud:
0308                 if not self.comm.setbaudrate(baud):
0309                     continue
0310             try:
0311                 self.sendbrewcommand(req, respc, callsetmode=False)
0312                 return True
0313             except com_phone.modeignoreerrortypes:
0314                 pass
0315         return False
0316 
0317     def get_detect_data(self, res):
0318         try:
0319             req=p_brew.firmwarerequest()
0320             resp=self.sendbrewcommand(req, p_brew.data)
0321             res['firmwareresponse']=resp.bytes
0322         except com_brew.BrewBadBrewCommandException:
0323             pass
0324 
0325     def eval_detect_data(self, res):
0326         _fw=res['firmwareresponse']
0327         if len(_fw)>47 and _fw[39:47]=='SPH-M300':
0328             # found it
0329             res['model']=Profile.phone_model
0330             res['manufacturer']=Profile.phone_manufacturer
0331             res['esn']=self.get_brew_esn()
0332 
0333     @classmethod
0334     def detectphone(_, coms, likely_ports, res, _module, _log):
0335         if not likely_ports:
0336             # cannot detect any likely ports
0337             return None
0338         for port in likely_ports:
0339             if not res.has_key(port):
0340                 res[port]={ 'mode_modem': None, 'mode_brew': None,
0341                             'manufacturer': None, 'model': None,
0342                             'firmware_version': None, 'esn': None,
0343                             'firmwareresponse': None }
0344             try:
0345                 if res[port]['mode_brew']==False or \
0346                    res[port]['model']:
0347                     # either phone is not in BREW, or a model has already
0348                     # been found, not much we can do now
0349                     continue
0350                 p=_module.Phone(_log, commport.CommConnection(_log, port, timeout=1))
0351                 if res[port]['mode_brew'] is None:
0352                     res[port]['mode_brew']=p.is_mode_brew()
0353                 if res[port]['mode_brew']:
0354                     p.get_detect_data(res[port])
0355                 p.eval_detect_data(res[port])
0356                 p.comm.close()
0357             except:
0358                 if __debug__:
0359                     raise
0360 
0361     getphonebook=NotImplemented
0362     getcalendar=NotImplemented
0363 
0364 parentprofile=com_samsung_packet.Profile
0365 class Profile(parentprofile):
0366     protocolclass=Phone.protocolclass
0367     serialsname=Phone.serialsname
0368 
0369     WALLPAPER_WIDTH=128
0370     WALLPAPER_HEIGHT=160
0371 
0372     MAX_RINGTONE_BASENAME_LENGTH=19
0373     RINGTONE_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789_ ."
0374     RINGTONE_LIMITS= {
0375         'MAXSIZE': 250000
0376     }
0377     phone_manufacturer='SAMSUNG'
0378     phone_model='SPH-M300MEDIA'
0379     numbertypetab=Phone.numbertypetab
0380 
0381     usbids=( ( 0x04e8, 0x6640, 2),)
0382     deviceclasses=("serial",)
0383 
0384     ringtoneorigins=('ringers',)
0385     excluded_ringtone_origins=('ringers',)
0386 
0387     excluded_wallpaper_origins=('camera', 'camera-fullsize')
0388     imageorigins={}
0389     imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images"))
0390     imageorigins.update(common.getkv(parentprofile.stockimageorigins, "camera"))
0391     imageorigins.update(common.getkv(parentprofile.stockimageorigins, "camera-fullsize"))
0392     def GetImageOrigins(self):
0393         return self.imageorigins
0394 
0395     imagetargets={}
0396     imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper",
0397                                       {'width': 128, 'height': 160, 'format': "JPEG"}))
0398     def GetTargetsForImageOrigin(self, origin):
0399         return self.imagetargets
0400 
0401     _supportedsyncs=(
0402         ('wallpaper', 'read', None),  # all wallpaper reading
0403         ('wallpaper', 'write', None), # Image conversion needs work
0404         ('ringtone', 'read', None),   # all ringtone reading
0405         ('ringtone', 'write', None),
0406         )
0407 
0408     __audio_ext={ 'MIDI': 'mid', 'PMD': 'pmd', 'QCP': 'qcp' }
0409     def QueryAudio(self, origin, currentextension, afi):
0410         # we don't modify any of these
0411         print "afi.format=",afi.format
0412         if afi.format in ("MIDI", "PMD", "QCP"):
0413             for k,n in self.RINGTONE_LIMITS.items():
0414                 setattr(afi, k, n)
0415             return currentextension, afi
0416         d=self.RINGTONE_LIMITS.copy()
0417         d['format']='QCP'
0418         return ('qcp', fileinfo.AudioFileInfo(afi, **d))
0419 
0420     field_color_data={
0421         'phonebook': {
0422             'name': {
0423                 'first': 0, 'middle': 0, 'last': 0, 'full': 0,
0424                 'nickname': 0, 'details': 0 },
0425             'number': {
0426                 'type': 0, 'speeddial': 0, 'number': 5, 'details': 0 },
0427             'email': 0,
0428             'address': {
0429                 'type': 0, 'company': 0, 'street': 0, 'street2': 0,
0430                 'city': 0, 'state': 0, 'postalcode': 0, 'country': 0,
0431                 'details': 0 },
0432             'url': 0,
0433             'memo': 0,
0434             'category': 0,
0435             'wallpaper': 0,
0436             'ringtone': 0,
0437             'storage': 0,
0438             },
0439         'calendar': {
0440             'description': False, 'location': False, 'allday': False,
0441             'start': False, 'end': False, 'priority': False,
0442             'alarm': False, 'vibrate': False,
0443             'repeat': False,
0444             'memo': False,
0445             'category': False,
0446             'wallpaper': False,
0447             'ringtone': False,
0448             },
0449         'memo': {
0450             'subject': False,
0451             'date': False,
0452             'secret': False,
0453             'category': False,
0454             'memo': False,
0455             },
0456         'todo': {
0457             'summary': False,
0458             'status': False,
0459             'due_date': False,
0460             'percent_complete': False,
0461             'completion_date': False,
0462             'private': False,
0463             'priority': False,
0464             'category': False,
0465             'memo': False,
0466             },
0467         }
0468 

Generated by PyXR 0.9.4