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