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

Source Code for Module phones.com_samsungsphm300media

  1  ### BITPIM 
  2  ### 
  3  ### Copyright (C) 2007 Joe Pham <djpham@bitpim.org> 
  4  ### 
  5  ### This program is free software; you can redistribute it and/or modify 
  6  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
  7  ### 
  8  ### $Id: com_samsungsphm300media.py 4530 2007-12-28 03:15:46Z djpham $ 
  9   
 10  """Communicate with the Samsung SPH-M300 through the diag port (Diag)""" 
 11   
 12  import sha 
 13   
 14  import common 
 15  import commport 
 16  import com_brew 
 17  import com_phone 
 18  import com_samsung_packet 
 19  import fileinfo 
 20  import helpids 
 21  import prototypes 
 22  import p_brew 
 23  import p_samsungsphm300 
 24  import re 
25 26 -class Phone(com_phone.Phone, com_brew.BrewProtocol):
27 "Talk to a Samsung SPH-M300 (Diag) phone" 28 29 desc="SPH-M300" 30 helpid=helpids.ID_PHONE_SAMSUNGSPHM300 31 protocolclass=p_samsungsphm300 32 serialsname='sphm300' 33 34 builtinringtones=tuple(['Ring %d'%x for x in range(1, 11)])+\ 35 ('After The Summer', 'Focus on It', 'Get Happy', 36 'Here It Comes', 'In a Circle', 'Look Back', 37 'Right Here', 'Secret Life', 'Shadow of Your Smile', 38 'Sunday Morning', 'Default') 39 40 builtinimages=tuple(['People %d'%x for x in range(1, 11)])+\ 41 tuple(['Animal %d'%x for x in range(1, 11)])+\ 42 ('No Image',) 43 numbertypetab=('cell', 'home', 'office', 'pager', 'fax') 44 builtingroups=('Unassigned', 'Family', 'Friends', 'Colleagues', 45 'VIPs', None) 46 47 __audio_mimetype={ 'mid': 'audio/midi', 'qcp': 'audio/vnd.qcelp', 'pmd': 'application/x-pmd'} 48 __image_mimetype={ 'jpg': 'image/jpeg', 'jpeg': 'image/jpeg', 'gif': 'image/gif', 'bmp': 'image/bmp', 'png': 'image/png'} 49
50 - def __init__(self, logtarget, commport):
51 com_phone.Phone.__init__(self, logtarget, commport) 52 com_brew.BrewProtocol.__init__(self) 53 self.mode=self.MODENONE
54
55 - def read_groups(self):
56 """Read and return the group dict""" 57 _buf=prototypes.buffer(self.getfilecontents(self.protocolclass.group_file_name)) 58 _groups=self.protocolclass.GroupFile() 59 _groups.readfrombuffer(_buf, logtitle='Reading Group File') 60 _res={} 61 for _idx,_item in enumerate(_groups.entry): 62 _res[_idx]={ 'name': _item.name if _item.num0 else \ 63 self.builtingroups[_idx] } 64 return _res
65 - def getfundamentals(self, results):
66 """Gets information fundamental to interopating with the phone and UI.""" 67 self.log("Retrieving fundamental phone information") 68 self.log("Phone serial number") 69 results['uniqueserial']=sha.new(self.get_brew_esn()).hexdigest() 70 results['groups']=self.read_groups() 71 self.getmediaindex(results) 72 return results
73
74 - def _get_camera_index(self, res):
75 """Get the index of images stored in the camera""" 76 _cnt=self.protocolclass.camera_index 77 for _item in self.listfiles(self.protocolclass.camera_dir).values(): 78 res[_cnt]={ 'name': self.basename(_item['name']), 79 'location': _item['name'], 80 'origin': self.protocolclass.camera_origin } 81 _cnt+=1
82 - def _get_savedtophone_index(self, res):
83 """Get the index of saved-to-phone images""" 84 _cnt=self.protocolclass.savedtophone_index 85 for _item in self.listfiles(self.protocolclass.savedtophone_dir).values(): 86 res[_cnt]={ 'name': self.basename(_item['name']), 87 'location': _item['name'], 88 'origin': self.protocolclass.savedtophone_origin } 89 _cnt+=1
90 - def _get_builtin_index(self, rt_index, wp_index):
91 """Get the indices of builtin ringtones and wallpapers""" 92 for _idx,_item in enumerate(self.builtinringtones): 93 rt_index[_idx]={ 'name': _item, 94 'origin': 'builtin' } 95 for _idx, _item in enumerate(self.builtinimages): 96 wp_index[_idx]={ 'name': _item, 97 'origin': 'builtin' }
98 - def _get_ams_index(self, rt_index, wp_index):
99 """Get the index of ringtones and wallpapers in AmsRegistry""" 100 buf=prototypes.buffer(self.getfilecontents(self.protocolclass.AMSREGISTRY)) 101 ams=self.protocolclass.amsregistry() 102 ams.readfrombuffer(buf, logtitle="Read AMS registry") 103 _wp_cnt=self.protocolclass.ams_index 104 _rt_cnt=self.protocolclass.ams_index 105 for i in range(ams.nfiles): 106 _type=ams.info[i].filetype 107 if _type==self.protocolclass.FILETYPE_RINGER: 108 rt_index[_rt_cnt]={ 'name': ams.filename(i), 109 'location': ams.filepath(i), 110 'origin': 'ringers' } 111 _rt_cnt+=1 112 elif _type==self.protocolclass.FILETYPE_WALLPAPER: 113 wp_index[_wp_cnt]={ 'name': ams.filename(i), 114 'location': ams.filepath(i), 115 'origin': 'images' } 116 _wp_cnt+=1
117
118 - def getmediaindex(self, results):
119 wp_index={} 120 rt_index={} 121 self._get_builtin_index(rt_index, wp_index) 122 self._get_camera_index(wp_index) 123 self._get_savedtophone_index(wp_index) 124 self._get_ams_index(rt_index, wp_index) 125 results['ringtone-index']=rt_index 126 results['wallpaper-index']=wp_index
127
128 - def getringtones(self, results):
129 _media={} 130 _rt_index=results.get('ringtone-index', {}) 131 for _item in _rt_index.values(): 132 if _item.get('origin', None)=='ringers': 133 _media[_item['name']]=self.getfilecontents(_item['location'], 134 True) 135 results['ringtone']=_media
136
137 - def getwallpapers(self, results):
138 _media={} 139 _wp_index=results.get('wallpaper-index', {}) 140 for _item in _wp_index.values(): 141 _origin=_item.get('origin', None) 142 _name=_item['name'] 143 if _origin=='images': 144 _media[_name]=self.getfilecontents(_item['location'], 145 True) 146 elif _origin in (self.protocolclass.camera_origin, 147 self.protocolclass.savedtophone_origin): 148 _buf=prototypes.buffer(self.getfilecontents(_item['location'], 149 True)) 150 _cam=self.protocolclass.CamFile() 151 _cam.readfrombuffer(_buf, logtitle='Reading CAM file') 152 _item['name']=_cam.filename 153 _media[_item['name']]=_cam.jpeg 154 results['wallpapers']=_media
155 156 # Following code lifted from module com_samsungspha620
157 - def makegcd(self, filename,size,mimetable):
158 "Build a GCD file for filename" 159 ext=common.getext(filename.lower()) 160 try: 161 mimetype=mimetable[ext] 162 except: 163 return "" 164 165 noextname=common.stripext(filename) 166 gcdcontent="Content-Type: "+mimetype+"\nContent-Name: "+noextname+"\nContent-Version: 1.0\nContent-Vendor: BitPim\nContent-URL: file:"+filename+"\nContent-Size: "+`size`+"\n\n\n" 167 return gcdcontent
168
169 - def saveringtones(self, result, merge):
170 dircache=self.DirCache(self) 171 media_prefix=self.protocolclass.RINGERPREFIX 172 endtransactionpath=self.protocolclass.ENDTRANSACTION 173 media=result.get('ringtone', {}) 174 media_index=result.get('ringtone-index', {}) 175 media_names=[x['name'] for x in media.values() \ 176 if x.get('origin', None)=='ringers' ] 177 index_names=[x['name'] for x in media_index.values() \ 178 if x.get('origin', None)=='ringers' ] 179 if merge: 180 del_names=[] 181 else: 182 del_names=[common.stripext(x) for x in index_names if x not in media_names] 183 gcdpattern=re.compile("[\n\r]Content-Name: +(.*?)[\n\r]") 184 new_names=[x for x in media_names if x not in index_names] 185 186 progressmax=len(del_names)+len(new_names) 187 progresscur=0 188 self.log("Writing ringers") 189 self.progress(progresscur, progressmax, "Writing ringers") 190 191 for icnt in range(1,101): 192 if not (new_names or del_names): 193 break 194 fname=media_prefix+`icnt` 195 fnamegcd=media_prefix+`icnt`+".gcd" 196 fstat=dircache.stat(fnamegcd) 197 if del_names and fstat: 198 # See if this file is in list of files to delete 199 gcdcontents=dircache.readfile(fnamegcd) 200 thisfile=gcdpattern.search(gcdcontents).groups()[0] 201 if thisfile in del_names: 202 self.log("Deleting ringer "+thisfile) 203 self.progress(progresscur, progressmax, "Deleting ringer "+thisfile) 204 progresscur+=1 205 dircache.rmfile(fname) 206 dircache.rmfile(fnamegcd) 207 del_names.remove(thisfile) 208 fstat=False 209 if new_names and not fstat: 210 newname=new_names.pop() 211 contents="" 212 for k in media.keys(): 213 if media[k]['name']==newname: 214 contents=media[k]['data'] 215 break 216 217 contentsize=len(contents) 218 if contentsize: 219 gcdcontents=self.makegcd(newname,contentsize,self.__audio_mimetype) 220 self.log("Writing ringer "+newname) 221 self.progress(progresscur, progressmax, "Deleting ringer "+newname) 222 progresscur+=1 223 dircache.writefile(fname,contents) 224 dircache.writefile(fnamegcd,gcdcontents) 225 226 fstat=dircache.stat(endtransactionpath) 227 self.log("Finished writing ringers") 228 self.progress(progressmax, progressmax, "Finished writing ringers") 229 if fstat: 230 dircache.rmfile(endtransactionpath) 231 result['rebootphone']=True 232 233 return
234
235 - def savewallpapers(self, result, merge):
236 dircache=self.DirCache(self) 237 media_prefix=self.protocolclass.WALLPAPERPREFIX 238 endtransactionpath=self.protocolclass.ENDTRANSACTION 239 media=result.get('wallpapers', {}) 240 media_index=result.get('wallpaper-index', {}) 241 media_names=[x['name'] for x in media.values() \ 242 if x.get('origin', None)=='images' ] 243 index_names=[x['name'] for x in media_index.values() \ 244 if x.get('origin', None)=='images' ] 245 if merge: 246 del_names=[] 247 else: 248 del_names=[common.stripext(x) for x in index_names if x not in media_names] 249 gcdpattern=re.compile("[\n\r]Content-Name: +(.*?)[\n\r]") 250 new_names=[x for x in media_names if x not in index_names] 251 252 progressmax=len(del_names)+len(new_names) 253 progresscur=0 254 self.log("Writing images") 255 self.progress(progresscur, progressmax, "Writing images") 256 257 for icnt in range(1,101): 258 if not (new_names or del_names): 259 break 260 fname=media_prefix+`icnt` 261 fnamegcd=media_prefix+`icnt`+".gcd" 262 fstat=dircache.stat(fnamegcd) 263 if del_names and fstat: 264 # See if this file is in list of files to delete 265 gcdcontents=dircache.readfile(fnamegcd) 266 thisfile=gcdpattern.search(gcdcontents).groups()[0] 267 if thisfile in del_names: 268 self.log("Deleting image "+thisfile) 269 self.progress(progresscur, progressmax, "Deleting image "+thisfile) 270 progresscur+=1 271 dircache.rmfile(fname) 272 dircache.rmfile(fnamegcd) 273 del_names.remove(thisfile) 274 fstat=False 275 if new_names and not fstat: 276 newname=new_names.pop() 277 contents="" 278 for k in media.keys(): 279 if media[k]['name']==newname: 280 contents=media[k]['data'] 281 break 282 283 contentsize=len(contents) 284 if contentsize: 285 gcdcontents=self.makegcd(newname,contentsize,self.__image_mimetype) 286 self.log("Writing image "+newname) 287 self.progress(progresscur, progressmax, "Deleting image "+newname) 288 progresscur+=1 289 dircache.writefile(fname,contents) 290 dircache.writefile(fnamegcd,gcdcontents) 291 292 fstat=dircache.stat(endtransactionpath) 293 self.log("Finished writing images") 294 self.progress(progressmax, progressmax, "Finished writing images") 295 if fstat: 296 dircache.rmfile(endtransactionpath) 297 result['rebootphone']=True 298 299 return
300 301 # Phone detection stuff-----------------------------------------------------
302 - def is_mode_brew(self):
303 req=p_brew.memoryconfigrequest() 304 respc=p_brew.memoryconfigresponse 305 306 for baud in 0, 38400, 115200: 307 if baud: 308 if not self.comm.setbaudrate(baud): 309 continue 310 try: 311 self.sendbrewcommand(req, respc, callsetmode=False) 312 return True 313 except com_phone.modeignoreerrortypes: 314 pass 315 return False
316
317 - def get_detect_data(self, res):
318 try: 319 req=p_brew.firmwarerequest() 320 resp=self.sendbrewcommand(req, p_brew.data) 321 res['firmwareresponse']=resp.bytes 322 except com_brew.BrewBadBrewCommandException: 323 pass
324
325 - def eval_detect_data(self, res):
326 _fw=res['firmwareresponse'] 327 if len(_fw)>47 and _fw[39:47]=='SPH-M300': 328 # found it 329 res['model']=Profile.phone_model 330 res['manufacturer']=Profile.phone_manufacturer 331 res['esn']=self.get_brew_esn()
332 333 @classmethod
334 - def detectphone(_, coms, likely_ports, res, _module, _log):
335 if not likely_ports: 336 # cannot detect any likely ports 337 return None 338 for port in likely_ports: 339 if not res.has_key(port): 340 res[port]={ 'mode_modem': None, 'mode_brew': None, 341 'manufacturer': None, 'model': None, 342 'firmware_version': None, 'esn': None, 343 'firmwareresponse': None } 344 try: 345 if res[port]['mode_brew']==False or \ 346 res[port]['model']: 347 # either phone is not in BREW, or a model has already 348 # been found, not much we can do now 349 continue 350 p=_module.Phone(_log, commport.CommConnection(_log, port, timeout=1)) 351 if res[port]['mode_brew'] is None: 352 res[port]['mode_brew']=p.is_mode_brew() 353 if res[port]['mode_brew']: 354 p.get_detect_data(res[port]) 355 p.eval_detect_data(res[port]) 356 p.comm.close() 357 except: 358 if __debug__: 359 raise
360 361 getphonebook=NotImplemented 362 getcalendar=NotImplemented
363 364 parentprofile=com_samsung_packet.Profile
365 -class Profile(parentprofile):
366 protocolclass=Phone.protocolclass 367 serialsname=Phone.serialsname 368 369 WALLPAPER_WIDTH=128 370 WALLPAPER_HEIGHT=160 371 372 MAX_RINGTONE_BASENAME_LENGTH=19 373 RINGTONE_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789_ ." 374 RINGTONE_LIMITS= { 375 'MAXSIZE': 250000 376 } 377 phone_manufacturer='SAMSUNG' 378 phone_model='SPH-M300MEDIA' 379 numbertypetab=Phone.numbertypetab 380 381 usbids=( ( 0x04e8, 0x6640, 2),) 382 deviceclasses=("serial",) 383 384 ringtoneorigins=('ringers',) 385 excluded_ringtone_origins=('ringers',) 386 387 excluded_wallpaper_origins=('camera', 'camera-fullsize') 388 imageorigins={} 389 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images")) 390 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "camera")) 391 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "camera-fullsize"))
392 - def GetImageOrigins(self):
393 return self.imageorigins
394 395 imagetargets={} 396 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", 397 {'width': 128, 'height': 160, 'format': "JPEG"}))
398 - def GetTargetsForImageOrigin(self, origin):
399 return self.imagetargets
400 401 _supportedsyncs=( 402 ('wallpaper', 'read', None), # all wallpaper reading 403 ('wallpaper', 'write', None), # Image conversion needs work 404 ('ringtone', 'read', None), # all ringtone reading 405 ('ringtone', 'write', None), 406 ) 407 408 __audio_ext={ 'MIDI': 'mid', 'PMD': 'pmd', 'QCP': 'qcp' }
409 - def QueryAudio(self, origin, currentextension, afi):
410 # we don't modify any of these 411 print "afi.format=",afi.format 412 if afi.format in ("MIDI", "PMD", "QCP"): 413 for k,n in self.RINGTONE_LIMITS.items(): 414 setattr(afi, k, n) 415 return currentextension, afi 416 d=self.RINGTONE_LIMITS.copy() 417 d['format']='QCP' 418 return ('qcp', fileinfo.AudioFileInfo(afi, **d))
419 420 field_color_data={ 421 'phonebook': { 422 'name': { 423 'first': 0, 'middle': 0, 'last': 0, 'full': 0, 424 'nickname': 0, 'details': 0 }, 425 'number': { 426 'type': 0, 'speeddial': 0, 'number': 5, 'details': 0 }, 427 'email': 0, 428 'address': { 429 'type': 0, 'company': 0, 'street': 0, 'street2': 0, 430 'city': 0, 'state': 0, 'postalcode': 0, 'country': 0, 431 'details': 0 }, 432 'url': 0, 433 'memo': 0, 434 'category': 0, 435 'wallpaper': 0, 436 'ringtone': 0, 437 'storage': 0, 438 }, 439 'calendar': { 440 'description': False, 'location': False, 'allday': False, 441 'start': False, 'end': False, 'priority': False, 442 'alarm': False, 'vibrate': False, 443 'repeat': False, 444 'memo': False, 445 'category': False, 446 'wallpaper': False, 447 'ringtone': False, 448 }, 449 'memo': { 450 'subject': False, 451 'date': False, 452 'secret': False, 453 'category': False, 454 'memo': False, 455 }, 456 'todo': { 457 'summary': False, 458 'status': False, 459 'due_date': False, 460 'percent_complete': False, 461 'completion_date': False, 462 'private': False, 463 'priority': False, 464 'category': False, 465 'memo': False, 466 }, 467 }
468