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

Source Code for Module phones.com_lgvx9800

  1  ### BITPIM 
  2  ### 
  3  ### Copyright (C) 2005 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_lgvx9800.py 4305 2007-07-16 04:05:25Z djpham $ 
  9   
 10  """Communicate with the LG VX9800 cell phone 
 11  """ 
 12   
 13  # standard modules 
 14  import re 
 15  import time 
 16  import cStringIO 
 17  import sha 
 18   
 19  # my modules 
 20  import common 
 21  import commport 
 22  import copy 
 23  import com_lgvx4400 
 24  import p_brew 
 25  import p_lgvx9800 
 26  import com_lgvx8100 
 27  import com_brew 
 28  import com_phone 
 29  import com_lg 
 30  import prototypes 
 31  import bpcalendar 
 32  import call_history 
 33  import sms 
 34  import memo 
 35  import playlist 
 36  import helpids 
 37   
38 -class Phone(com_lgvx8100.Phone):
39 "Talk to the LG VX9800 cell phone" 40 41 desc="LG-VX9800" 42 helpid=helpids.ID_PHONE_LGVX9800 43 protocolclass=p_lgvx9800 44 serialsname='lgvx9800' 45 my_model='VX9800' 46 47 builtinringtones= ('Low Beep Once', 'Low Beeps', 'Loud Beep Once', 'Loud Beeps', 'VZW Default Tone') + \ 48 tuple(['Ringtone '+`n` for n in range(1,11)]) + \ 49 ('No Ring',) 50 51 ringtonelocations= ( 52 # type index-file size-file directory-to-use lowest-index-to-use maximum-entries type-major icon index_offset 53 ( 'ringers', 'dload/my_ringtone.dat', 'dload/my_ringtonesize.dat', 'brew/16452/lk/mr', 100, 150, 0x201, 1, 0), 54 # the sound index file uses the same index as the ringers, bitpim does not support this (yet) 55 ( 'sounds', 'dload/mysound.dat', 'dload/mysoundsize.dat', 'brew/16452/ms', 100, 150, 0x402, 0, 151), 56 ) 57 58 calendarlocation="sch/schedule.dat" 59 calendarexceptionlocation="sch/schexception.dat" 60 calenderrequiresreboot=0 61 memolocation="sch/memo.dat" 62 63 builtinwallpapers = () # none 64 65 wallpaperlocations= ( 66 ( 'images', 'dload/image.dat', 'dload/imagesize.dat', 'brew/16452/mp', 100, 50, 0, 0, 0), 67 ( 'video', 'dload/video.dat', None, 'brew/16452/mf', 1000, 50, 0x0304, 0, 0), 68 ) 69 70 # for removable media (miniSD cards) 71 _rs_path='mmc1/' 72 _rs_ringers_path=_rs_path+'ringers' 73 _rs_images_path=_rs_path+'images' 74 media_info={ 'ringers': { 75 'localpath': 'brew/16452/lk/mr', 76 'rspath': _rs_ringers_path, 77 'vtype': protocolclass.MEDIA_TYPE_RINGTONE, 78 'icon': protocolclass.MEDIA_RINGTONE_DEFAULT_ICON, 79 'index': 100, # starting index 80 'maxsize': 155, 81 'indexfile': 'dload/my_ringtone.dat', 82 'sizefile': 'dload/my_ringtonesize.dat', 83 'dunno': 0, 'date': False, 84 }, 85 'sounds': { 86 'localpath': 'brew/16452/ms', 87 'rspath': None, 88 'vtype': protocolclass.MEDIA_TYPE_SOUND, 89 'icon': protocolclass.MEDIA_IMAGE_DEFAULT_ICON, 90 'index': 100, 91 'maxsize': 155, 92 'indexfile': 'dload/mysound.dat', 93 'sizefile': 'dload/mysoundsize.dat', 94 'dunno': 0, 'date': False }, 95 'images': { 96 'localpath': 'brew/16452/mp', 97 'rspath': _rs_images_path, 98 'vtype': protocolclass.MEDIA_TYPE_IMAGE, 99 'icon': protocolclass.MEDIA_IMAGE_DEFAULT_ICON, 100 'index': 100, 101 'maxsize': 155, 102 'indexfile': 'dload/image.dat', 103 'sizefile': 'dload/imagesize.dat', 104 'dunno': 0, 'date': False }, 105 'video': { 106 'localpath': 'brew/16452/mf', 107 'rspath': None, 108 'vtype': protocolclass.MEDIA_TYPE_VIDEO, 109 'icon': protocolclass.MEDIA_VIDEO_DEFAULT_ICON, 110 'index': 1000, 111 'maxsize': 155, 112 'indexfile': 'dload/video.dat', 113 'sizefile': 'dload/videosize.dat', 114 'dunno': 0, 'date': True }, 115 } 116
117 - def __init__(self, logtarget, commport):
118 com_lgvx8100.Phone.__init__(self, logtarget, commport) 119 p_brew.PHONE_ENCODING=self.protocolclass.PHONE_ENCODING 120 self.mode=self.MODENONE
121
122 - def get_esn(self, data=None):
123 # return the ESN of this phone 124 return self.get_brew_esn()
125
126 - def get_detect_data(self, res):
127 com_lgvx8100.Phone.get_detect_data(self, res) 128 res[self.esn_file_key]=self.get_esn()
129 130 # Fundamentals: 131 # - get_esn - same as LG VX-8300 132 # - getgroups - same as LG VX-8100 133 # - getwallpaperindices - LGNewIndexedMedia2 134 # - getrintoneindices - LGNewIndexedMedia2 135 # - DM Version - N/A 136 137 # Media stuff---------------------------------------------------------------
138 - def _is_rs_file(self, filename):
139 return filename.startswith(self._rs_path)
140
141 - def getmedia(self, maps, results, key):
142 origins={} 143 # signal that we are using the new media storage that includes the origin and timestamp 144 origins['new_media_version']=1 145 146 for type, indexfile, sizefile, directory, lowestindex, maxentries, typemajor, def_icon, idx_ofs in maps: 147 media={} 148 for item in self.getindex(indexfile): 149 data=None 150 timestamp=None 151 try: 152 stat_res=self.statfile(item.filename) 153 if stat_res!=None: 154 timestamp=stat_res['date'][0] 155 if not self._is_rs_file(item.filename): 156 data=self.getfilecontents(item.filename, True) 157 except (com_brew.BrewNoSuchFileException,com_brew.BrewBadPathnameException,com_brew.BrewNameTooLongException): 158 self.log("It was in the index, but not on the filesystem") 159 except com_brew.BrewAccessDeniedException: 160 # firmware wouldn't let us read this file, just mark it then 161 self.log('Failed to read file: '+item.filename) 162 data='' 163 except: 164 if __debug__: 165 raise 166 self.log('Failed to read file: '+item.filename) 167 data='' 168 if data!=None: 169 media[common.basename(item.filename)]={ 'data': data, 'timestamp': timestamp} 170 origins[type]=media 171 172 results[key]=origins 173 return results
174
175 - def _mark_files(self, local_files, rs_files, local_dir):
176 # create empty local files as markers for remote files 177 _empty_files=[common.basename(x) for x,_entry in local_files.items() \ 178 if not _entry['size']] 179 _remote_files=[common.basename(x) for x in rs_files] 180 for _file in _remote_files: 181 if _file not in _empty_files: 182 # mark this one 183 self.writefile(local_dir+'/'+_file, '') 184 for _file in _empty_files: 185 if _file not in _remote_files: 186 # remote file no longer exists, del the marker 187 self.rmfile(local_dir+'/'+_file)
188
189 - def _write_index_file(self, type):
190 _info=self.media_info.get(type, None) 191 if not _info: 192 return 193 _files={} 194 _local_dir=_info['localpath'] 195 _rs_dir=_info['rspath'] 196 _vtype=_info['vtype'] 197 _icon=_info['icon'] 198 _index=_info['index'] 199 _maxsize=_info['maxsize'] 200 _dunno=_info['dunno'] 201 indexfile=_info['indexfile'] 202 sizefile=_info['sizefile'] 203 _need_date=_info['date'] 204 try: 205 _files=self.listfiles(_local_dir) 206 except (com_brew.BrewNoSuchDirectoryException, 207 com_brew.BrewBadPathnameException): 208 pass 209 try: 210 if _rs_dir: 211 _rs_files=self.listfiles(_rs_dir) 212 if type=='ringers': 213 self._mark_files(_files, _rs_files, _local_dir) 214 _files.update(_rs_files) 215 except (com_brew.BrewNoSuchDirectoryException, 216 com_brew.BrewBadPathnameException): 217 # dir does not exist, no media files available 218 pass 219 # del all the markers (empty files) ringers 220 if type=='ringers': 221 _keys=_files.keys() 222 for _key in _keys: 223 if not _files[_key]['size']: 224 del _files[_key] 225 # dict of all indices 226 _idx_keys={} 227 for _i in xrange(_index, _index+_maxsize): 228 _idx_keys[_i]=True 229 # assign existing indices 230 for _item in self.getindex(indexfile): 231 if _files.has_key(_item.filename): 232 _files[_item.filename]['index']=_item.index 233 _idx_keys[_item.index]=False 234 # available new indices 235 _idx_keys_list=[k for k,x in _idx_keys.items() if x] 236 _idx_keys_list.sort() 237 _idx_cnt=0 238 # assign new indices 239 _file_list=[x for x in _files if not _files[x].get('index', None)] 240 _file_list.sort() 241 if len(_file_list)>len(_idx_keys_list): 242 _file_list=_file_list[:len(_idx_keys_list)] 243 for i in _file_list: 244 _files[i]['index']=_idx_keys_list[_idx_cnt] 245 _idx_cnt+=1 246 # (index, file name) list for writing 247 _res_list=[(x['index'],k) for k,x in _files.items() if x.get('index', None)] 248 _res_list.sort() 249 _res_list.reverse() 250 # writing the index file 251 ifile=self.protocolclass.indexfile() 252 _file_size=0 253 for index,idx in _res_list: 254 _fs_size=_files[idx]['size'] 255 ie=self.protocolclass.indexentry() 256 ie.index=index 257 ie.type=_vtype 258 ie.filename=idx 259 if _need_date: 260 # need to fill in the date value 261 _stat=self.statfile(_files[idx]['name']) 262 if _stat: 263 ie.date=_stat['datevalue']-time.timezone 264 ie.dunno=_dunno 265 ie.icon=_icon 266 ie.size=_fs_size 267 ifile.items.append(ie) 268 if not self._is_rs_file(idx): 269 _file_size+=_fs_size 270 buf=prototypes.buffer() 271 ifile.writetobuffer(buf, logtitle="Index file "+indexfile) 272 self.log("Writing index file "+indexfile+" for type "+type+" with "+`len(_res_list)`+" entries.") 273 self.writefile(indexfile, buf.getvalue()) 274 # writing the size file 275 if sizefile: 276 szfile=self.protocolclass.sizefile() 277 szfile.size=_file_size 278 buf=prototypes.buffer() 279 szfile.writetobuffer(buf, logtitle="Updated size file for "+type) 280 self.log("You are using a total of "+`_file_size`+" bytes for "+type) 281 self.writefile(sizefile, buf.getvalue())
282
283 - def savemedia(self, mediakey, mediaindexkey, maps, results, merge, reindexfunction):
284 """Actually saves out the media 285 286 @param mediakey: key of the media (eg 'wallpapers' or 'ringtones') 287 @param mediaindexkey: index key (eg 'wallpaper-index') 288 @param maps: list index files and locations 289 @param results: results dict 290 @param merge: are we merging or overwriting what is there? 291 @param reindexfunction: the media is re-indexed at the end. this function is called to do it 292 """ 293 294 # take copies of the lists as we modify them 295 wp=results[mediakey].copy() # the media we want to save 296 wpi=results[mediaindexkey].copy() # what is already in the index files 297 298 # remove builtins 299 for k in wpi.keys(): 300 if wpi[k].get('origin', "")=='builtin': 301 del wpi[k] 302 303 # build up list into init 304 init={} 305 for type,_,_,_,lowestindex,_,typemajor,_,_ in maps: 306 init[type]={} 307 for k in wpi.keys(): 308 if wpi[k]['origin']==type: 309 index=k 310 name=wpi[k]['name'] 311 fullname=wpi[k]['filename'] 312 vtype=wpi[k]['vtype'] 313 icon=wpi[k]['icon'] 314 data=None 315 del wpi[k] 316 for w in wp.keys(): 317 # does wp contain a reference to this same item? 318 if wp[w]['name']==name and wp[w]['origin']==type: 319 data=wp[w]['data'] 320 del wp[w] 321 if not merge and data is None: 322 # delete the entry 323 continue 324 ## assert index>=lowestindex 325 init[type][index]={'name': name, 'data': data, 'filename': fullname, 'vtype': vtype, 'icon': icon} 326 327 # init now contains everything from wallpaper-index 328 # wp contains items that we still need to add, and weren't in the existing index 329 assert len(wpi)==0 330 print init.keys() 331 332 # now look through wallpapers and see if anything was assigned a particular 333 # origin 334 for w in wp.keys(): 335 o=wp[w].get("origin", "") 336 if o is not None and len(o) and o in init: 337 idx=-1 338 while idx in init[o]: 339 idx-=1 340 init[o][idx]=wp[w] 341 del wp[w] 342 343 # wp will now consist of items that weren't assigned any particular place 344 # so put them in the first available space 345 for type,_,_,_,lowestindex,maxentries,typemajor,def_icon,_ in maps: 346 # fill it up 347 for w in wp.keys(): 348 if len(init[type])>=maxentries: 349 break 350 idx=-1 351 while idx in init[type]: 352 idx-=1 353 init[type][idx]=wp[w] 354 del wp[w] 355 356 # time to write the files out 357 for type, indexfile, sizefile, directory, lowestindex, maxentries,typemajor,def_icon,_ in maps: 358 # get the index file so we can work out what to delete 359 names=[init[type][x]['name'] for x in init[type]] 360 for item in self.getindex(indexfile): 361 if common.basename(item.filename) not in names and \ 362 not self._is_rs_file(item.filename): 363 self.log(item.filename+" is being deleted") 364 self.rmfile(item.filename) 365 # fixup the indices 366 fixups=[k for k in init[type].keys() if k<lowestindex] 367 fixups.sort() 368 for f in fixups: 369 for ii in xrange(lowestindex, lowestindex+maxentries): 370 # allocate an index 371 if ii not in init[type]: 372 init[type][ii]=init[type][f] 373 del init[type][f] 374 break 375 # any left over? 376 fixups=[k for k in init[type].keys() if k<lowestindex] 377 for f in fixups: 378 self.log("There is no space in the index for "+type+" for "+init[type][f]['name']) 379 del init[type][f] 380 # write each entry out 381 for idx in init[type].keys(): 382 entry=init[type][idx] 383 filename=entry.get('filename', directory+"/"+entry['name']) 384 entry['filename']=filename 385 fstat=self.statfile(filename) 386 if 'data' not in entry: 387 # must be in the filesystem already 388 if fstat is None: 389 self.log("Entry "+entry['name']+" is in index "+indexfile+" but there is no data for it and it isn't in the filesystem. The index entry will be removed.") 390 del init[type][idx] 391 continue 392 # check len(data) against fstat->length 393 data=entry['data'] 394 if data is None: 395 assert merge 396 continue # we are doing an add and don't have data for this existing entry 397 if fstat is not None and len(data)==fstat['size']: 398 self.log("Not writing "+filename+" as a file of the same name and length already exists.") 399 else: 400 self.writefile(filename, data) 401 # write out index 402 self._write_index_file(type) 403 return reindexfunction(results)
404 405 # Phonebook stuff-----------------------------------------------------------
406 - def savephonebook(self, data):
407 "Saves out the phonebook" 408 res=com_lgvx8100.Phone.savephonebook(self, data) 409 # fix up the Wallpaper ID issue 410 _wp_paths=self.protocolclass.wallpaper_id_file() 411 _path_entry=self.protocolclass.wallpaper_id() 412 # clear out all entries 413 for i in range(self.protocolclass.NUMPHONEBOOKENTRIES): 414 _wp_paths.items.append(_path_entry) 415 # go through each entry and update the wallpaper path 416 _buf=prototypes.buffer(self.getfilecontents( 417 self.protocolclass.pb_file_name)) 418 _pb_entries=self.protocolclass.pbfile() 419 _pb_entries.readfrombuffer(_buf, logtitle="Read phonebook file "+self.protocolclass.pb_file_name) 420 _wp_index=res.get('wallpaper-index', {}) 421 for _entry in _pb_entries.items: 422 try: 423 if _entry.wallpaper==0 or _entry.wallpaper==0xffff: 424 # no picture ID assigned 425 continue 426 _filename=_wp_index[_entry.wallpaper]['filename'] 427 if _filename: 428 _path_str=_filename+'\x00' 429 _path=self.protocolclass.wallpaper_id() 430 _path.path=_path_str 431 _wp_paths.items[_entry.entrynumber]=_path 432 except: 433 if __debug__: 434 raise 435 _buf=prototypes.buffer() 436 _wp_paths.writetobuffer(_buf, logtitle="Updated wallpaper ids "+self.protocolclass.wallpaper_id_file_name) 437 self.writefile(self.protocolclass.wallpaper_id_file_name, 438 _buf.getvalue())
439 440 # SMS Stuff-----------------------------------------------------------------
441 - def _readsms(self):
442 res={} 443 # go through the sms directory looking for messages 444 for item in self.listfiles("sms").values(): 445 folder=None 446 for f,pat in self.protocolclass.SMS_PATTERNS.items(): 447 if pat.match(item['name']): 448 folder=f 449 break 450 if folder: 451 buf=prototypes.buffer(self.getfilecontents(item['name'], True)) 452 self.logdata("SMS message file " +item['name'], buf.getdata()) 453 if folder=='Inbox': 454 sf=self.protocolclass.sms_in() 455 sf.readfrombuffer(buf, logtitle="SMS inbox item") 456 entry=self._getinboxmessage(sf) 457 res[entry.id]=entry 458 elif folder=='Sent': 459 sf=self.protocolclass.sms_out() 460 sf.readfrombuffer(buf, logtitle="SMS sent item") 461 entry=self._getoutboxmessage(sf) 462 res[entry.id]=entry 463 elif folder=='Saved': 464 sf=self.protocolclass.sms_saved() 465 sf.readfrombuffer(buf, logtitle="SMS saved item") 466 if sf.inboxmsg: 467 entry=self._getinboxmessage(sf.inbox) 468 else: 469 entry=self._getoutboxmessage(sf.outbox) 470 entry.folder=entry.Folder_Saved 471 res[entry.id]=entry 472 return res
473 474 # Playlist stuff------------------------------------------------------------
475 - def _read_pl_list(self, file_name):
476 _buf=prototypes.buffer(self.getfilecontents(file_name)) 477 _pl_index=self.protocolclass.playlistfile() 478 _pl_index.readfrombuffer(_buf, logtitle="Read playlist "+file_name) 479 _songs=[x.name[self.protocolclass.mp3_dir_len:] for x in _pl_index.items] 480 _entry=playlist.PlaylistEntry() 481 if file_name.endswith(self.protocolclass.pl_extension): 482 _entry.name=file_name[self.protocolclass.pl_dir_len:\ 483 -self.protocolclass.pl_extension_len] 484 else: 485 _entry.name=file_name[self.protocolclass.pl_dir_len:] 486 _entry.songs=_songs 487 return _entry
488
489 - def getplaylist(self, result):
490 # return the mp3 playlists if available 491 # first, read the list of all mp3 songs 492 _mp3_list=[] 493 try: 494 _files=self.listfiles(self.protocolclass.mp3_dir) 495 _file_list=_files.keys() 496 _file_list.sort() 497 _mp3_list=[x[self.protocolclass.mp3_dir_len:] for x in _file_list ] 498 except: 499 if __debug__: 500 raise 501 result[playlist.masterlist_key]=_mp3_list 502 # then read the playlist 503 _pl_list=[] 504 try: 505 _files=self.listfiles(self.protocolclass.pl_dir) 506 _file_list=_files.keys() 507 _file_list.sort() 508 for _f in _file_list: 509 _pl_list.append(self._read_pl_list(_f)) 510 except: 511 if __debug__: 512 raise 513 result[playlist.playlist_key]=_pl_list 514 return result
515
516 - def _write_playlists(self, pl, all_songs):
517 for _pl_item in pl: 518 try: 519 _pl_file=self.protocolclass.playlistfile() 520 for _song in _pl_item.songs: 521 _song_name=self.protocolclass.mp3_dir+'/'+_song 522 if all_songs.has_key(_song_name): 523 _entry=self.protocolclass.playlistentry() 524 _entry.name=_song_name 525 _pl_file.items.append(_entry) 526 if len(_pl_file.items): 527 # don't write out an empty list 528 _buf=prototypes.buffer() 529 _file_name=self.protocolclass.pl_dir+'/'+_pl_item.name+\ 530 self.protocolclass.pl_extension 531 _pl_file.writetobuffer(_buf, logtitle="Updating playlist "+_file_name) 532 self.writefile(_file_name, _buf.getvalue()) 533 except: 534 if __debug__: 535 raise
536
537 - def saveplaylist(self, result, merge):
538 # check to see if the pl_dir exist 539 if not self.exists(self.protocolclass.pl_dir): 540 self.log('Playlist dir does not exist. Bail') 541 return result 542 # get the list of available mp3 files 543 _all_songs=self.listfiles(self.protocolclass.mp3_dir) 544 # delete all existing playlists 545 _files=self.listfiles(self.protocolclass.pl_dir) 546 for _f in _files: 547 try: 548 self.rmfile(_f) 549 except: 550 if __debug__: 551 raise 552 # update the new playlists 553 self._write_playlists(result.get(playlist.playlist_key, []), 554 _all_songs) 555 return result
556 557 #------------------------------------------------------------------------------- 558 parentprofile=com_lgvx8100.Profile
559 -class Profile(parentprofile):
560 protocolclass=Phone.protocolclass 561 serialsname=Phone.serialsname 562 563 BP_Calendar_Version=3 564 phone_manufacturer='LG Electronics Inc' 565 phone_model='VX9800' 566 567 WALLPAPER_WIDTH=320 568 WALLPAPER_HEIGHT=256 569 MAX_WALLPAPER_BASENAME_LENGTH=32 570 WALLPAPER_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789()_ .-" 571 WALLPAPER_CONVERT_FORMAT="jpg" 572 573 # the 9800 uses "W" for wait in the dialstring, it does not support "T" 574 DIALSTRING_CHARS="[^0-9PW#*]" 575 576 MAX_RINGTONE_BASENAME_LENGTH=32 577 RINGTONE_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789()_ .-" 578 579 # there is an origin named 'aod' - no idea what it is for except maybe 580 # 'all other downloads' 581 582 # the vx8100 supports bluetooth for connectivity to the PC, define the "bluetooth_mgd_id" 583 # to enable bluetooth discovery during phone detection 584 # the bluetooth address starts with LG's the three-octet OUI, all LG phone 585 # addresses start with this, it provides a way to identify LG bluetooth devices 586 # during phone discovery 587 # OUI=Organizationally Unique Identifier 588 # see http://standards.ieee.org/regauth/oui/index.shtml for more info 589 bluetooth_mfg_id="001256" 590 591 # the 8100 doesn't have seperate origins - they are all dumped in "images" 592 imageorigins={} 593 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images")) 594 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "video"))
595 - def GetImageOrigins(self):
596 return self.imageorigins
597 598 ringtoneorigins=('ringers', 'sounds') 599 excluded_ringtone_origins=('sounds') 600 excluded_wallpaper_origins=('video') 601 602 # our targets are the same for all origins 603 imagetargets={} 604 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", 605 {'width': 320, 'height': 230, 'format': "JPEG"})) 606 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "outsidelcd", 607 {'width': 320, 'height': 198, 'format': "JPEG"})) 608
609 - def GetTargetsForImageOrigin(self, origin):
610 return self.imagetargets
611 612
613 - def __init__(self):
614 parentprofile.__init__(self)
615 616 _supportedsyncs=( 617 ('phonebook', 'read', None), # all phonebook reading 618 ('calendar', 'read', None), # all calendar reading 619 ('wallpaper', 'read', None), # all wallpaper reading 620 ('ringtone', 'read', None), # all ringtone reading 621 ('call_history', 'read', None),# all call history list reading 622 ('sms', 'read', None), # all SMS list reading 623 ('memo', 'read', None), # all memo list reading 624 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 625 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 626 ('wallpaper', 'write', 'MERGE'), # merge and overwrite wallpaper 627 ('wallpaper', 'write', 'OVERWRITE'), 628 ('ringtone', 'write', 'MERGE'), # merge and overwrite ringtone 629 ('ringtone', 'write', 'OVERWRITE'), 630 ('sms', 'write', 'OVERWRITE'), # all SMS list writing 631 ('memo', 'write', 'OVERWRITE'), # all memo list writing 632 ('playlist', 'read', 'OVERWRITE'), 633 ('playlist', 'write', 'OVERWRITE'), 634 )
635